2

In the book I am reading, Software Exorcism, has this example code for a buffer overflow:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 4

void victim(char *str)
{
        char buffer[BUFFER_SIZE];
        strcpy(buffer,str);
        return;
}

void redirected()
{
        printf("\tYou've been redirected!\n");
        exit(0);
        return;
}

void main()
{
        char buffer[]=
        {
                '1','2','3','4',
                '5','6','7','8',
                '\x0','\x0','\x0','\x0','\x0'
        };
        void *fptr;
        unsigned long *lptr;

        printf("buffer = %s\n", buffer);
        fptr = redirected;
        lptr = (unsigned long*)(&buffer[8]);
        *lptr = (unsigned long)fptr;

        printf("main()\n");
        victim(buffer);
        printf("main()\n");
        return;
}

I can get this to work in Windows with Visual Studio 2010 by specifying

  • Basic Runtime Checks -> Uninitialized variables
  • Buffer Security Check -> No

With those compile options, I get this behavior when running:

buffer = 12345678
main()
        You've been redirected!

My question is about the code not working on Linux. Is there any clear reason why it is so?

Some info on what I've tried:

I've tried to run this with 32-bit Ubuntu 12.04 (downloaded from here), with these options:

[09/01/2014 11:46] root@ubuntu:/home/seed# sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0

Getting:

[09/01/2014 12:03] seed@ubuntu:~$ gcc -fno-stack-protector -z execstack -o overflow overflow.c 
[09/01/2014 12:03] seed@ubuntu:~$ ./overflow
buffer = 12345678
main()
main()
Segmentation fault (core dumped)

And with 64-bit CentOS 6.0, with these options:

[root]# sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0
[root]# sysctl -w kernel.exec-shield=0
kernel.exec-shield = 0

Getting:

[root]# gcc -fno-stack-protector -z execstack -o overflow overflow.c 
[root]# ./overflow
buffer = 12345678
main()
main()
[root]#

Is there something fundamentally different in Linux environment, which would cause the example not working, or am I missing something simple here?

Note: I've been through the related questions such as this one and this one, but haven't been able to find anything that would help on this. I don't think this is a duplicate of previous questions even though there are a lot of them.

Community
  • 1
  • 1
eis
  • 51,991
  • 13
  • 150
  • 199
  • If it works on Linux and not on Windows or vice versa, then that means most of the time that it "works" only by chance. – Jabberwocky Sep 02 '14 at 16:02
  • 1
    Buffer overflows cause undefined behavior, which means it could do anything. Including work as if the the overflow didn't happen (though you're probably corrupting some other information). – Colonel Thirty Two Sep 02 '14 at 16:04
  • @MichaelWalz possibly, but in this kind of environment most answers I've seen are related to some environment configuration change, not to code itself. Also, since this is a book example and not something made up by me, I would be hesitant to claim that it only works by chance. At least if it only works by chance I would be interested to know why that is so. – eis Sep 02 '14 at 16:05
  • 2
    Different operating systems may use different ABIs, especially the bits that specify what arguments are passed/returned on the stack vs. in registers, and the exact layout of the typical stack frame. So, you may not actually be overwriting the return address as you expect, but something else entirely... – twalberg Sep 02 '14 at 16:12
  • Use the compiler flag (`-S` with gcc, `/Fa` with msvc) to generate an assembly listing to see the code that the compiler is generating for these functions. You'll likely see that `gcc` generates a slightly larger or smaller stack frame, which results in the overflow not correctly overwriting the return address. Based on that, you can probably modify the code to 'work' on `gcc`. – Chris Dodd Sep 02 '14 at 17:27

2 Answers2

4

Your example overflows the stack, a small and predictable memory layout, in an attempt to modify the return address of the function void victim(), which would then point to void redirected() instead of coming back to main().

It works with Visual. But GCC is a different compiler, and can use some different stack allocation rule, making the exploit fail. C doesn't enforce a strict "stack memory layout", so compilers can make different choices.

A good way to view this hypothesis is to test your code using MinGW (aka GCC for Windows), proving the behavior difference is not related strictly to the OS.

Cyan
  • 13,248
  • 8
  • 43
  • 78
  • Indeed, tested with another compiler (Cygwins gcc) and the thing no longer works, so doesn't seem to be operating system bound. – eis Sep 02 '14 at 16:26
0
#define BUFFER_SIZE 4

void victim(char *str)
{
        char buffer[BUFFER_SIZE];
        strcpy(buffer,str);
        return;
}

There is another potential problem here if optimizations are enabled. buffer is 12 bytes, and its called as victim(buffer). Then, within victim, you try to copy 12 bytes into a 4 byte buffer with strcpy.

FORTIFY_SOURCES should cause the program to seg fault on the call to strcpy. If the compiler can deduce the destination buffer size (which it should in this case), then the compiler will replace strcpy with a "safer" version that includes the destination buffer size. If the bytes to copy exceeds the destination buffer size, then the "safer" strcpy will call abort().

To turn off FORTIFY_SOURCES, then compile with -U_FORTIFY_SOURCE or -D_FORTIFY_SOURCE=0.

jww
  • 97,681
  • 90
  • 411
  • 885
  • didn't cause the code to work on 64-bit centos + gcc at least. I'll have another test with 32-bit ubuntu. – eis Sep 13 '14 at 07:29
  • @eis - The above might explain the `Segmentation fault (core dumped)` on Ubuntu. Sorry I was not clear. Also, its not clear to me what the overflow is overwriting. If its the return address on the stack, then 64-bit OSes often use a link register (R-13, I believe) so stack-based return addresses cannot be overwritten :) Its one of the reasons to prefer 64-bit over 32-bit. – jww Sep 13 '14 at 09:51
  • yes, it's meant to overwrite return address on the stack. Ok, that would explain the problem with 64-bit executables. – eis Sep 14 '14 at 14:33
  • did not change anything on Ubuntu either. Still getting Segmentation fault there. – eis Sep 16 '14 at 15:15