This code challenge can be found on Pwnable.kr if you are interested in security and programming head over and check out there webpage
This is a write up of the solution to Collision
logging onto the server we do a quick ls -l to see what we are working with.
col@ubuntu:~$ ls -l total 16 -r-sr-x--- 1 col_pwn col 7341 Jun 11 2014 col -rw-r--r-- 1 root root 555 Jun 12 2014 col.c -r--r----- 1 col_pwn col_pwn 52 Jun 11 2014 flag
This shows us now a few things. 1st user col can read and execute col but can not write to it. col can read col.c but not edit it. 2nd col can not read write or execute flag. 3rd col.c is run as root and can read flag.
So lets see what we can see. vim col.c returns:
As you can see from the highlighted code, the password length needs to be 20 bytes. How can we represent a single number as 20 bytes though. Well the program gives you a hint in the function
check_password. It is looking for 5 int’s. This is explained with some basic computer math we know one int is 4 bytes (check size of int) therefore we know we need 5 integers then the numbers are added to together and passed the logic that outputs flag. The hash we are looking for is
0x21DD09EC To obtain the flag we need to input a this value into the program. Let’s find 5 integers that are in total equal to
Python 2.7.10 [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> 0x21DD09EC 568134124 >>> 568134124 / 5 113626824 >>> 568134124 - 4 * 113626824 113626828 >>> 4 * 113626824 + 113626828 568134124
Hey hey he what did we just do there. ok lets take it step by step.
First we opened python and converted 0x21DD09EC to an in 568134124
Next we devided 568134124 by 5 giving us 113626824
So why did we not stop there were have 5 values. Well that is not intirely true, we have a truncated value. 568134124 /5 is actually 113626824.8 so wee need our last value.
568134124 – 4*113626824 giving us 113626828
Checking our results
4 * 113626824 + 113626828 equals
Next step is to convert the integers to bytes. It is not that easy as typing it in python. It is good to note that C integers are stored in little-endian format (meaning byte order is reversed).
I needed a little google foo to find out how to convert I use
struct.pack to convert integers to the little-endian. The first argument of
struct.pack(the format of the conversion) consists of two parts:
< means that the bytes need to be in little-endian order, and
i means that it needs to be converted to the size of an int.
>>> import struct >>> 4 * struct.pack('<i', 113626824) + struct.pack('<i', 113626828) '\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06'
lets input our result and see if we are right:
col@ubuntu:~$ ./col '\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06'
passcode length should be 20 bytes
Hum ok what is wrong. The next step really gave me a hard time. Since we have hex values they are not translating correctly into the program
lets take a look at how we can pass a value correctly to strlen(argv) != 20 and identify how we can pass argv as a string and still be 20 bytes. Doing some more google foo we need to pass the string as its binary representation to col so we can use echo -e acording to the documentation -e will enturprate \xHH to the Hexadecimal value HH
col@ubuntu:~$ ./col `echo -e "\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06"` daddy! I just managed to create a hash collision 🙂
And we did it. Congratulations.