One of my objective in 2020 is to level up in Reverse Engineering. Since I am starting from zero I got few people recommend http://pwnable.kr which appear to have a reasonable curve of difficulty making it ideal for learning.
If you have other recommandations for good ressources to learn and practices Reverse Engineering feels free to let me know below in a comment or contact me ;)
That being said, here is my write-up for the second challenge of pwnable.kr: Collision.
Let’s go!
Challenge Description
Collision
Daddy told me about cool MD5 hash collision today. I wanna do something like that too!
This gives us an SSH access to a box with a hint that this challenge seems to be about MD5 Collision.
Hash Collision
In order to get the right context, here is a definition of Hash Collision:
A Hash Collision Attack is an attempt to find two input strings of a hash function that produce the same hash result. Because hash functions have infinite input length and a predefined output length, there is inevitably going to be the possibility of two different inputs that produce the same output hash. If two separate inputs produce the same hash output, it is called a collision. This collision can then be exploited by any application that compares two hashes together – such as password hashes, file integrity checks, etc.
We notice a function called check_password, that’s very probably where the magic happen. Let’s use ni to jump a bunch of time until we arrive to the said function:
We can see that our password got set in $rdi and $rdx registers. At this point I put up a breakpoint on check_password function but doesn’t quite understood what exactly was going on (except some loop and calculations). Let’s continue to see what happen in main once check_password return something:
Let’s edit the value we have in $rax to match 0x21dd09ec (568134124):
1 2 3
gef➤ print $rax $2 = 0x46464645 gef➤ set $rax=568134124
We continue the execution and we can see the bin trying to display the flag:
1 2 3 4 5 6
gef➤ c Continuing. [Detaching after vfork from child process 986] /bin/cat: flag: No such file or directory [Inferior 1 (process 916) exited normally] gef➤
Alright! We now understood the big picture of what’s going on behind the hood and what kind of check is being made by the bin. Let’s now take a look at the source code to find a proper way to exploit the check_password function without using a debugger.
The function check_password() take our password as argument and should return 0x21DD09EC in order to have the flag displayed. In decimal this gives:
1 2
>>> int("0x21DD09EC", 16) 568134124
Let’s now focus on the check_password() function since the magic is happening here. We know it needs to returns 568134124 in order to pass the check and displays the flag.
1 2 3 4 5 6 7 8 9
unsignedlongcheck_password(constchar* p){ int* ip = (int*)p; int i; int res=0; for(i=0; i<5; i++){ res += ip[i]; } return res; }
The function is quite short but no so straightforward.
Let’s decompose line by line to understand what’s going on.
The char pointer passed as parameter is cast to an int pointer: (int*)p;. ip is an array of pointers starting with the pointer to p. Example: char* p = "ABCD" = "\\x41\\x42\\x43\\x44", int* ip = 0x44434241
The char array is iterated 4 bytes at a time (4 * 5 = 20 bytes of the passcode), then summed up into res.
1 2 3
for(i=0; i<5; i++){ res += ip[i]; }
The sum results get returned.
Exploitation
Manual exploitation
So we understood we need our passcode 5 parts sum to be equal to 0x21DD09EC. Now it’s math time!
Alright, we have all the 4 parts of our password. Since we are working with C, integers are stored in little-endian format. Therefore the byte order of the integers need to be reversed.
Nothing gets printed since those are non-printable chars.
Let’s now use it to solve the challenge:
1 2
col@pwnable:~$ ./col $(python -c 'print "\xc8\xce\xc5\x06" * 4 + "\xcc\xce\xc5\x06"') daddy! I just managed to create a hash collision :)
Using pwntools
A simple challenge like this one is a good occasion to try our hand on pwntools which can probably comes very useful in the future for more complex challenges.
Pwntools is a CTF framework and exploit development library. Written in Python, it is designed for rapid prototyping and development, and intended to make exploit writing as simple as possible.
After installing the tool let’s create a simple script to solve the challenge.
First let’s connect to the server:
1 2 3
from pwn import *
server = ssh('col', 'pwnable.kr', 2222, 'guest')
Pop a shell and run ./col process with our payload as argument. Here we use the very useful pack function from pwnlib in order to convert our hex values to little endian: