1
votes

I am trying to make the buffer exploitation example (example3.c from http://insecure.org/stf/smashstack.html) work on Debian Lenny 2.6 version. I know the gcc version and the OS version is different than the one used by Aleph One. I have disabled any stack protection mechanisms using -fno-stack-protector and sysctl -w kernel.randomize_va_space=0 arguments. To account for the differences in my setup and Aleph One's I introduced two parameters : offset1 -> Offset from buffer1 variable to the return address and offset2 -> how many bytes to jump to skip a statement. I tried to figure out these parameters by analyzing assembly code but was not successful. So, I wrote a shell script that basically runs the buffer overflow program with simultaneous values of offset1 and offset2 from (1-60). But much to my surprise I am still not able to break this program. It would be great if someone can guide me for the same. I have attached the code and assembly output for consideration. Sorry for the really long post :)

Thanks.


// Modified example3.c from Aleph One paper - Smashing the stack
void function(int a, int b, int c, int offset1, int offset2) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   ret = (int *)buffer1 + offset1;// how far is return address from buffer ?
   (*ret) += offset2; // modify the value of return address
}

int main(int argc, char* argv[]) {
  int x;
  x = 0;
  int offset1 = atoi(argv[1]);
  int offset2 = atoi(argv[2]);
  function(1,2,3, offset1, offset2);
  x = 1; // Goal is to skip this statement using buffer overflow
  printf("X : %d\n",x);
  return 0;
}

-----------------
// Execute the buffer overflow program with varying offsets
#!/bin/bash
for ((i=1; i<=60; i++))
do
   for ((j=1; j<=60; j++))
   do
    echo "`./test $i $j`"
   done
done

-- Assembler output
(gdb) disassemble main
Dump of assembler code for function main:
0x080483c2 <main+0>:    lea    0x4(%esp),%ecx
0x080483c6 <main+4>:    and    $0xfffffff0,%esp
0x080483c9 <main+7>:    pushl  -0x4(%ecx)
0x080483cc <main+10>:   push   %ebp
0x080483cd <main+11>:   mov    %esp,%ebp
0x080483cf <main+13>:   push   %ecx
0x080483d0 <main+14>:   sub    $0x24,%esp
0x080483d3 <main+17>:   movl   $0x0,-0x8(%ebp)
0x080483da <main+24>:   movl   $0x3,0x8(%esp)
0x080483e2 <main+32>:   movl   $0x2,0x4(%esp)
0x080483ea <main+40>:   movl   $0x1,(%esp)
0x080483f1 <main+47>:   call   0x80483a4 <function>
0x080483f6 <main+52>:   movl   $0x1,-0x8(%ebp)
0x080483fd <main+59>:   mov    -0x8(%ebp),%eax
0x08048400 <main+62>:   mov    %eax,0x4(%esp)
0x08048404 <main+66>:   movl   $0x80484e0,(%esp)
0x0804840b <main+73>:   call   0x80482d8 <printf@plt>
0x08048410 <main+78>:   mov    $0x0,%eax
0x08048415 <main+83>:   add    $0x24,%esp
0x08048418 <main+86>:   pop    %ecx
0x08048419 <main+87>:   pop    %ebp
0x0804841a <main+88>:   lea    -0x4(%ecx),%esp
0x0804841d <main+91>:   ret    
End of assembler dump.

(gdb) disassemble function
Dump of assembler code for function function:
0x080483a4 <function+0>:    push   %ebp
0x080483a5 <function+1>:    mov    %esp,%ebp
0x080483a7 <function+3>:    sub    $0x20,%esp
0x080483aa <function+6>:    lea    -0x9(%ebp),%eax
0x080483ad <function+9>:    add    $0x30,%eax
0x080483b0 <function+12>:   mov    %eax,-0x4(%ebp)
0x080483b3 <function+15>:   mov    -0x4(%ebp),%eax
0x080483b6 <function+18>:   mov    (%eax),%eax
0x080483b8 <function+20>:   lea    0x7(%eax),%edx
0x080483bb <function+23>:   mov    -0x4(%ebp),%eax
0x080483be <function+26>:   mov    %edx,(%eax)
0x080483c0 <function+28>:   leave  
0x080483c1 <function+29>:   ret    
End of assembler dump.
1

1 Answers

1
votes

The disassembly for function you provided seems to use hardcoded values of offset1 and offset2, contrary to your C code.

The address for ret should be calculated using byte/char offsets: ret = (int *)(buffer1 + offset1), otherwise you'll get hit by pointer math (especially in this case, when your buffer1 is not at a nice aligned offset from the return address).

offset1 should be equal to 0x9 + 0x4 (the offset used in lea + 4 bytes for the push %ebp). However, this can change unpredictably each time you compile - the stack layout might be different, the compiler might create some additional stack alignment, etc.

offset2 should be equal to 7 (the length of the instruction you're trying to skip).


Note that you're getting a little lucky here - the function uses the cdecl calling convention, which means the caller is responsible for removing arguments off the stack after returning from the function, which normally looks like this:

push arg3
push arg2
push arg1
call func
add esp, 0Ch ; remove as many bytes as were used by the pushed arguments

Your compiler chose to combine this correction with the one after printf, but it could also decide to do this after your function call. In this case the add esp, <number> instruction would be present between your return address and the instruction you want to skip - you can probably imagine that this would not end well.