I was experimenting some security stuff and especially trying to understand a ret2ret exploit. The code I was experimentating on :
void foo(char * val){
char buffer[64];
int i;
for (i=0; val[i]!=0; i++) buffer[i]=val[i];
return;
}
int main(int argc, char ** argv) {
foo(argv[1]);
return 0;
}
ASLR, N^X and stack canaries were off during my test. And I compiled it in 32 bits with gcc. I don't know why but I couldn't get the usual "0x41414141 in ?? ()" saying that I had overwritten $eip. So I decided to debug with gdb and put a breakpoint on the ret in the function "cop" and strangely enough even after writting more than 300 "A" the stack was like this:
0xbffff46c: 0xb7ee2290 0xbffff496 0xb7e8f5f5 0x41414141
0xbffff47c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff48c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff49c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff4ac: 0x41414141 0x41414141 0x41414141 0x00410043
The 64 chars corresponding to the buffer are here but the rest was not written.. and I don't know why ? Is it due to some kind of update ?
EDIT: GDB log for buff[64]
Dump of assembler code for function main:
0x08048415 <+0>: push %ebp
0x08048416 <+1>: mov %esp,%ebp
0x08048418 <+3>: sub $0x4,%esp
0x0804841b <+6>: mov 0xc(%ebp),%eax
0x0804841e <+9>: add $0x4,%eax
0x08048421 <+12>: mov (%eax),%eax
0x08048423 <+14>: mov %eax,(%esp)
0x08048426 <+17>: call 0x80483dc <foo>
0x0804842b <+22>: mov $0x0,%eax
0x08048430 <+27>: leave
0x08048431 <+28>: ret
Dump of assembler code for function foo:
0x080483dc <+0>: push %ebp
0x080483dd <+1>: mov %esp,%ebp
0x080483df <+3>: sub $0x44,%esp
0x080483e2 <+6>: movl $0x0,-0x4(%ebp)
0x080483e9 <+13>: jmp 0x8048404 <foo+40>
0x080483eb <+15>: mov -0x4(%ebp),%edx
0x080483ee <+18>: mov 0x8(%ebp),%eax
0x080483f1 <+21>: add %edx,%eax
0x080483f3 <+23>: movzbl (%eax),%eax
0x080483f6 <+26>: lea -0x44(%ebp),%ecx
0x080483f9 <+29>: mov -0x4(%ebp),%edx
0x080483fc <+32>: add %ecx,%edx
0x080483fe <+34>: mov %al,(%edx)
0x08048400 <+36>: addl $0x1,-0x4(%ebp)
0x08048404 <+40>: mov -0x4(%ebp),%edx
0x08048407 <+43>: mov 0x8(%ebp),%eax
0x0804840a <+46>: add %edx,%eax
0x0804840c <+48>: movzbl (%eax),%eax
0x0804840f <+51>: test %al,%al
0x08048411 <+53>: jne 0x80483eb <foo+15>
0x08048413 <+55>: leave
0x08048414 <+56>: ret
(gdb) b *foo+56
Breakpoint 1 at 0x8048414: file exploit.c, line 9.
(gdb) r `python -c 'print "A"*64'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/prog `python -c 'print "A"*64'`
Breakpoint 1, 0x08048414 in foo (arg=0xbffff6da 'A' <repeats 64 times>) at exploit.c:9
9 }
(gdb) r `python -c 'print "A"*65'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/prog `python -c 'print "A"*65'`
Program received signal SIGSEGV, Segmentation fault.
0x0804840c in foo (arg=0xbffff6d9 'A' <repeats 65 times>) at exploit.c:6
6 for(i = 0; arg[i] != 0; i++) buff[i] = arg[i];
EDIT 2: GDB log for buff[20]
(gdb) disas foo
Dump of assembler code for function foo:
0x080483dc <+0>: push %ebp
0x080483dd <+1>: mov %esp,%ebp
0x080483df <+3>: sub $0x18,%esp
0x080483e2 <+6>: movl $0x0,-0x4(%ebp)
0x080483e9 <+13>: jmp 0x8048404 <foo+40>
0x080483eb <+15>: mov -0x4(%ebp),%edx
0x080483ee <+18>: mov 0x8(%ebp),%eax
0x080483f1 <+21>: add %edx,%eax
0x080483f3 <+23>: movzbl (%eax),%eax
0x080483f6 <+26>: lea -0x18(%ebp),%ecx
0x080483f9 <+29>: mov -0x4(%ebp),%edx
0x080483fc <+32>: add %ecx,%edx
0x080483fe <+34>: mov %al,(%edx)
0x08048400 <+36>: addl $0x1,-0x4(%ebp)
0x08048404 <+40>: mov -0x4(%ebp),%edx
0x08048407 <+43>: mov 0x8(%ebp),%eax
0x0804840a <+46>: add %edx,%eax
0x0804840c <+48>: movzbl (%eax),%eax
0x0804840f <+51>: test %al,%al
0x08048411 <+53>: jne 0x80483eb <foo+15>
0x08048413 <+55>: leave
0x08048414 <+56>: ret
End of assembler dump.
(gdb) b *foo+56
Breakpoint 1 at 0x8048414: file exploit.c, line 9.
(gdb) r `python -c 'print "A"*200'`
Starting program: /root/prog `python -c 'print "A"*200'`
Breakpoint 1, 0x08048414 in foo (arg=0xbffff652 'A' <repeats 200 times>) at exploit.c:9
9 }
(gdb) c
Continuing.
[Inferior 1 (process 3474) exited normally]
I think I figured it out, at least for the 64 buffer. Your counting variable i is located higher on the stack than your buffer (per your disassembly).That means, your 65th store changes the value of i. Note that it won't be the entire value of i as it is probably a 4 byte integer; so only the lower byte (little-endian). In any case, after, it's as if you counted up i enough that the next write (66) should point to the area populated by the environmental variables (past ret), which is harmless and doesn't pollute eip.
My batts are almost done, and I can't finish this rigorously. But think along these lines.
Edit/crossing batt fingers: also, the 66th write might already pull in a 0 as both sides are affected by the pollution of i (where you store it in relative to &buffer; where you read it from relative to argv[1][0].