DEP, ASLR и CFI: Почему переполнение буфера ещё не означает взлом
• Мы уже говорили про ASLR и ROP, сегодня посмотрим, как разные механизмы защиты усложняют эксплуатацию ошибок памяти и почему одного из них недостаточно.
• DEP (Data Execution Prevention) запрещает выполнять код из страниц памяти, помеченных как неисполняемые, например из обычного стека или кучи.
• Записать туда данные из-за переполнения всё ещё может получиться, но попытка выполнить их как код вызовет исключение. При этом DEP не мешает использовать уже существующий исполняемый код, на чём и строится ROP.
• ASLR усложняет поиск нужных адресов, случайным образом размещая области памяти процесса. Даже если атакующий знает, какие фрагменты кода ему нужны, для эксплуатации потребуется определить их расположение.
• CFI (Control Flow Integrity) ограничивает допустимые передачи управления, например проверяет, соответствует ли вызываемая через указатель функция ожидаемому типу. Набор проверок зависит от реализации, поэтому CFI нельзя считать универсальной защитой от любых ROP-цепочек.
• Посмотрим на небольшой пример CFI, для сборки понадобятся Linux, Clang, LLD и runtime санитайзеров Clang:
• Создадим файл demo.c, в котором намеренно вызовем функцию через указатель несовместимого типа:
#include <stdio.h>
static void hello(void) {
puts("Called");
}
int main(void) {
void (*fn)(int) = (void (*)(int))hello;
fn(123);
return 0;
}
• Функция hello не принимает аргументов, но мы вызываем её через указатель на функцию, принимающую
int, явное приведение типа не делает такой вызов корректным.• Сначала соберём без CFI:
clang -O0 demo.c -o plain
./plain
• Программа может вывести Called, хотя в коде уже есть неопределённое поведение, поэтому рассчитывать на такой результат нельзя.
• Теперь включим проверку косвенных вызовов:
clang -O0 -flto -fuse-ld=lld \
-fsanitize=cfi-icall -fno-sanitize-trap=cfi-icall \
demo.c -o protected
./protected
• Ожидаемый результат, сообщение об ошибке CFI и остановка до выполнения hello, поскольку тип функции не соответствует типу указателя. Именно такую проверку выполняет режим cfi-icall.
• DEP и ASLR здесь не решают проблему, мы обращаемся к существующей функции по известному адресу, а ошибка заключается в недопустимом вызове.
• Эти механизмы дополняют друг друга, но не исправляют уязвимый код, поэтому включённые защиты не заменяют устранение самой ошибки. Удачи!
#Безопасность #Эксплуатация
• Поддержать автора монеткой: @v_meshke
