Эксплойт Ret2libc не работает, но в GDB он выглядит корректным.

Вопрос или проблема

В данный момент я пытаюсь выполнить атаку 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).

Возможные причины проблемы:

  1. Неверное выравнивание стека: После перезаписи адреса возврата, когда управление передается в ваш гаджет pop rdi ; ret, следует убедиться, что при переходе к функции system() стек действительно выровнен. Если вы используете pop rdi ; ret, это может изменить стек по сравнению с оригинальным состоянием, что может привести к нарушению выравнивания.

  2. Отсутствие переменных для сохранения состояния: Убедитесь, что перед использованием pop rdi ; ret вы не изменяете выравнивание стека каким-либо другим способом, и что вы корректно обрабатываете запланированные обходы в стеке.

Решения:

  1. Корректировка выравнивания стека: Добавьте дополнительный гаджет, который выравнивает стек. Вы можете использовать, например, add rsp, <number> ; ret, где <number> – это значение, которое приведет стек в нужное выравнивание.

    Пример:

    add rsp, 8 ; выравнивание стека по 16 байт

    Замените <number> подходящим значением, чтобы получить нужное выравнивание.

  2. Модификация кода: Модифицируйте свой код так, чтобы вы могли контролировать выравнивание. Для этого можно использовать отдельный гаджет, чтобы сделать необходимое количество операций add или sub.

  3. Исключение первых инструкций: Как вы упомянули, вы также можете попытаться пропустить первую инструкцию push rsp в system(), чтобы избежать проблем с выравниванием, но это решение может привести к дальнейшим ошибкам в будущем, если фон будет обрабатываться неправильно.

Проверка состояния стека:

В GDB вы можете проверить текущее значение регистра rsp перед вызовом функции system(). Запустите GDB и используйте следующую команду, чтобы проверить выравнивание:

info registers rsp

Убедитесь, что значение rsp не делится на 16. Если нет, внесите изменения в свой код или добавьте необходимый гаджет для исправления выравнивания.

Заключение:

Ваше наблюдение о том, что программа работает в GDB и не вызывает SIGSEGV, но завершается иначе, может указывать на проблемы с выровнением стека. Попробуйте внести изменения в вашу эксплуатационную нагрузку для решения проблемы с выравниванием или добавления дополнительной логики для проверки величины rsp. Надеюсь, эти рекомендации помогут вам успешно завершить вашу атаку return-to-libc. Если у вас есть дополнительные вопросы или вам нужна помощь с конкретными аспектами, пожалуйста, дайте знать!

Оцените материал
Добавить комментарий

Капча загружается...