Our goal in this challenge is to bypass a logging page to get the flag. Moving the mouse over the web page, we found a link to admin.php page, the source code of this page was provided to us.
Navigating to admin.php page, we see a login page where we are prompted to provide the correct username, password and hmac for a valid logging.
Looking into the source code, a flag.inc.php page is included to get the value of the variables: `$secret`, `$password` and `$flag`, obviously cause these variable were not defined anywere in the admin.php sorce code.
the first check, test if all the POST variables: username, password, hmac and nonce are not empty (nonce is a random value generated each time we refresh the page, and it is sended back once we submit our request), we can easily validate this check.
if (!empty($_POST['username']) && !empty($_POST['password']) &&
!empty($_POST['hmac']) && !empty($_POST['nonce']))
The next check, test if the username is 'admin" and password equal to `$password` and hmac equal to `$hmac`:
if (strcmp($_POST['username'], "admin") == 0 &&
strcmp($_POST['password'], $password) == 0 &&
$_POST['hmac'] === $hmac)
Let's decompose this into three checks and bypass each one individually (for this purpose we can setup a web site locally with the admin.php code, and modify it so that we can test each check individually):
- We can simply set username to "admin" to pass the first condition.
- To validate the password, the website check if the user provided password equal to the one defined in flah.inc.php file, this is done by using strcmp function and check if the returned value equal to 0, strcmp return 0 if both string match, as per [user note](https://www.php.net/manual/en/function.strcmp.php#108563) in php Docs of this function: strcmp returns 0 in some cases where the variables being compared are of different types, for example a string and an array will return 0.
So to bypass this check without knowing the `$password` value we can simply set the user password as an array.
- The admin page override the `$secret` value with the sha256 hmac of `$nonce` using `$secret` as key ($secret was firstly defined in flag.inc.php page): `$secret = hash_hmac('sha256', $_POST['nonce'], $secret);`.
Then it set `$hmac` var with the calculated value of sha256 hmac of "admin"(username) using the new `$secret` value as key: `$hmac = hash_hmac('sha256', $_POST['username'], $secret);`.
And it check if the user hmac match the `$hmac` variable (here also the type is checked): `$_POST['hmac'] === $hmac`
As per the below test, if we try to calculate the hmac of an array we got a warning and the variable taking the output of hmac is defined with an empty string:
php > echo $secret;
PHP Notice: Undefined variable: secret in php shell code on line 1
php > $secret = hash_hmac('sha256', , 'secret');
PHP Warning: hash_hmac() expects parameter 2 to be string, array given in php shell code on line 1
php > echo $secret;
So to bypass this check without knowing the `$secret` value we can set `$nonce` as an array, which will result in an empty string, assigned to `$secret`, at this point we can simply calculate the hmac of "admin" with an empty string as key which will be used as user hmac in our request:
php > echo hash_hmac('sha256', 'admin', '');
So now, let us join all the pieces together to construct out final payload:
[[email protected] ~]$ curl -X POST http://challenges.uactf.com.au:30002/admin.php -d "username=admin&password=admin&hmac=8d5f8aeeb64e3ce20b537d04c486407eaf489646617cfcf493e76f5b794fa080&nonce=c0ab90748f754cdd53a4a2ad1e780500"
With the above request, we can pass the checks and get the flag.