Вопрос или проблема
В данный момент я пытаюсь выполнить атаку return-to-libc против локально запущенной программы. Вот шаги, которые я предпринял:
Я вычислил количество байт, необходимое для перезаписи сохранённого адреса возврата.
Я использовал переполнение буфера, чтобы перезаписать сохранённый адрес возврата адресом гаджета “pop rdi ; ret”, чтобы иметь возможность вызвать функцию system() с аргументом строку “/bin/sh” (программа 64-битная).
После перезаписанного адреса возврата я поместил адрес строки “/bin/sh”, чтобы команда “pop rdi” извлекла адрес “/bin/sh” в регистр rdi.
После этого я поместил адрес функции system в libc (ASLR выключен, поэтому адрес статический).
В качестве резюме, стек выглядит так:
padding|address_of_pop_rdi|address_of_binsh|address_of_system_in_libc
Все адреса преобразованы с помощью метода p64() библиотеки python pwntools.
Каждый раз, когда я запускаю эксплойт, программа аварийно завершает работу с SIGSEGV (сегментационная ошибка), что приводит меня к мысли, что длина заполнения некорректна, но вот в чём странность:
Когда я прошёл через эксплойт с помощью GDB, я вижу, что сохранённый rip установлен правильно на адрес гаджета pop_rdi. Когда я продолжаю дальше, я вижу, что адрес “/bin/sh” корректно попадает в регистр RDI. Когда я продолжаю дальше, я вижу, что функция system выполняется. Независимо от того, сколько раз я продолжаю, программа никогда не завершается с SIGSEGV. Выполнение просто останавливается в какой-то момент, что, я думаю, связано с тем, что программа ожидает ввода команды оболочки.
Встречался ли кто-нибудь с этой проблемой? Может быть, включена какая-то защита, которая автоматически завершает программу из-за обнаружения эксплойта? В бинарном файле нет канареек, нет PIE, но работает NX.
Я буду признателен за любую помощь, которую смогу получить, потому что я совершенно потерян.
Я могу предоставить скриншоты при необходимости.
Заранее спасибо!
Существует большая вероятность, что эта авария вызвана инструкцией movaps внутри функции system. movaps требует, чтобы стек был выровнен на 16-байт. И когда вы переходите к функции system, стек может быть неправильно выровнен.
ссылка из Why movaps causes segmentation fault?
Из руководства разработчика программного обеспечения по архитектурам Intel® 64 и IA-32, спецификация MOVAPS:
MOVAPS—Перемещение выровненных упакованных однопоточных значений с плавающей точкой
…
Когда исходный или целевой операнд является операндом памяти, операнд должен быть выровнен на границе 16 байт (128-битная версия), 32 байта (версия VEX.256) или 64 байта (версия EVEX.512), иначе будет сгенерировано исключение общей защиты (#GP).
Вы можете выяснить это, исследуя точку сбоя и код выхода с помощью gdb.
Чтобы решить эту проблему, вы можете добавить гаджет для корректировки стека или просто пропустить первую инструкцию push rsp функции system (учтите: если вы сделаете это таким образом, процесс аварийно завершится при возврате функции system, поскольку стек больше не будет сбалансирован).
Ответ или решение
Судя по вашему описанию, ваша проблема с атакой return-to-libc может действительно быть связана с неправильным выравниванием стека перед вызовом функции system()
. Как вы правильно заметили, инструкция movaps
требует, чтобы стек был выровнен по 16-байтовой границе. Если выровненности нет, это может привести к ошибке защиты и аварийному завершению программы с кодом сегментации (SIGSEGV).
Возможные причины проблемы:
-
Неверное выравнивание стека: После перезаписи адреса возврата, когда управление передается в ваш гаджет
pop rdi ; ret
, следует убедиться, что при переходе к функцииsystem()
стек действительно выровнен. Если вы используетеpop rdi ; ret
, это может изменить стек по сравнению с оригинальным состоянием, что может привести к нарушению выравнивания. -
Отсутствие переменных для сохранения состояния: Убедитесь, что перед использованием
pop rdi ; ret
вы не изменяете выравнивание стека каким-либо другим способом, и что вы корректно обрабатываете запланированные обходы в стеке.
Решения:
-
Корректировка выравнивания стека: Добавьте дополнительный гаджет, который выравнивает стек. Вы можете использовать, например,
add rsp, <number> ; ret
, где<number>
– это значение, которое приведет стек в нужное выравнивание.Пример:
add rsp, 8 ; выравнивание стека по 16 байт
Замените
<number>
подходящим значением, чтобы получить нужное выравнивание. -
Модификация кода: Модифицируйте свой код так, чтобы вы могли контролировать выравнивание. Для этого можно использовать отдельный гаджет, чтобы сделать необходимое количество операций
add
илиsub
. -
Исключение первых инструкций: Как вы упомянули, вы также можете попытаться пропустить первую инструкцию
push rsp
вsystem()
, чтобы избежать проблем с выравниванием, но это решение может привести к дальнейшим ошибкам в будущем, если фон будет обрабатываться неправильно.
Проверка состояния стека:
В GDB вы можете проверить текущее значение регистра rsp
перед вызовом функции system()
. Запустите GDB и используйте следующую команду, чтобы проверить выравнивание:
info registers rsp
Убедитесь, что значение rsp
не делится на 16. Если нет, внесите изменения в свой код или добавьте необходимый гаджет для исправления выравнивания.
Заключение:
Ваше наблюдение о том, что программа работает в GDB и не вызывает SIGSEGV, но завершается иначе, может указывать на проблемы с выровнением стека. Попробуйте внести изменения в вашу эксплуатационную нагрузку для решения проблемы с выравниванием или добавления дополнительной логики для проверки величины rsp
. Надеюсь, эти рекомендации помогут вам успешно завершить вашу атаку return-to-libc. Если у вас есть дополнительные вопросы или вам нужна помощь с конкретными аспектами, пожалуйста, дайте знать!