Tags: md5 

Rating:

**Description**

> Prove you are not a baby:
>
> http://185.168.130.148/

**No files provided**

**Solution**

> (Note: write-up written without access to the original service. I tried to replicate the source as best as I could remember.)

We are presented with a PHP script giving us access to its source and a [`.so` file](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-09-08-HackIT-CTF/files/auth.so) (shared object) it uses.

```php
you may need [this](?source) or [this](auth.so).

```

The script seems simple enough but clearly we need to understand the `auth` function, which is defined in the shared object. We can look at the function and decompile it with IDA:

```c
Php::Parameters *__fastcall auth(Php::Parameters *a1, __int64 a2)
{
__int64 v2; // rax
const char *v3; // rax
char dest; // [rsp+10h] [rbp-60h]
char v6[8]; // [rsp+30h] [rbp-40h]
unsigned __int64 v7; // [rsp+58h] [rbp-18h]

v7 = __readfsqword(0x28u);
strcpy(v6, "21232f297a57a5a743894a0e4a801fc3");
v2 = std::vector<Php::Value,std::allocator<Php::Value>>::operator[](a2, 1LL);
v3 = (const char *)Php::Value::operator char const*(v2);
strcpy(&dest, v3);
Php::Value::Value(a1, v6, -1);
return a1;
}
```

The PHP wrappers are a bit confusing at first but essentially, `v2` gets set to the second argument given to the function, `v3` gets its C-string value, and that value is then `strcpy` copied into `dest`. [`strcpy`](https://linux.die.net/man/3/strcpy) (see "Bugs") is a big source of overflow vulnerabilities of course, and it is the same here. If the second argument given to `auth` is too long, i.e. more than 32 bytes, it will overflow into `v6` (since `v6` is right after `dest` on the stack).

The first argument given to `auth` is actually not used at all.

What happens if we overflow into `v6`? We can cause the function to return a different value. It can be any short string of our choosing (the `$p` variable is still limited to 45 characters, minus the 32 needed to overflow into `dest`).

So, we need two values which are different but their MD5 digests are not? The condition seems to assert that, and it would mean that we need to find two relatively short strings that cause a MD5 collision. Some quick research shows that the shortest known pair like this is [64 bytes of binary data](https://stackoverflow.com/questions/1999824/whats-the-shortest-pair-of-strings-that-causes-an-md5-collision), which will not fit.

Luckily, PHP is a bit weird, and the vulnerability is in this line:

```php
if (md5($user) == md5($digest) && $digest !== $user) {
```

The MD5 digests are compared using the [equality operator `==`](https://secure.php.net/manual/en/language.operators.comparison.php), while the strings themselves are compared using the [not-identity operator `!==`](https://secure.php.net/manual/en/language.operators.comparison.php). Basically, the latter is type safe, while the former can sometimes compare variables in somewhat arbitrary ways.

In particular, strings can be interpreted as numbers in scientific notation. For this to happen, the string needs to be all numbers except for an `e`. As it turns out, MD5, or more specifically, the `md5` function in PHP, produces digests of 32 hexadecimal digits. It is possible that the digest for a certain string follows the scientific notation. It takes some time to brute-force out candidates like this, but it is possible.

With even more time, digests of even more specific formats can be found. If the digest starts with `0e`, followed by decimal digits only, e.g. `0e830400451993494058024219903391`, to PHP it looks like `0 * 10^830400451993494058024219903391`, which is zero in value!

So we need to provide two short strings that both give an MD5 digest starting with `0e` followed by decimal digits only. If you search for such an example, you may find this pair:

```
md5("240610708") === "0e462097431906509019562988736854"
md5("QNKCDZO") === "0e830400451993494058024219903391"
```

So now we have all we need to exploit the website. We provide `240610708` as the username, and we put `QNKCDZO` in our password after 32 padding characters to make the digest be `QNKCDZO`:

http://185.168.130.148/?u=240610708&p=abcdabcdabcdabcdabcdabcdabcdabcdQNKCDZO

`flag{here_is_a_warmup_chal_for_u_baby_}`

Original writeup (https://github.com/Aurel300/empirectf/blob/master/writeups/2018-09-08-HackIT-CTF/README.md#42-web--babypeehpee).