Пример переполнения буфера, работающий в Windows, но не в Linux

В книге, которую я читаю, Software Exorcism, приведен пример кода для переполнения буфера:

#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;
}

Я могу заставить это работать в Windows с Visual Studio 2010, указав

  • Базовые проверки времени выполнения -> Неинициализированные переменные
  • Проверка безопасности буфера -> Нет

С этими параметрами компиляции я получаю следующее поведение:

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

Мой вопрос о коде, не работающем в Linux. Есть ли четкая причина, почему это так?

Немного информации о том, что я пробовал:

Я пытался запустить это с 32-битной Ubuntu 12.04 (скачано здесь), с этими параметрами:

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

Получение:

[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)

И с 64-битным CentOS 6.0, с этими опциями:

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

Получение:

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

Есть ли что-то принципиально иное в среде Linux, что может привести к тому, что пример не будет работать, или я упустил что-то простое здесь?

Примечание: я перебрал связанные вопросы, такие как этот и этот, но не смог найти ничего, что могло бы помочь в этом. Я не думаю, что это дубликат предыдущих вопросов, хотя их много.

2 ответа

Решение

Ваш пример переполняет стек, небольшую и предсказуемую структуру памяти, пытаясь изменить адрес возврата функции void victim(), который затем будет указывать на void redirected() вместо того, чтобы вернуться к main(),

Работает с Visual. Но GCC - это другой компилятор, который может использовать другое правило распределения стека, что делает эксплойт неудачным. C не требует строгой "компоновки памяти стека", поэтому компиляторы могут делать разные варианты.

Хороший способ проверить эту гипотезу - протестировать свой код с помощью MinGW (он же GCC для Windows), доказав, что разница в поведении не связана строго с ОС.

#define BUFFER_SIZE 4

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

Здесь есть еще одна потенциальная проблема, если оптимизация включена. buffer 12 байт, и его называют victim(buffer), Тогда внутри victimвы пытаетесь скопировать 12 байтов в 4-байтовый буфер с strcpy,

FORTIFY_SOURCES должен вызвать сбой программы при вызове strcpy, Если компилятор может определить размер буфера назначения (который он должен в этом случае), то компилятор заменит strcpy с "более безопасной" версией, которая включает размер буфера назначения. Если количество копируемых байтов превышает размер целевого буфера, тогда "безопаснее" strcpy позвоню abort(),

Чтобы отключить FORTIFY_SOURCES, затем скомпилировать с -U_FORTIFY_SOURCE или же -D_FORTIFY_SOURCE=0,

Другие вопросы по тегам