Ответ 1
Вам нужно достаточно данных, чтобы заполнить зарезервированную память для стека, где находится "буфер", затем больше, чтобы перезаписать указатель фрейма стека, а затем перезаписать обратный адрес с адресом target()
, а затем еще один адрес внутри target()
но не в самом начале функции - введите его так, чтобы старый стекный указатель кадра не был нажат на стек. Это приведет к запуску цели вместо правильного возврата из vulnerable()
, а затем снова запустите target()
, чтобы вы вернулись с target()
до main()
и, следовательно, выходите без ошибки сегментации.
Когда мы вводим уязвимость() в первый раз и собираемся помещать данные стека выглядит так:
-----------
| 24-bytes of local storage - 'buffer' lives here
-----------
| old stack frame pointer (from main) <-- EBP points here
-----------
| old EIP (address in main)
-----------
| 'input' argument for 'vulnerable'
-----------
| top of stack for main
-----------
| ... more stack here ...
Итак, начиная с адреса "буфера" нам нужно положить 24 байта мусора в пройдите мимо локального хранилища, зарезервированного в стеке, затем еще 4 байта, чтобы получить мимо старого указателя кадра стека, хранящегося в стеке, тогда мы находимся на где хранится старый EIP. Это указатель инструкции, что процессор слепо следит. Нам он нравится. Он собирается помочь нам раздавить эту программу. Мы перезаписываем значение старого EIP в стеке, которое в настоящее время указывает на адрес в main() с начальным адресом target(), который находится через gdb дизассемблировать команду:
(gdb) disas target
Dump of assembler code for function target:
0x08048424 <+0>: push %ebp
0x08048425 <+1>: mov %esp,%ebp
0x08048427 <+3>: sub $0x18,%esp
0x0804842a <+6>: movl $0x8048554,(%esp)
0x08048431 <+13>: call 0x8048354 <[email protected]>
0x08048436 <+18>: leave
0x08048437 <+19>: ret
End of assembler dump.
Адрес функции target() - 0x08048424. Так как система (по меньшей мере, моя система) немного инициализирована, мы сначала вводим эти значения с LSB так, чтобы x24, x84, x04 и x08.
Но это оставляет нам проблему, потому что, поскольку уязвимость() возвращает ее, она всплывает мусор, который мы кладем в стек из стека, и у нас остается стек это выглядит так, когда мы собираемся обработать в target() для первый раз:
-----------
| 'input' argument for 'vulnerable'
-----------
| top of stack for main
-----------
| ... more stack here ...
Итак, когда target() хочет вернуть, он не найдет обратный адрес на вершина его стека, как ожидалось, и поэтому будет иметь ошибку сегментации.
Итак, мы хотим принудительно ввести новое возвращаемое значение в верхнюю часть стека, прежде чем мы начать обработку в target(). Но какое значение выбрать? Мы не хотим толкать EBP, поскольку он содержит мусор. Запомнить? Мы засунули мусор в него, когда мы перезаписали "буфер". Поэтому вместо этого нажмите команду target() сразу после
push %ebp
(в этом случае адрес 0x08048425).
Это означает, что стек будет выглядеть так, когда target() готов к возврату в первый раз:
-----------
| address of mov %esp, %ebp instruction in target()
-----------
| top of stack for main
-----------
| ... more stack here ...
Итак, при первом возврате из target() EIP теперь укажет на вторую команду в target(), что означает, что во второй раз, когда мы обрабатываем target(), он имеет тот же стек, что и main() когда он обрабатывается. Верхняя часть стека - та же самая вершина стека для main(). Теперь стек выглядит так:
-----------
| top of stack for main
-----------
| ... more stack here ...
Итак, когда target() возвращает второй раз, у него есть хороший стек для возврата с поскольку он использует тот же стек, что и main(), и поэтому программа выходит из системы.
Итак, суммируем это, это 28-байты, за которыми следует адрес первой команды в target(), за которой следует адрес второй команды в target().
sys1:/usr2/home> ./buggy AAAAAAAAAABBBBBBBBBBCCCCCCCC$'\x24\x84\x04\x08'$'\x25\x84\x04\x08'
target
target
sys1:/usr2/home>
И так как вы только что успешно взломали что-то, в этот момент вам нужно кричать AAARRRGHHH YEEE HARDYS! ПОДГОТОВЬТЕСЬ СОБИРАТЬСЯ!