The caption for this CTF reads "When we access this page we get a Forbidden error. However we believe that something strange lies behind... Can you find a way in and retrieve the flag?"
After starting the Docker instance and browsing to http://docker.hackthebox.eu:30724/ we are greeted by
"(403) Forbidden, You don't have permission to access / on this server
As with all web pages, the content rendered by the browser is the tip of the ice berg in terms of the actual data exchanged by the client and server. It is my personal preference to utilise two separate browsers simultaneously, this is a practice I have used for a number of reasons. One of these reasons is that the content which browsers display may vary both because of the design of that browser but also the server may serve content bespoke for each browser (This is determined in the user-agent request header). The two browsers I primarily use are Chrome and Firefox, as a personal preference I prefer using Firefox for quickly customising requests.
My standard go-to for any development / testing "Open developer tools and a second window with the page source". Nothing in the response headers appeared out of the ordinary, the page source was your standard Apache 403 message, no hidden breadcrumbs in either place. Next up, manually test:
After a cursory "prod and poke" (spending no more than 2 minutes) I first looked to DirBuster for further testing as much of next set of tests would be repetitive and time consuming if done manually. Starting with the shortest wordlist of the most common words and "GET" and "POST" methods. Immediately a 200 OK hit returned for (POST) http://docker.hackthebox.eu:30724/index.php.
Using Firefox developer tools (see previous) I sent a POST request to the index.php page. Firefox displayed a form with "Change Username:" and "not an admin (yet)". The page source revealed a comment in the form
<input type="text" name="fuckhtml" placeholder="notimportant">
<!-- HTB hint:really not important...totaly solvable without using it! Just there to fill things and to save you from some trouble you might get into :) -->
<input type="submit" value="Change">
On typing "admin" and clicking the "Change" button it appeared that the form was either broke in some way or had no functionality. To confirm this I tried with cURL:
In the response headers one line in particular stood out:
the last 6 characters of this string "%3D%3D" were of particular interest %3D is the URL encoded representation of = this looked liked a Base64 string with a "==" padding suffix. Lets decode that:
echo eyJVc2VyIjoid2hvY2FyZXMiLCJBZG1pbiI6IkZhbHNlIiwiTUFDIjoiZmY2ZDBhNTY4ZDYxZTVhMDNiY2RiMDQ1MDlkNTg4NWQifQ== | base64 --decode
The "Admin":"False" key seemed a good starting point and after changing "False" to "True" and rehashing as Base64 I had the string (worth noting this could not be done via echo then piping to Base64 on the command line to encode as the JSON contained quotation marks which need to be escaped. I used an online tool to encode this string):
In Firefox developer tools I re-sent the POST request with the "Cookie: ses=...." now set to the new string and the previous message of "not an admin (yet)" now read "what are you trying to do huh?"
The next value to attempt to tamper was the Message Authentication Code "MAC" field. A few cursory probes at this resulted in capturing the flag, however, I wasn't exactly sure how this result came about (the values tested were a NULL value and then variants of letters or numbers both inside and outside of quotes, e.g. -1 "-1" "a" a). So I went to Google to investigate ...
I immediately ran into other write-ups for this CTF and a wealth of information on PHP type juggling (a strange behavior of PHP where "==" is a "loose comparison" operator and "===" would be a strict comparison, there is a lookup table available online to show how which loose comparisons would return True, for example array() == NULL. String to integer conversion in PHP also has strange results, if the string starts with a letter or the number 0 then that string will have an integer value of 0 (e.g. "abcde1337" would be int(0)). Some other examples (taken from OWASP):
So, fumbling around fuzzing input and semi-educated poking and prodding helped me capture the flag. More importantly some lessons were learned ...
Customise request headers and HTTP verb
Response to POST request made to http://docker.hackthebox.eu:30724/index.php