Search code examples
assemblybuffer-overflowgnu-assembler

Example assembly language program for stack buffer overflow


I am trying to write an example program to understand stack buffer overflow and i have the following program.

overflow.s:

    .section .data
    .section .text
    .globl _start
    _start:
          call sum
          movl %eax, %ebx
          movl $15, %ebx
          movl $1, %eax
          int $0x80

         .type sum, @function
    sum:
         pushl %ebp        # save the current base pointer
         movl %esp, %ebp   # store current stack pointer to %ebp
         subl $4, %esp     # inc the stack pointer by 4 bytes for local variable 
         movl $5, -8(%ebp) # store value 5 from 8 bytes of %ebp 4 bytes beyond stack pointer
         addl $5, -8(%ebp) # add 5 to the value store beyond of stack pointer 
         movl -8(%ebp), %eax # store the value in %eax
         movl %ebp, %esp
         popl %ebp
         ret

assemble and link the program:

   as -gstabs+ overflow.s -o oveflow.o
   ld overflow.o -o overflow
   ./overflow
   echo $?
   15 <============= the result

I expected either i get some garbage or segfault. but it seems to work as expected. So in the sum function when i increment the stack pointer by 4 bytes and when i storing the value 5 8 bytes from base pointer, I was expecting this is a simulation of overflow. Is the above program wrong to be used an example of stack buffer overflow. ?


Solution

  • Memory below %esp might be clobbered asynchronously (by a signal handler1), but the behaviour of your program doesn't depend on the values you read/write with addl $5, -8(%ebp) or movl -8(%ebp), %eax, in that 4 byte stack slot right below ESP.

    On Linux, it's not an error to touch memory below ESP, at least within the same page. Farther than that and it might segfault instead of growing the stack (for the main thread's stack) unless the stack pointer is moved first. (Thread stack will already be fully allocated, not grown on the fly. The initial process stack is special.)


    If you ran push in a loop (without a pop or anything else to balance it), the stack would grows until ESP decreased to point to an unmapped page beyond the point where the kernel will grow the stack for you. Then the next push (or call or whatever) would segfault, and we'd call that a stack overflow.


    A buffer overflow would be if you did sub $12, %esp to reserve space for int arr[3], but then wrote to int arr[5]: that would overwrite your return address so the eventual ret would jump to wherever the attacker wanted you to jump.

     # arg in EAX: how many array elements to store into arr[3]
     vulnerable_function:
        sub  $12, %esp
        mov  %esp, %ecx
    .Lloop:
        mov  %eax, (%ecx)
        add  $4, %ecx
        dec  %eax
        jnz  .Lloop
    
        add  $12, %esp
        ret
    

    Footnote 1: You haven't installed any signal handlers, so (on Linux) nothing can use stack memory asynchronously, and you have an unlimited below ESP.

    But this is a special case when you're writing a complete program and not using any libraries, normally you should just inline small functions if the extra instructions to reserve and release stack space have non-trivial cost.

    Also, debuggers can step on this space if you do print foo() in GDB or something to have it call a function while stopped at a breakpoint somewhere.