Стековое переполнение буфера в R 3.4.4. Полная эксплуатация на x86, но на x64 — только контроль RIP с анализом гаджетов из-за ограничений программы. Одна и та же уязвимость в двух архитектурах, приводящая к разным путям эксплуатации.
Переполнение буфера на основе стека в R 3.4.4. Полная эксплуатация на x86, но на x64 — только контроль над RIP с анализом гаджетов из-за ограничений программы. Одна и та же уязвимость на двух архитектурах приводит к разным путям эксплуатации.
Этот репозиторий — часть материалов, которые я использую при преподавании эксплуатации повреждения памяти (помимо основной работы я также преподаю на различных курсах по кибербезопасности, где помогаю обучать следующее поколение реверс-инженеров).
CVE-2019-25485 — это случай, который я использую, когда хочу, чтобы студенты проработали одну и ту же уязвимость на двух разных архитектурах и воочию увидели, что между ними меняется. R 3.4.4 поставляется в версиях x86 и x64, и ровно одно и то же переполнение существует в обеих: то же поле GUI, тот же обработчик ввода, тот же крах. Обе версии задокументированы и эксплуатируются здесь как отдельные упражнения:
R 3.4.4 — это приложение для статистических вычислений, а не сетевой сервис или браузер. Переполнение срабатывает через поле графического интерфейса рабочего стола, а значит, поверхность атаки полностью отличается от всех остальных случаев, которые я преподаю. Что делает этот случай полезным для обучения:
R — это среда для статистических вычислений и построения графиков, доступная для Windows, macOS и Linux. Уязвимость находится в диалоговом окне GUI Preferences, а именно в поле Language for menus and messages, которое копирует пользовательский ввод в буфер фиксированного размера на стеке, не проверяя его длину.
Ключевые технические детали:
R 3.4.4 обрабатывает поле Language for menus and messages, копируя переданную строку в буфер фиксированного размера на стеке без проверки длины. Упрощённая версия уязвимой логики выглядит так:
char language_buffer[256];
strcpy(language_buffer, user_input);
Отправка достаточно длинной строки заставляет операцию копирования выйти за конец буфера, повреждая стек до тех пор, пока не будет перезаписан сохранённый обратный адрес. Когда функция возвращается, CPU загружает управляемое атакующим значение из стека в RIP и пытается выполнить переход на него.
На x64 Windows применяет проверку канонических адресов перед любым переходом. Неканоническое значение, такое как 0x4141414141414141, вызывает немедленное нарушение доступа ещё до загрузки RIP, а это значит, что крах выглядит иначе, чем на x86, — без явного значения RIP = 4141414141414141. Смещение приходится находить, читая циклический паттерн из стека после краха, а не из самого RIP.
Крах можно воспроизвести, вставив длинную строку в языковое поле. Аутентификация не требуется. Пример генерации полезной нагрузки с помощью Python:
import struct
payload = b'A' * 400
with open('payload.txt', 'wb') as f:
f.write(payload)
Open R 3.4.4 x64
Edit -> GUI Preferences
Paste contents of payload.txt into Language for menus and messages
Click OK
Цель этого репозитория — не только продемонстрировать крах, но и провести полный процесс эксплуатации на обеих архитектурах, документируя, что работает на x86, что ломается на x64 и, что более важно, почему.
Чтобы основной README оставался чистым, подробные заметки по эксплуатации, скрипты и шаги отладчика размещены в папке Vulnerability 📂 этого репозитория и разбиты на отдельные подпапки для x86 и x64.
Там вы найдёте полный рабочий процесс для обеих архитектур:
x86 - Полная эксплуатация:
x64 - Контроль над RIP и анализ эксплуатации: