странные адреса в статье переполнения буфера стека - PullRequest
0 голосов
/ 23 сентября 2018

читая эту статью о обходе защиты

если вы начнете читать эту статью, вы обнаружите, что автор сделал действительно странный расчет, который я не понял:

хорошо, давайте начнем:

RET2RET

демонстрационный код (содержащий уязвимую функцию strcpy)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv) {
     char buf[256];
     strcpy(buf, argv[1]);
     return 1;
}

после разборки кода выше с помощью GDB вы получите что-то вроде этого (разборка главной функции):

(gdb) disassemble main
Dump of assembler code for function main:
0x08048384 <main+0>: push %ebp
0x08048385 <main+1>: mov %esp,%ebp
0x08048387 <main+3>: sub $0x108,%esp
0x0804838d <main+9>: and $0xfffffff0,%esp
0x08048390 <main+12>: mov $0x0,%eax
0x08048395 <main+17>: sub %eax,%esp
0x08048397 <main+19>: sub $0x8,%esp
0x0804839a <main+22>: mov 0xc(%ebp),%eax
0x0804839d <main+25>: add $0x4,%eax
0x080483a0 <main+28>: pushl (%eax)
0x080483a2 <main+30>: lea 0xfffffef8(%ebp),%eax
0x080483a8 <main+36>: push %eax
0x080483a9 <main+37>: call 0x80482b0 <_init+56>
0x080483ae <main+42>: add $0x10,%esp
0x080483b1 <main+45>: mov $0x1,%eax
0x080483b6 <main+50>: leave
0x080483b7 <main+51>: ret
End of assembler dump.
(gdb)

после этого мы установим точку останова на strcpy, потому что это уязвимая функция:

(gdb) break *main+37
Breakpoint 1 at 0x80483a9

теперь мы запускаем нашу программу и даем ей большой массив A char для переполнения буфера

(gdb) run `perl -e 'print "A"x272'`
Starting program: /tmp/vuln `perl -e 'print "A"x272'`

, после этого мы печатаем содержимое регистра eax (я думаю, что eax удерживая buf адрес, поправьте меня, если я ошибся )

(gdb) print (void *) $eax
$1 = (void *) 0xbffff5d0

Окей, после этого автор статьи произнесет это утверждение ниже

Простой расчет дает вариацию «buf»весь диапазон [0xbffff6d8 - 0xbffff5d0] / (264 bytes;0x108h)

, который я не мог понять, что это значит, а также откуда он пришел с этим 0xbffff6d8 адресом

Ответы [ 2 ]

0 голосов
/ 23 сентября 2018

Основная идея эксплойтов ret2ret состоит в том, чтобы найти значение в стеке, которое можно слегка изменить, чтобы оно указывало на буфер, который можно переполнить.Выполнив столько ret инструкций, сколько необходимо для получения этого значения, вы сможете в конечном итоге выполнить код в буфере (эксплойт).

Стек из примера непосредственно перед выполнением call 0x80482b0 <_init+56> выглядитвот так:

--------------------
|    0xbffff6f0    | ebp+28
--------------------
|    0xb7fdb000    | ebp+24
--------------------
|    0xb800167c    | ebp+20
--------------------
|    0xbffff770    | ebp+16 (previous stack frame)
--------------------
|    0xbffff764    | ebp+12 (points to the argv array)
--------------------
|       0x2        | ebp+8  (holds the value of argc)
--------------------
|   main ret addr  | ebp+4
--------------------
|previous ebp value| ebp
--------------------
|   appears to be  |
|      unused      | ebp-8
--------------------
|                  |
|     256-byte     |
|       buf        |
|                  |
|                  |
|                  | ebp-264
--------------------
|                  | ebp-268 (holds the second argument to strcpy)
--------------------
|                  | ebp-272 = esp (holds the first argument to strcpy)
--------------------

Итак, как выполнить успешную атаку ret2ret на этот стек?Мы знаем, что последняя инструкция в main это ret.Нам нужно найти способ выполнить эту инструкцию несколько раз, чтобы в конечном итоге выполнялся любой код, который мы решили вставить в buf.Помните, что каждый раз, когда ret выполняется в 32-битном режиме x86, он извлекает 4-байтовое значение из стека.Итак, первое, что нам нужно выяснить - это сколько 4-байтовых значений нужно извлечь из стека, чтобы получить значение, которое представляет указатель на buf.Автор статьи показывает, что первое такое значение находится на ebp+28.Диапазон 0xbffff6d8 - 0xbffff5d0 содержит 256-байтовый буфер.Однако значение в ebp+28 равно 0xbffff6f0, что на самом деле находится за пределами буфера.Но поскольку strcpy добавит в конец байт NULL, мы можем сделать так, чтобы он перезаписывал первый байт ebp+28 на NULL, чтобы он содержал 0xbffff600, который указывает внутри буфера.Начиная с ebp+4, существует 7 4-байтовых значений.Таким образом, нам нужно выполнить ret 7 раз, а затем выполнение будет продолжаться где-то в buf.

. Для достижения этого значение в ebp+4 должно быть установлено на адрес retв main, что составляет 0x080483b7.Точно так же все 4-байтовые расположения вплоть до ebp+24 должны быть установлены в 0x080483b7.Таким образом, первые 6 выполнений ret просто перейдут на ret, чтобы выполняться снова и снова.Но в последний раз при выполнении ret управление будет передано в 0xbffff6f0, который находится в буфере, который будет перезаписан.

Теперь рассмотрим, что происходит, когда evilbuf из кода эксплойта передается как argv[1].Буфер содержит 261 однобайтовых инструкций NOP.Следующие 7 байтов являются инструкциями, которые выполняют системный вызов exit (хотя реальный эксплойт мог бы сделать что-то более интересное).Эти 268 байтов перезапишут все байты от ebp-264 до ebp+3.Тогда есть 6 копий адреса ret.В конце, strcpy щедро добавит для нас NULL, перезаписав таким образом байт в ebp+28.

После того, как strcpy вернется и ret в main будет выполнен впервые,оно будет выполнено еще 6 раз, а затем будет выполнена инструкция по адресу 0xbffff600.Это один из наших НОПов.Тогда остальные сани NOP будут выполняться, пока не достигнет заданной последовательности инструкций (полезной нагрузки), которая в этом случае просто выполняет системный вызов выхода.

Ret2ret не требует предварительногознание адреса буфера.Следовательно, он может работать, даже если ОС рандомизирует базовый адрес стека.Но для этого нужно знать адрес инструкции ret.

0 голосов
/ 23 сентября 2018

поправьте меня, если я ошибся

Сначала вы правы насчет eax, в котором содержится адрес buf (есть много способов).Чтобы быть уверенным в этом: первое и самое простое - это то, что args функций, помещаются в стек в обратном порядке, поэтому последний из них будет первым args, как вы можете видеть из окна разборки вышепоследний перед вызовом функции - push eax, поэтому значение в eax должно быть адресом buf.

, который я не мог понять, что это значит, а также откуда он пришелс этим 0xbffff6d8 адресом

0xbffff6d8 это значение ebp и идея 0xbffff6d8 - 0xbffff5d0 является одним из многих способов узнать ваш максимальный размер буфера до того, как он начнет генерировать segfault

Еще один способ узнать эту информацию без выполнения этих вычислений - из окна разборки над 0x08048387 <main+3>: sub $0x108,%esp, которое выделяет место для локального buf массива, но это не так во всех случаях: зависит от количествалокальные переменные (если есть одна локальная переменная / массив, это будет работать каждый раз, в противном случае вы должны быть умны в использовании этого метода), но метод автора работает каждыйвремя.

...