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сравнивается со стандартным значением0x0BB40E64Etest- это побитовый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
Как видите, все довольно тривиально. Я и сам до этого момента не погружался в детали этой защиты. Можно сказать, что мы сделали это вместе!)
На этом первая часть завершена, всем спасибо!
Вверх