Как написать эксплойт с переполнением буфера в GCC, Windows XP, x86? - PullRequest
8 голосов
/ 30 марта 2010
void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   ret = buffer1 + 12;
   (*ret) += 8;//why is it 8??
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}

Вышеприведенная демоверсия отсюда:

http://insecure.org/stf/smashstack.html

Но здесь это не работает:

D:\test>gcc -Wall -Wextra hw.cpp && a.exe
hw.cpp: In function `void function(int, int, int)':
hw.cpp:6: warning: unused variable 'buffer2'
hw.cpp: At global scope:
hw.cpp:4: warning: unused parameter 'a'
hw.cpp:4: warning: unused parameter 'b'
hw.cpp:4: warning: unused parameter 'c'
1

И я не понимаю, почему это 8, хотя автор думает:

Маленькая математика говорит нам, что расстояние 8 байтов.

Мой дамп GDB как называется:

Dump of assembler code for function main:
0x004012ee <main+0>:    push   %ebp
0x004012ef <main+1>:    mov    %esp,%ebp
0x004012f1 <main+3>:    sub    $0x18,%esp
0x004012f4 <main+6>:    and    $0xfffffff0,%esp
0x004012f7 <main+9>:    mov    $0x0,%eax
0x004012fc <main+14>:   add    $0xf,%eax
0x004012ff <main+17>:   add    $0xf,%eax
0x00401302 <main+20>:   shr    $0x4,%eax
0x00401305 <main+23>:   shl    $0x4,%eax
0x00401308 <main+26>:   mov    %eax,0xfffffff8(%ebp)
0x0040130b <main+29>:   mov    0xfffffff8(%ebp),%eax
0x0040130e <main+32>:   call   0x401b00 <_alloca>
0x00401313 <main+37>:   call   0x4017b0 <__main>
0x00401318 <main+42>:   movl   $0x0,0xfffffffc(%ebp)
0x0040131f <main+49>:   movl   $0x3,0x8(%esp)
0x00401327 <main+57>:   movl   $0x2,0x4(%esp)
0x0040132f <main+65>:   movl   $0x1,(%esp)
0x00401336 <main+72>:   call   0x4012d0 <function>
0x0040133b <main+77>:   movl   $0x1,0xfffffffc(%ebp)
0x00401342 <main+84>:   mov    0xfffffffc(%ebp),%eax
0x00401345 <main+87>:   mov    %eax,0x4(%esp)
0x00401349 <main+91>:   movl   $0x403000,(%esp)
0x00401350 <main+98>:   call   0x401b60 <printf>
0x00401355 <main+103>:  leave
0x00401356 <main+104>:  ret
0x00401357 <main+105>:  nop
0x00401358 <main+106>:  add    %al,(%eax)
0x0040135a <main+108>:  add    %al,(%eax)
0x0040135c <main+110>:  add    %al,(%eax)
0x0040135e <main+112>:  add    %al,(%eax)
End of assembler dump.

Dump of assembler code for function function:
0x004012d0 <function+0>:        push   %ebp
0x004012d1 <function+1>:        mov    %esp,%ebp
0x004012d3 <function+3>:        sub    $0x38,%esp
0x004012d6 <function+6>:        lea    0xffffffe8(%ebp),%eax
0x004012d9 <function+9>:        add    $0xc,%eax
0x004012dc <function+12>:       mov    %eax,0xffffffd4(%ebp)
0x004012df <function+15>:       mov    0xffffffd4(%ebp),%edx
0x004012e2 <function+18>:       mov    0xffffffd4(%ebp),%eax
0x004012e5 <function+21>:       movzbl (%eax),%eax
0x004012e8 <function+24>:       add    $0x5,%al
0x004012ea <function+26>:       mov    %al,(%edx)
0x004012ec <function+28>:       leave
0x004012ed <function+29>:       ret

В моем случае расстояние должно быть - = 5, верно? Но, похоже, оно не работает ..

Почему function требуется 56 байтов для локальных переменных? (sub $0x38,%esp)

Ответы [ 6 ]

2 голосов
/ 31 марта 2010

Как указал joveha , значение EIP, сохраненное в стеке (адрес возврата) с помощью инструкции call, необходимо увеличить на 7 байт (0x00401342 - 0x0040133b = 7 ), чтобы пропустить инструкцию x = 1; (movl $0x1,0xfffffffc(%ebp)).

Вы правы, что 56 байтов зарезервированы для локальных переменных (sub $0x38,%esp), поэтому пропущенный фрагмент - это количество байтов после buffer1 в стеке - это сохраненный EIP.


Немного кода теста и встроенной сборки говорит мне, что магическое значение для моего теста 28 . Я не могу дать однозначного ответа относительно того, почему это 28, но я бы предположил, что компилятор добавляет канареки для заполнения и / или стека канарей .

Следующий код был скомпилирован с использованием GCC 3.4.5 (MinGW) и протестирован в Windows XP SP3 (x86).


unsigned long get_ebp() {
   __asm__("pop %ebp\n\t"
           "movl %ebp,%eax\n\t"
           "push %ebp\n\t");
}

void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   /* distance in bytes from buffer1 to return address on the stack */
   printf("test %d\n", ((get_ebp() + 4) - (unsigned long)&buffer1));

   ret = (int *)(buffer1 + 28);

   (*ret) += 7;
}

void main() {
   int x;

   x = 0;
   function(1,2,3);
   x = 1;
   printf("%d\n",x);
}

Я мог бы так же легко использовать GDB для определения этого значения.

(скомпилировано с / 1032 * для включения символов отладки)

(gdb) break function
...
(gdb) run
...
(gdb) p $ebp
$1 = (void *) 0x22ff28
(gdb) p &buffer1
$2 = (char (*)[5]) 0x22ff10
(gdb) quit

(0x22ff28 + 4) - 0x22ff10 = 28

(значение ebp + размер слова) - адрес buffer1 = количество байтов


В дополнение к Smashing The Stack для развлечения и прибыли , я бы предложил прочитать некоторые из статей, которые я упомянул в , мой ответ на предыдущий вопрос о вас и / или другие материалы на предмет. Хорошее понимание того, как именно работает этот тип эксплойта, должно помочь вам написать более безопасный код .

2 голосов
/ 30 марта 2010

Трудно предсказать, на что действительно указывает buffer1 + 12. Ваш компилятор может поместить buffer1 и buffer2 в любое место в стеке, которое он желает, даже если не будет экономить место для buffer2. Единственный способ по-настоящему узнать, куда идет buffer1, - это посмотреть на вывод ассемблера вашего компилятора, и есть большая вероятность, что он будет прыгать с разными настройками оптимизации или разными версиями одного и того же компилятора.

1 голос
/ 30 марта 2010

Часть +8 байт зависит от того, насколько он хочет, чтобы сохраненный EIP увеличивался на. EIP был сохранен, чтобы программа могла вернуться к последнему назначению после выполнения function - теперь он хочет пропустить его, добавив 8 байтов к сохраненному EIP.

Так что все, что он пытается, это "пропустить"

x = 1;

В вашем случае сохраненный EIP будет указывать на 0x0040133b, возвращается первая инструкция после function. Чтобы пропустить назначение, вам нужно указать в сохраненном EIP значение 0x00401342. Это 7 байтов.

Это действительно "беспорядок с RET EIP", а не пример переполнения буфера.

А что касается 56 байтов для локальных переменных, это может быть что угодно, что придумывает ваш компилятор, например, padding, стековые канареи и т.д.

Edit:

Это показывает, как трудно создавать примеры переполнения буфера в C. Смещение 12 от buffer1 предполагает определенный стиль заполнения и параметры компиляции. В настоящее время GCC с радостью вставит канареек стека (которая становится локальной переменной, которая «защищает» сохраненный EIP), если вы не запретите это. Кроме того, новый адрес, к которому он хочет перейти (инструкция запуска для вызова printf), действительно должен быть разрешен вручную из сборки. В его случае, на его машине, с его ОС, с его компилятором, в тот день ... это было 8.

1 голос
/ 30 марта 2010

Этот код также печатает 1 в OpenBSD и FreeBSD и выдает ошибку сегментации в Linux.

Этот вид эксплойта сильно зависит как от набора команд конкретной машины, так и от соглашений о вызовах компилятора и операционной системы. Все, что касается компоновки стека, определяется реализацией, а не языком Си. В статье предполагается, что Linux на x86, но похоже, что вы используете Windows, и ваша система может быть 64-битной, хотя вы можете переключить gcc на 32-битную с помощью -m32.

Параметры, которые вы должны будете настроить: 12, это смещение от вершины стека до адреса возврата, и 8, то есть, сколько байтов main вы хотите перепрыгнуть. Как говорится в статье, вы можете использовать gdb для проверки дизассемблирования функции, чтобы увидеть (а) как далеко толкается стек при вызове function, и (b) смещение байтов инструкций в main.

1 голос
/ 30 марта 2010

Я еще не тестировал код на своем компьютере, но учли ли вы выравнивание памяти? Попробуйте разобрать код с помощью gcc. Я думаю, что ассемблерный код может дать вам дальнейшее понимание кода. : -)

0 голосов
/ 30 марта 2010

Вы компилируете программу на C с помощью компилятора C ++. Переименуйте hw.cpp в hw.c, и вы обнаружите, что он скомпилируется.

...