pwnable.kr [Part I] Random

Sept. 29, 2020 // echel0n

It is a really easy challenge but enjoyable.
When we look the source code, this line seems weird:

  1. random = rand(); // random value!

Let's check the description of rand() function.

  1. int rand(void):
  2. returns a pseudo-random number in the range of 0 to RAND_MAX.
  3. RAND_MAX: is a constant whose default value may vary
  4. between implementations but it is granted to be at least 32767.

But we can see that, it declared without a seed.

  1. The srand() function sets its argument as the seed for a new sequence of pseudo-random integers to be returned by rand().
  2. These sequences are repeatable by calling srand() with the same seed value.
  3. If no seed value is provided, the rand() function is automatically seeded with a value of 1.

So, it is generating the same number all the time. Easy solution would be inserting a printf() call and run the binary on your machine.

  1. unsigned int random;
  2. random = rand(); // random value!
  3. printf("%d", random);
  1. gcc source.c -o random
  1. $ ./random
  2. 1804289383

It yields the generated number now.

  1. if( (key ^ random) == 0xdeadbeef ){

We can XOR the generated number with 0xdeadbeef to get key.

  1. >>> 1804289383 ^ 0xdeadbeef
  2. 3039230856
  3. >>>

The result is our key.

  1. random@pwnable:~$ ./random
  2. 3039230856
  3. Good!
  4. Mommy, I thought libc random is unpredictable...
  5. random@pwnable:~$

The other way, let's assume that we dont own the source code. So, we will use a debugger.

  1. 0x00000000000011dd <+84>: call 0x1070 <__isoc99_scanf@plt>
  2. 0x00000000000011e2 <+89>: mov eax,DWORD PTR [rbp-0x10]
  3. 0x00000000000011e5 <+92>: xor eax,DWORD PTR [rbp-0xc]
  4. 0x00000000000011e8 <+95>: cmp eax,0xdeadbeef
  5. 0x00000000000011ed <+100>: jne 0x1213 <main+138>
  6. 0x00000000000011ef <+102>: lea rdi,[rip+0xe15] # 0x200b
  7. 0x00000000000011f6 <+109>: call 0x1030
  8. 0x00000000000011fb <+114>: lea rdi,[rip+0xe0f] # 0x2011
  9. 0x0000000000001202 <+121>: mov eax,0x0
  10. 0x0000000000001207 <+126>: call 0x1050 <system@plt>

After scanf() the binary compares two value (11e8: cmp eax, 0xdeadbeef), they are located at [rbp-0x10] and [rbp-0xc] We can put a breakpoint there and check these.

  1. gef➤ print $rbp-0x10
  2. $1 = (void *) 0x7fffffffe820
  3. gef➤ print *0x7fffffffe820
  4. $2 = 0x0
  5. gef➤ print $rbp-0xc
  6. $3 = (void *) 0x7fffffffe824
  7. gef➤ print *0x7fffffffe824
  8. $4 = 0x6b8b4567
  9. gef➤ print/d *0x7fffffffe824
  10. $5 = 1804289383
  11. gef➤

We got the same value again but at this time we grabbed that from our memory.