Information#
CTF#
- Name : CONFidence CTF 2019 Teaser
- Website : confidence2019.p4.team
- Type : Online
- Format : Jeopardy
- CTF Time : link
My admin panel - Web#
I think I've found something interesting, but I'm not really a PHP expert. Do you think it's exploitable?
Ok let's give them what is asked and get a hint:
We will get into the for
loop 32 time as the length of a md5 hash is 32.
ord(MD5($cfg_pass)[$i]) & 0xC0
will take each char of the MD5 hash of $cfg_pass
one by one and Bitwise AND it with 0xC0
, then will put the result as a decimal.
First we need to split 0006464640640064000646464640006400640640646400
. But how?
MD5 hashes are only using hexadecimal characters so chars in [0-9A-Z]
, in decimal from 48 (0) to 57 (9) and from 65 (A) to 90 (Z). But there is the Bitwise AND with 0xC0
.
0x00 & 0xC0
to 0x39 & 0xC0
will be equal to int(0)
and 0x40 & 0xC0
to 0x7F & 0xC0
will be equal to int(64)
.
So all letters will result in 64
and all numbers to 0
.
So we have 0 0 0 64 64 64 0 64 0 0 64 0 0 0 64 64 64 64 0 0 0 64 0 0 64 0 64 0 64 64 0 0
.
[0-9]
: 10 possibilities[A-F]
: 6 possibilities
Let's count how many of each there are:
This make a lots of possibilities even if it is far less than bruteforcing MD5 without a clue:
This make the hash nearly 96 bits strong instead of 128 bits strong. This is still too much.
So we may need to bypass the loose comparison $session_data['hash'] != strtoupper(MD5($cfg_pass))
with a type juggling.
Let's see the PHP type comparison tables and remind us some stuff reading PHP Magic Tricks: Type Juggling.
We may expect NULL
to be truthy with something else but $session_data === NULL
is preventing us from doing that and we still need to match this regex /^{"hash": [0-9A-Z\"]+}$/
.
But json_decode
will convert the JSON string to PHP object, so we can not only provide strings but also integers thanks to JSON.
Instead of /^{"hash": [0-9A-Z\"]+}$/
the regex should have been /^{"hash": "[0-9A-Z]{32}"}$/
to be more secure to force us to send a string.
And we know this trick with type juggling:
As we have 0 0 0 64 64 64 0 64 0 0 64 0 0 0 64 64 64 64 0 0 0 64 0 0 64 0 64 0 64 64 0 0
the string will be interpreted to an integer because it begins with a number and as there are three 0
it begins with three numbers. So if we send {"hash": <num>}
where num is an integer form 0 to 999 we should get the bypass by bruteforcing all the values.
If you don't understand what I mean, we need something like that:
So I did a quick bash script:
The flag was p4{wtf_php_comparisons_how_do_they_work}
.