2014-09-02 3 views
2

В книге я читал, Software Exorcism, имеет этот пример кода для переполнения буфера:Buffer пример переполнения работает на Windows, но не на Linux

#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, указав

  • Basic времени выполнения проверки -> Неинициализированные переменные
  • Buffer Security Check -> Нет

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

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

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

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

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

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

Получение:

[09/01/2014 12:03] [email protected]:~$ gcc -fno-stack-protector -z execstack -o overflow overflow.c 
[09/01/2014 12:03] [email protected]:~$ ./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, которые могли бы привести пример не работает, или я что-то отсутствует простой здесь?

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

+0

Если это работает на Linux, а не на Windows или наоборот, то это означает, что большую часть времени он «работает» только случайно. –

+1

Переполнение буфера вызывает неопределенное поведение, а это значит, что он может что-то сделать. Включая работу, как будто переполнение не произошло (хотя вы, вероятно, развращаете какую-то другую информацию). –

+0

@MichaelWalz, возможно, но в такой среде большинство ответов, которые я видел, связаны с изменением конфигурации среды, а не с кодом. Кроме того, поскольку это пример книги, а не что-то сделанное мной, я бы не решался утверждать, что он работает только случайно. По крайней мере, если он работает только случайно, мне было бы интересно узнать, почему это так. – eis

ответ

4

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

Он работает с визуальным. Но GCC - это другой компилятор и может использовать какое-то другое правило распределения стека, заставляя эксплоит сбой. C не применяет строгий «формат памяти стека», поэтому компиляторы могут делать разные варианты.

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

+0

Действительно, протестирован с другим компилятором (Cygwins gcc), и эта вещь больше не работает, поэтому, похоже, не связана операционная система. – eis

0
#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.

+0

не приводил к тому, что код работал на 64-битном centos + gcc как минимум. У меня будет еще один тест с 32-битным ubuntu. – eis

+0

@eis - вышеизложенное может объяснить «Ошибка сегментации (сбрасывание ядра)» на Ubuntu. Извините, я не был чист. Кроме того, неясно, что переполнение переписывается. Если его обратный адрес в стеке, то 64-разрядные операционные системы часто используют реестр ссылок (я полагаю, R-13), поэтому обратные адреса на основе стека не могут быть перезаписаны :) Его одна из причин, чтобы предпочесть 64-битную над 32 -немного. – jww

+0

Да, он предназначен для перезаписи адреса возврата в стеке. Хорошо, это объясняет проблему с 64-разрядными исполняемыми файлами. – eis

Смежные вопросы