So. This box really fricked me up until a friend pointed me in the right direction after I had already explained a critical part of the box. Man I hate XXE (not really but the part I got stuck on was so obvious omg!).
Honestly nothing interesting here, just a generic NMap scan and some gobusting that leads to a couple files. The NMap results show an uninteresting port 22 and 80 open. Gobuster shows a couple directories which are forbidden, with the exception of one who's contents we never really acknowledge, and a couple files, the most interesting being "db.php" and "portal.php". Most of the recon involved dissecting the actual site itself, but we will revisit these results later.
We can checkout the website which is a generic company homepage. All links lead to nowhere, except one. There is a menu item named "portal" that points to an actual page (aptly named portal.php). Clicking it brings us to a suspiciously empty page with a single sentence: Portal under development. Go here to test the bounty tracker. which had a link embedded in the "here". As there's literally nothing else on this page the only logical thing to do was to follow that "here" to the next page. This new page brings us a very basic/generic "Bounty Report System - Beta" page, with a space that allows user input. So so far we have a site under development which allows user input? Might as well have had a header: "Hack this page!".
We can send some junk data and get a return that just echoes our input, making note that if the database were implemented, that input would now be in it. Intercepting it with BurpSuite shows us that the page is sending base64-encoded XML data to a page "tracker_diRbPr00f314.php", named so to prevent bruteforcing tools from discovering it. Immediately when I saw the data was send as XML I knew this would be an eXternal XML Entity attack. The real data being sent was:
<?xml version="1.0" encoding="ISO-8859-1"?>
To test the parser I try throwing some junk data that a poor XML parser wouldn't like, mostly arbitrary symbols. I noticed sometimes none of my input would be echoed if I passed one of these bad characters: "&", and "<". Next I tried an interesting string: FA<!-- test -->KE. The return was simply "FAKE", indicating the site understood and hid the HTML comment. Next up: Googling!
We can search for a generic XML attack, which eventually leads me to the code to extract the "/etc/passwd" file :
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hack [<!ENTITY mike SYSTEM "file:///etc/passwd">]>
As a quick XXE lesson: Some XML parsers will not scrub user data, allowing an actor to add their own XML to be parsed. This code defines an "entity" (similar to < in HTML) whos value is the contents of the file "/etc/passwd". We then tell the server to insert that entity as the "cwe" tag's return value to print. Once we send the data we can see that it worked! Instead of whatever data that would normally be in the CWE section, we have a dump of the servers "/etc/passwd" file! We can take note of the only user (who isn't root) with a home directory: "development". Maybe we can find their password somewhere... Now we need to use this to grant us access to the server itself.
Testing the exploit via BurpSuite
To simplify (and show off) the process of testing different payloads, I wrote a script that allows the user to define the payload, then send the exploit via cURL . At some point I got quite stuck and added a function of the script to bruteforce many common paths as defined in a txt file, the location of which you will need to edit.
On it's own, XXE doesn't result in RCE, unless the server has a specific PHP module installed that allows "expect://", which is not our case. Through our gobuster recon we notice one of the files found was "db.php". Trying to access it yeilds nothing. Typical PHP, eh? Trying to access it via our XXE exploit doesn't do much because the "db.php" page is still being processed as if a client browser was accessing it. Instead what we can do is grab the whole file using a PHP I/O stream: php://filter/convert.base64-encode/resource="db.php" . This will return the full file encoded in base64. All it takes now is to decode it; I use the "base64" command line tool to do so: base64 -d <<< '<encoded data here>'. We're given the contents of a database which contains a password! We can try the newly discovered password on the "development" user and... BAM! We're in. cat user.txt for the user flag, then ls to find some more interesting files...
Exploit results, dumping the "/etc/passwd" file in base64
In this home directory, we can see a file named "contract.txt". Reading it reveals we have "permissions" to test an internal tool given to this company by a client. Because we were given "permissions" to access this file, running sudo -l shows us the location of the tool, which is written in python!
Dumping the file shows us a bunch of code that honestly isn't hard to understand as long as you know even a little bit about general coding. Checking the last few lines of the file, we see the main code asks for a "ticket" path, runs the "load_file()" function, then finally runs "evaluate()". That last function sounds promising! Scrolling up reveals the 2 functions, which lead us to a bunch of rules we need to follow. Under the "load_file()" function we need to pass a file with the extension ".md", which we will create and start writing in. In the "evaluate()" function, we need to ensure the file contents start with:
# Skytrain Inc
## Ticket to
NOTE: There is a space at the end of the second line! This MUST be there or the tool won't accept the file!
The final line of the file is called the "code line". The first characters of this line must start with "**" followed by a number who's mod by 7 is 4, finally followed by a "+". Phew! And this isn't even the challenging part!
Snippet of the tool's code that executes the "code line"
After the pre-code shenanigans, we can - almost - start executing code. The tool will strip the "**" from the beginning of the line then evaluate the rest using the "eval()" function. The first thing to take care of is the incomplete equation of "4+". To solve this we can simply add a "0" after the plus to complete the equation. Next, we can try executing a simple "print" statement by appending an "and" to the line, followed by a print statement: **4+0 and print('Miked!'). Running the tool and passing our file's path results in a beautiful "Hello", followed by a bunch of error code :/. The errors don't matter much because we've executed our own code! Now we can import the "pty" module then spawn a shell, no? No. The "eval()" python function evaluates expressions. This means we get an error if we try using the "import" keyword. A similar python function is "exec()" which allows for the dynamic execution of code. Therefore, all we need to do is let the tool evaluate the return of an "exec()" function, wherein we spawn a shell. The full line ends up being: **4+0 and exec('import pty; pty.spawn(\"/bin/bash\")'). Run the tool as sudo, pass our malicious file, and we finally get a root shell! Read that root.txt file knowing you're now skilled enough to understand and manipulate Python code!
Executing our "ticket" to get a root shell
Portswigger XXE intro [EXTERNAL]
IPPSec video for XXE exfiltration [EXTERNAL]
Last edit: 2021.08.20