tags: windows - defence
Механизмы защиты Windows от эксплуатации уязвимостей. Часть 1.
Intro
В этой серии статей мы поговорим о механизмах защиты от эксплуатациии уязвимостей в Windows.
Начать копать эту тему, меня подтолкнула новость, о новой технологии защиты от ROP, которая будет реализована в новых процессорах Intel на уровне железа, но уже реализованной в Windows 10 в софтварном виде. О ней позже, в следующих частях.
Методы предотвращения эксплуатации делятся на защиту уровня ОС и защиту на уровне компилятора.
Compile based - то что встраивает компилятор
OS based - защита самой Windows, не зависящая от того, как вы собрали свою программу
Hardware based - защита на уровне процессора
Buffer security check
Переполнение буфера используется в эксплоитах, для перезаписи адреса возврата из функции, что позволяет перенаправить выполнение на свой вредоносный код.
Советую прочитать мою статью Классический buffer overflow и создание шеллкода вручную, чтобы понимать детали.
Если кратко, то перед буфером обычно находится адрес кода, который будет выполнен по завершению функции. Записав в буфер больше данных, чем он содержит, мы выйдем за границы и получим доступ к этому адресу (на картинке это ret адрес, от слова return).
Buffer security check - это compile based защита компилятора Microsoft C/C++. Представлена она в виде ключа компиляции /GS, который включен по дефолту. Компилятор сам определяет функции в вашей программе, которые могут быть подвержены переполнению буфера и защищает их.
Давайте посмотрим, в чем она заключается. Для этого я написал простенькую программу, которая пишет в 64 байтный буфер 128 байт из файла.
int main()
{
unsigned char buf[64];
FILE *fp = fopen("crash_file", "r");
fread(buf, 1, 128, fp);
return 0;
}
Скомпилировал оба варианта, с ключом /GS
и /GS-
(отключает защиту). Откроем в IDA и посмотрим, что получилось.
Наглядно видим разницу - некое значение security cookie
записывается в начале функции и проверяется в конце (__security_check_cookie()
). Разберем построчно
mov eax, __security_cookie ; Записываем в регистр eax значение security cookie
xor eax, ebp ; Добавляем рандом в значение
mov [ebp + var_4], eax ; Кладем на стек, после ebp
Проверка security cookie
mov ecx, [ebp + var_4] ; Кладем в регистр ecx значение security cookie
xor ecx, ebp ; Восстанавливаем изначальное значение
call __security_check_cookie(x) ; Проверяем
Стек теперь выглядит вот так
Если вы выйдете за границы буфера, то затрете security cookie
и после проверки не произойдет переход по адресу возврата, что является сутью эксплуатации переполнением буфера. При этом, программа мгновенно завершится. Изучим, что же такое security cookie
. Ниже код в самом начале нашей программы
security cookie
сравнивается со стандартным значением0x0BB40E64E
test
- это побитовыйAND
. Результатsecurity cookie AND 0x0FFFF0000
должен равняться нулю
Если одно из этих условий выполняется, то начинается генерация настоящего security cookie
. Выглядит она вот так
Здесь вызываются функции
GetSystemTimeAsFileTime
- текущая дата и времяGetCurrentThreadId
- id потокаGetCurrentProcessId
- id процессаQueryPerformanceCounter
- это тот же timestamp, но с повышенной точностью
Результаты функций ксорятся, вот и готово security cookie
.
Теперь рассмотрим проверку по завершению функции
Если помните, ранее в регистр ecx
мы положили восстановленное значение security cookie
. В конце мы просто сравниваем с изначальным значением и если оно изменилось то завершаем процесс
Важный момент - любая программа скомпилированная, с помощью Microsoft C/C++ компилятора, начинает свое выполнение не с вашей main()
функции, а с функций инициализации C Runtime. Именно там генерируется security cookie
. Но если вы компилируете с флагом /ENTRY
и явно указываете с какой функции начинать свою программу, то эту процедуру вы пропускаете и должны сами вручную вызывать функцию генерации security cookie
. Если вы этого не сделаете, то как вы уже видели, оно примет стандартное значение, а значит злоумышленник в своем эксплоите захардкодит его и пройдет проверку.
Outro
Как видите, все довольно тривиально. Я и сам до этого момента не погружался в детали этой защиты. Можно сказать, что мы сделали это вместе!)
На этом первая часть завершена, всем спасибо!
Вверх