impossible to write on stack (stack overflow)
Asked Answered
M

1

7

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]
Mechanize answered 1/2, 2014 at 21:51 Comment(7)
How did you compile your example? Which precise version of GCC are you using? It might have been optimized by the compiler.....Obscurity
I am running under kali 32 bits and gcc (Debian 4.7.2-5) 4.7.2. I compiled it this way : "gcc -ggdb -z execstack -fno-stack-protector -o prog exploit.c" (ASLR is off). I just tried something 5mn ago by adding "-mpreferred-stack-boundary=2" and resizing my buffer to 20 chars. And I was astonished to see in gdb that with 200 chars I can still return to the main and execute the rest.... ?Mechanize
Can you provide gdb log? and of course - disas the functions.Ichinomiya
As this shouldn't happen. I suggest you also add how you feed your buffer. You probably use some perl idiom, but how exactly do you execute your program? (and aligning the stack obviously doesn't matter if you don't mean to feed an actual address; as well as the nx bit set or not is irrelevant if you're not trying to insert/execute shell code).Petrous
With GCC 4.8.2 on my 32-bit machine, this code compiled with those options quite happily segfaults if I give it too much input...Jarvisjary
Here on the first edit, I can't understand why I have a segfault even before executing ret and just with an overflow of 1 character ? The second edit is even more troubling with 200 input chars I have no segfaults... ?Mechanize
Please print the stack before and after you actually start to copy the input into the buffer.Ichinomiya
P
3

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].

Petrous answered 1/2, 2014 at 23:33 Comment(10)
Sounds very reasonable! I also think this is the reason. Can be verified easily by checking the value of i during the run.Ichinomiya
@Petrous look at the i's value. It is 0x00410043, so I don't understand why you're not sure in your own answer. It either gets PF while trying to read argv[0] + 0x00410043 or it is 0 that is stored thereEncumbrance
Yes it looks like you are right, I have printed i and when I input 65 chars, i goes from 64 and jump directly to 66 without passing by 65 and 66 is the end of argv[1] ('\0'). for the second code, the variable i is also affected. With an overflow of 70 chars for only 20 reserved char, i goes from 0 to 20 and from 66 to 67. I guess it might be actually writing in the environmental variable. I have tried to change the order of the variables but it doesn't affect the problem. And here I believed that variables were being pushed in the same order as in the code, is there a way to do so ?Mechanize
@Mechanize what do you mean? It just compiles the code that you've written. If you'd included any performance optimization flags i would certainly be contained in register, and therefore your buffer overflow would actually happen as you expectedEncumbrance
well like gnometorule said i is stored in a lower address than buff[] in the stack and is being polluted . So how can I reverse the order making it that buff[] is directly placed after $ebp so that i isn't affected anymore by the overflow ? (I have tried to change the order of the variables in my C code but there is no change)Mechanize
@Mechanize struct { int i; char buf[0x40]; } s; for exampleEncumbrance
@user3102158: gcc doesn't need to compile in the order you define your variables. Older versions of gcc DID compile in the order you defined variables - not because the C standard was different then, it's just how it worked. You can get this kind of overflow to run with different mechanics; just take it as a valuable lesson that order of variables in your code doesn't relate to order in the disassembly.Petrous
Then just out of curiosity, in this case of scenario, it's impossible to write over the return address and even ebp ? And beyond the fact that the code is vulnerable, is there a way to exploit it or is it due to some luck that it's impossible to redirect the flow of execution ?Mechanize
@user3102158: just strcpy() your argv[1] onto your buffer instead of using a loop. This should generate a segfault. As to actually 'exploiting' it as in re-directing execution flow into (say) stack-based shell code, this still works too (after disabling what you're familiar with), but obviously needs different code.Petrous
I should say it still works on my system (ubuntu 12.04.) after disabling ASLR, canaries, aligning the stack boundary, and disabling the nx bit (with the proper code; eg, from Hacking: The Art of Exploitation). But I assume also on your system. Note that the gcc -z execstack option doesn't work for me: I needed to install execstack from the repositories, and call it separately after compilation.Petrous

© 2022 - 2024 — McMap. All rights reserved.