I'm learning about basic buffer overflows, and I have the following C code:
int your_fcn()
{
char buffer[4];
int *ret;
ret = buffer + 8;
(*ret) += 16;
return 1;
}
int main()
{
int mine = 0;
int yours = 0;
yours = your_fcn();
mine = yours + 1;
if(mine > yours)
printf("You lost!\n");
else
printf("You won!\n");
return EXIT_SUCCESS;
}
My goal is to bypass the line mine = yours + 1;
, skip straight to the if
statement comparison, so I can "win". main()
cannot be touched, only your_fcn()
can.
My approach is to override the return address with a buffer overflow. So in this case, I identified that the return address should be 8
bytes away from buffer
, since buffer is 4
bytes and EBP
is 4
bytes. I then used gdb
to identify that the line I want to jump to is 16
bytes away from the function call. Here is the result from gdb:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000054a <+0>: lea 0x4(%esp),%ecx
0x0000054e <+4>: and $0xfffffff0,%esp
0x00000551 <+7>: pushl -0x4(%ecx)
0x00000554 <+10>: push %ebp
0x00000555 <+11>: mov %esp,%ebp
0x00000557 <+13>: push %ebx
0x00000558 <+14>: push %ecx
0x00000559 <+15>: sub $0x10,%esp
0x0000055c <+18>: call 0x420 <__x86.get_pc_thunk.bx>
0x00000561 <+23>: add $0x1a77,%ebx
0x00000567 <+29>: movl $0x0,-0xc(%ebp)
0x0000056e <+36>: movl $0x0,-0x10(%ebp)
0x00000575 <+43>: call 0x51d <your_fcn>
0x0000057a <+48>: mov %eax,-0x10(%ebp)
0x0000057d <+51>: mov -0x10(%ebp),%eax
0x00000580 <+54>: add $0x1,%eax
0x00000583 <+57>: mov %eax,-0xc(%ebp)
0x00000586 <+60>: mov -0xc(%ebp),%eax
0x00000589 <+63>: cmp -0x10(%ebp),%eax
0x0000058c <+66>: jle 0x5a2 <main+88>
0x0000058e <+68>: sub $0xc,%esp
0x00000591 <+71>: lea -0x1988(%ebx),%eax
I see the line 0x00000575 <+43>: call 0x51d <your_fcn>
and 0x00000583 <+57>: mov %eax,-0xc(%ebp)
are four lines away from each other, which tells me I should offset ret
by 16
bytes. But the address from gdb says something different. That is, the function call starts on 0x00000575
and the line I want to jump to is on 0x00000583
, which means that they are 15
bytes away?
Either way, whether I use 16
bytes or 15
bytes, I get a segmentation fault
error and I still "lose".
Question: What am I doing wrong? Why don't the address given in gdb go by 4 bytes at a time and what's actually going on here. How can I correctly jump to the line I want?
Clarification: This is being done on a x32 machine on a VM running linux Ubuntu. I'm compiling with the command gcc -fno-stack-protector -z execstack -m32 -g guesser.c -o guesser.o
, which turns stack protector off and forces x32 compilation.
gdb of your_fcn()
as requested:
(gdb) disassemble your_fcn
Dump of assembler code for function your_fcn:
0x0000051d <+0>: push %ebp
0x0000051e <+1>: mov %esp,%ebp
0x00000520 <+3>: sub $0x10,%esp
0x00000523 <+6>: call 0x5c3 <__x86.get_pc_thunk.ax>
0x00000528 <+11>: add $0x1ab0,%eax
0x0000052d <+16>: lea -0x8(%ebp),%eax
0x00000530 <+19>: add $0x8,%eax
0x00000533 <+22>: mov %eax,-0x4(%ebp)
0x00000536 <+25>: mov -0x4(%ebp),%eax
0x00000539 <+28>: mov (%eax),%eax
0x0000053b <+30>: lea 0xc(%eax),%edx
0x0000053e <+33>: mov -0x4(%ebp),%eax
0x00000541 <+36>: mov %edx,(%eax)
0x00000543 <+38>: mov $0x1,%eax
0x00000548 <+43>: leave
0x00000549 <+44>: ret
-fomit -frame-pointer
and you’re working with GCC, you may get a different result. And a good optimizing compiler night spot the UB and arrange to reformat your disk before you do any real damage — or maybe you’ll be lucky and your compiler will let your hard drive live to see another day – Jonathan Lefflercall
itself is not relevant since you are overwriting the return address. Hence, you should calculate the offset relative to0x0000057a <+48>
. Assuming you have found the correct place on the stack, that should mean an offset of9
. – Jesteryour_func
. Presumably your offset ofret = buffer + 8;
is wrong. – Jesterret
at ebp-4 andbuffer
at ebp-8, so the return address is at buffer+12. – prl