Пример переполнения буфера, работающий в 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
,