4

So I'm doing some CTF and one of the challenge is about php type juggling. The code looks something like this

if($_GET['var1'] == hash('md4', $_GET['var1']))
{
//print flag
}

So I was 80% sure that I need to pass in an integer so it'll be true but all I can manipulate is the url. I tried using python request to do it but still failed.

import requests
url = 'http://example.com'
params = dict(var1=0)
r = requests.get(url=url, params=params)

Is there any special case where php will treat the variable pass into GET array as int? Or there is other way to do so?

Dharman
  • 30,962
  • 25
  • 85
  • 135
Rui Dian
  • 75
  • 6
  • Maybe it's just me, but what is a CTF? – Ibu Jun 07 '19 at 02:12
  • 1
    Also unless you have a collision, `$_GET['var1']` cannot be equal to `hash('md4', $_GET['var1'])`. – Ibu Jun 07 '19 at 02:15
  • I guess https://ctf.hacker101.com/ – A l w a y s S u n n y Jun 07 '19 at 02:16
  • 1
    @Ibu CTF is Capture The Flag. Also, there is case where it can be. Let's say md4 hash of 0 is ea5698173fc6fdbe30a9af462b9fc847. In php if you compare 0 and a string, if the string doesn't starts with int it'll always return true – Rui Dian Jun 07 '19 at 02:25
  • This is a really interesting question. What PHP version are you using? – Phil Jun 07 '19 at 02:45
  • @Phil Well, the whole point of this is to find the vulnerabilities. I have no access to the source code so I can't change anything and my job is to exploit this function. If I'm not wrong this exists in every php versions? Type-juggling is always a bug in php – Rui Dian Jun 07 '19 at 02:47
  • On my system (PHP 7.2.17), any value in `$_GET` is a string. Only when comparing the actual integer `0` to `ea5698173fc6fdbe30a9af462b9fc847` do I get a _truthy_ result. – Phil Jun 07 '19 at 02:52
  • It would work with `True` too, but not a string neither. – remeus Jun 07 '19 at 02:54
  • Ah, I understand your question now and I think the answer is no, PHP always treats request parameters as strings – Phil Jun 07 '19 at 02:55
  • I'm aware of int and string comparison will result in some false positive result and that's what I want to exploit. But now my question is can I pass in an int to the $_GET array and have it stored as an int instead of string? – Rui Dian Jun 07 '19 at 02:56
  • 1
    Have you tried not passing anything at all? That should give you null on both sides. – aaaaaa123456789 Jun 07 '19 at 02:59
  • Again, I think the answer is no. The only other type you can serialize into request parameters is an array by using square brackets, eg `?var1[]=0` but that's unlikely to help – Phil Jun 07 '19 at 03:00
  • @aaaaaa123456789 nope, the MD4 hash of `null` is `31d6cfe0d16ae931b73c59d7e0c089c0` – Phil Jun 07 '19 at 03:01
  • Thanks @Phil. Your answer reminds me of another bug in php with strcmp. If an array is compared to string it'll return 0 haha but in this case it's not going to work XD Guess now I have to research on md4 collisions then. Thanks again – Rui Dian Jun 07 '19 at 03:10
  • I am curious about the answer of this problem. Do you think there is any invariant for the md4 function? – remeus Jun 07 '19 at 03:35

1 Answers1

1

The answer to this CTF can be found here https://medium.com/@Asm0d3us/part-1-php-tricks-in-web-ctf-challenges-e1981475b3e4

The general idea is to find a value which starts with 0e and its hash also starts with 0e. The page gives an example of 0e001233333333333334557778889 which produces a hash of 0e434041524824285414215559233446

This notation is called scientific notation. To understand why this kind of type juggling works you need to understand what both numbers are. If we convert from scientific notation to decimal it becomes obvious.

0e001233333333333334557778889 = 0×101233333333333334557778889
0e434041524824285414215559233446 = 0×10434041524824285414215559233446

From primary school maths we know that anything multiplied by 0 is 0, which means both numbers are 0.

All input provided in the $_GET or $_POST superglobals is of type string. Thanks to PHP's type juggling system both strings are treated as floating point numbers written in scientific notation and when cast to float they both equal 0.

Dharman
  • 30,962
  • 25
  • 85
  • 135