Exploiting Out-of-Band XXE in the Wild

Mahmoud Youssef
7 min readSep 6, 2022

Hello all, I hope you’re fine! Our story today is about one of the most interesting bugs I found, actually, it’s my first time finding this bug in a BB Program, and some problems faced me while making a PoC to retrieve local files, so I decided to share it! SO, LET’S start ;)

Phase 1 → Recon:

I like shodan and enumerate IPs from it, so I collected a list of IPs that related to my target which we’ll call it redacted.com and after that, I made a full port scan with rustscan (you can use naabu or whatever you want) then I made an HTTP probe to them and started to check the unusual ports such as 8443, 8080, 8888, 9180, 9000, etc. and sure I passed all the requests to burp then I came across an IP with port 9180 open, this IP uses some xmlcontent so I tried to make a POST request with Content-Type: application/xml then I got some errors! In this stage, I decided to test XXE, which you can read about and practice here, so Let’s try to exploit it!

Phase 2 → Analysis:

I tried the payload that was used to detect the vulnerability, but the response contains only errors, so I tried to retrieve local files directly, so I used the classic payload:

But I got nothing

Mmmmm, nothing came, then I tried some other payloads, but I got the same errors, so after that, I searched for XXE payloads and started to use it and modified some of them and started to compare with the responses differences, but I got some different content lengths, but I didn’t understand anything, every response contains errors and errors and errors,..etc.

Phase 3 → SSRF:

So, I thought that I can’t get P1 from this, then I tried to perform an HTTP request to my burp collab (SSRF), so I fire the burp again and tried the payload to perform SSRF from the XXE, so I tried this payload:

and the same result, I failed too, after that I tried some payloads to perform SSRF, but nothing came, so I started to search and read some old write-ups, till I came across this payload:

BTW, you can use SYSTEM entity too, they are almost synonym.

You can check this too.

I decided to use PUBLIC anyway, so let’s test our payload.

And Bingoooo! I got HTTP interaction, so now I have a P4 submission because it’s just anHTTP interaction, at this moment I thought what about escalating it to P3 with a simple port scanning? So I sent a simple request to which I know that port 9180is open, and sent another one to a random port such as and compare the two responses with burp comparer:

And as I expected I gotConnection refused from port 333 which is an uncommon port and I thought that it will not be open.

So I made a simple port scan for top 10000 ports, and filter the responses which contain Connection refusedand I got some open ports :)

Filter responses that contain Connection refused

And now I have a juicy P3 in a paid BB program, actually, I was about to report it, but I remembered that I was searching for XXE which is P1, so some idea came to my mind, which is what about trying again to get XXE. But I thought that it may be OOB then, so getting /etc/passwd maybe impossible because it’s a multi-line file, but /etc/hostname will be fine, so let’s do it.

Phase 4 → OOB XXE:

And for now, I thought it’s time to test Out-of-band XXE because I could make SSRF, so if I hosted a malicious DTD file and did SSRF, forcing the site to request my malicious DTD file, may I could achieve the XXE, btw I told myself that this is the final try to get this XXE. But before the exploitation, you need to understand what is OOB XXE and how it works.

The following pic describes how OOB XXE works:


So we need a malicious DTD file that requests local files, and I used this :

and we need to create an HTTP server on port 8080 (or any port)

Q: So now what is expected to get from this exploit?

  1. The site requested our malicious DTD file
  2. Our malicious DTD requests /etc/hostname file and send it to my IP on 1337 port
  3. my listener should receive a request with the /etc/hostname file content

And guess what?

I got the result in the listener with the hostname XD

So now after a lot of failures, I made the OOB XXE exploitation successfully!! But actually, I couldn’t get any file with multiple lines such as /etc/passwd or /etc/hosts I only could get files with one line, I thought it was enough for the PoC and I could report it, but to be honest, I wanted to get /etc/passwd because I thought that the disability of getting large file contents may reduce the impact and severity to P2 or lower..(I don’t know if I’m right or wrong) but I decided to try to get the large file contents, So I returned to Burp to do some fun!!

Phase 5 → XXE Final Exploitation:

In this stage, I started to do what I know in OOB XXE attacks to get large file contents such as base64, FTP or even using some tools that were used to get the data through FTP, but I failed in all these ways :( But it is still something to try which is getting large file contents via error messages, But how it will happen?

  1. Hosted a malicious DTD file does two things:
  • has a defined XML parameter entity containing the content of the /etc/passwd file.
  • has a defined XML parameter entity containing a dynamic declaration of another XML parameter entity, and this will be evaluated by loading a nonexistent file whose name contains the pre-defined entity that we defined above, which in his turn getting /etc/passwd content.

2. So when the site requests my DTD file, it will try to get a nonexistent file that has in its name an entity that we defined as /etc/passwd, so it’ll show an error about the nonexistent file and after this error, we can see /etc/passwd, For more info and practice, check this

More explanation with a request:

I hosted a malicious DTD file that requested a nonexistent file, and we’ll use this payload to understand how the exploit works:

The response should contain the value of the XML parameter entity %secret

As you see, when I sent the request, the value of %secret reflected in the response, so if we changed the %secret value to /etc/passwd the file content will be reflected in the response, let’s see!!

The malicious DTD file will be like this:

we will do the same we did before, and wait to see if the response contains the /etc/passwd content or not!

and BINGOoO0o0oOO I got /etc/passwd contents

and finally, I managed to get a juicy P1 through Out-of-band XXE which leads to reading all files contents even large ones, then I reported it to the security team and it was accepted as a P1 submission and that’s what I want :))

Finally, thanks for reading! For feedback or any questions, just dm me on Twitter.

See you in the next write-up :)