• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Механизмы защиты Windows от эксплуатации уязвимостей

tabac

CPU register
Пользователь
Регистрация
30.09.2018
Сообщения
1 610
Решения
1
Реакции
3 332
Механизмы защиты Windows от эксплуатации уязвимостей. Часть 1.


Intro

В этой серии статей мы поговорим о механизмах защиты от эксплуатациии уязвимостей в Windows.
Эта статья для моего телеграм канала Order Of Six Angles
Начать копать эту тему, меня подтолкнула новость, о новой технологии защиты от ROP, которая будет реализована в новых процессорах Intel на уровне железа, но уже реализованной в Windows 10 в софтварном виде. О ней позже, в следующих частях.

Методы предотвращения эксплуатации делятся на защиту уровня ОС и защиту на уровне компилятора.
Untitled%20Diagram.png


Compile based - то что встраивает компилятор
OS based - защита самой Windows, не зависящая от того, как вы собрали свою программу
Hardware based - защита на уровне процессора


Buffer security check
Переполнение буфера используется в эксплоитах, для перезаписи адреса возврата из функции, что позволяет перенаправить выполнение на свой вредоносный код.

Советую прочитать мою статью "Классический buffer overflow и создание шеллкода вручную", чтобы понимать детали.

Если кратко, то перед буфером обычно находится адрес кода, который будет выполнен по завершению функции. Записав в буфер больше данных, чем он содержит, мы выйдем за границы и получим доступ к этому адресу (на картинке это ret адрес, от слова return).
stack.png

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 и посмотрим, что получилось.
2.png

Наглядно видим разницу - некое значение 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) ; Проверяем
Стек теперь выглядит вот так
3.png

Если вы выйдете за границы буфера, то затрете security cookie и после проверки не произойдет переход по адресу возврата, что является сутью эксплуатации переполнением буфера. При этом, программа мгновенно завершится. Изучим, что же такое security cookie. Ниже код в самом начале нашей программы
4.png

  1. security cookie сравнивается со стандартным значением 0x0BB40E64E
  2. test - это побитовый AND. Результат security cookie AND 0x0FFFF0000 должен равняться нулю
Если одно из этих условий выполняется, то начинается генерация настоящего security cookie. Выглядит она вот так
5.png

Здесь вызываются функции
  1. GetSystemTimeAsFileTime - текущая дата и время
  2. GetCurrentThreadId - id потока
  3. GetCurrentProcessId - id процесса
  4. QueryPerformanceCounter - это тот же timestamp, но с повышенной точностью
Результаты функций ксорятся, вот и готово security cookie.

Теперь рассмотрим проверку по завершению функции

6.png


Если помните, ранее в регистр ecx мы положили восстановленное значение security cookie. В конце мы просто сравниваем с изначальным значением и если оно изменилось то завершаем процесс
7.png


Важный момент - любая программа скомпилированная, с помощью Microsoft C/C++ компилятора, начинает свое выполнение не с вашей main() функции, а с функций инициализации C Runtime. Именно там генерируется security cookie. Но если вы компилируете с флагом /ENTRY и явно указываете с какой функции начинать свою программу, то эту процедуру вы пропускаете и должны сами вручную вызывать функцию генерации security cookie. Если вы этого не сделаете, то как вы уже видели, оно примет стандартное значение, а значит злоумышленник в своем эксплоите захардкодит его и пройдет проверку.

Outro
Как видите, все довольно тривиально. Я и сам до этого момента не погружался в детали этой защиты. Можно сказать, что мы сделали это вместе!)

На этом первая часть завершена, всем спасибо!


Автор Thatskriptkid / @OrderOfSixAngles
 
Механизмы защиты Windows от эксплуатации уязвимостей. Часть 2: SAFESEH и SEHOP.

Intro


Использовал:
  1. компилятор Microsoft (R) C/C++ версии 19.24.28316 для x86
  2. vscode
  3. Windows 10 с самыми последними обновлениями
  4. x64dbg
В этой серии статей мы говорим о механизмах защиты от эксплуатации уязвимостей в Windows.


SEH
Если вы программируете, то знаете, что в работе кода могут возникнуть ситуации, при которых происходит обращение к недоступной вам памяти, запись в несуществующий элемент массива, запись в несуществующий список, запись на переполненный диск и так далее. Такие ситуации называют исключениями. Вы можете их обработать явно - указать, что сделать, если возникнет исключение. Можете и не обрабатывать. В Windows для этого существует специальный механизм SEH или Structured Exception Handling, который отвечает за обработку исключений в вашей программе, даже если вы явно не написали обработчик. Компилятор Microsoft C/C++ сам вставит код для обработки исключений и сама Windows вам поможет его обработать. Поэтому SEH является и технологией уровня компилятора (compile based) и уровня ОС (os based).

SEH%20Diagram.png


Кстати, как бы не критиковали яву, но этот язык вам даже не позволит скомпилировать программу с необработанным исключением. Еще она очень сильно форсит ООП на уровне компилятора, за счет чего долго живет в энтерпрайзе

Важно заметить, что SEH - это механизм x86 систем. В 64-битных механизм другой и рассматриваться не будет. Для начала, разберем, что это такое, потом как его эксплуатируют и существующую защиту.

Заранее предупреждаю - это не статья о всех деталях работы SEH, нам это знать не обязательно, мы лишь обозреваем как его защищают. Также - это не статья об эксплуатации SEH, мы лишь кратко узнаем, в чем ее суть, чтобы просто понимать от чего защищаемся, как это было в первой части. Если же вы хотите увидеть более детальную статью об этом - пишите мне в личку.

Для обработки исключений программы, во время работы создается список обработчиков (handlers). Так как SEH применяется к каждому потоку, то найти указатель на список можно в специальной и очень важной структуре TEB (Thread Environment Block). Она содержит различную информацию о конкретном потоке:
Код:
typedef struct _NT_TIB {
    struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
    PVOID StackBase;
    PVOID StackLimit;
    PVOID SubSystemTib;
#if defined(_MSC_EXTENSIONS)
    union {
        PVOID FiberData;
        DWORD Version;
    };
#else
    PVOID FiberData;
#endif
    PVOID ArbitraryUserPointer;
    struct _NT_TIB *Self;
} NT_TIB;
typedef NT_TIB *PNT_TIB;
Thread Environment Block обычно расположен по сдвигу от адреса, расположенного в регистре FS (для x86). Дебагер x64dbg также сообщает об этом:

TEB_location.png


ExceptionList (первый элемент структуры) - содержит указатель на первый элемент списка обработчиков исключений. В x64dbg командой teb() можно получить абсолютный адрес TEB и изучить ее:

TEB.png

Обработчики представлены в виде структуры EXCEPTION_REGISTRATION_RECORD, которая является недокументированной, но в сети есть ее описание от различных реверсеров.

Так как я проверял все на Windows 10 с самыми последними обновлениями на момент написания (25.03.2020), то пришлось все отдебажить лично и убедиться, что источники в интернете все еще актуальны.

Ее описание выглядит так:
Код:
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
     PEXCEPTION_REGISTRATION_RECORD Next;
     PEXCEPTION_DISPOSITION Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
Next - указатель на следующий элемент списка

Handler - указатель на код обработчика

Вид в дебагере:
SEH_HANDLER.png

Написав простенькую программу и открыв ее в x64dbg, можно проследить весь список:
SEH%20records.png

Когда в программе происходит исключение, специальная функция в ntdll KiUserExceptionDispatcher начинает обход списка, для поиска необходимого обработчика, подходящего к конкретной ситуации.

KiUserExceptionDispatcher.png

Вот так, очень очень кратко, работает SEH. Теперь посмотрим на эксплуатацию этого механизма.

SEH exploitation
Для примера возьмем код из первой части и добавим запись по некорректному адресу в памяти, чтобы вызывать исключение:
Код:
#include "stdio.h"
int main()
{
    unsigned char buf[64];
    FILE *fp = fopen("crash_file", "r");
    fread(buf, 1, 1024, fp);

    int *a = (int *) 0x41414141;
        *a = 0xdeadbeef;
     return 0;
}
Код содержит уязвимость buffer overflow, так как читает 1024 байта в 64 байтный буфер.

Откроем в дебагере и посмотрим на список SEH:

SEH_chain_before_overwrite.png


Это обработчики конкретно нашей программы. Их адреса расположены на стеке и выглядят вот так:

STACK_before_SEH_overwrite.png

Схематично и наглядно:
SEH_schema_before_overwrwite.png

Очевидно, что переполнив буфер, мы можем затереть адреса реальных обработчиков на свои. В данном случае мы затерли адресом 0xDEADBEEF:
stack_after_SEH_OVERWRITE.png


Список SEH тоже поменялся:
SEH_chain_after_overwrite.png


Схематично:
SEH_schemda_after_overwrite.png

Отпустив дебагер и продолжив выполнение мы получим access violation, так как адрес 0xDEADBEEF явно некорректный:

EXCEPTION_ACCESS_VIOLATION.png


Если бы мы вместо 0xDEADBEEF указали реальный адрес вредоносного кода, то, при возникновении исключения, он бы выполнился. Собственно это и есть суть эксплутации SEH overwrite. Данный способ применяется, когда программа скомпилированна с Buffer security check. Как видите на рисунке выше, мы также затерли security cookie, но прикол в том, что его проверка происходит по выходу из функции, а исключение происходит раньше. То есть, перезапись SEH является обходом защиты из первой части. Перейдем к методам защиты.

Обнуление регистров
Начиная с Windows XP SP1, перед вызовом обработчиков исключений, все регистры процессора обнуляются. Это делается для того, чтобы даже если SEH был перезатерт вредоносным кодом, его работала была нарушена отсутствием нужных данных в регистрах. Усложняет написание шеллкодов, так как теперь он не сможет получить на вход важные данные. Данная защита является защитой на уровне ОС.

SEHOP
Structured Exception Handler Overwrite Protection - начиная с Windows Vista SP1, предотвращает перезапись обработчиков, которую мы рассмотрели ранее. Является защитой на уровне ОС и не зависит от того, как вы скомпилировали свою программу. Посмотрим еще раз на скрин с SEH цепочкой:
SEH%20records.png


Суть SEHOP заключается в том, что эта технология проверяет всю валидность цепочки. Это означает, что все адреса следующего элемента списка должны быть корректными и располагаться на стеке. Теперь нельзя просто забить хламом буфер и перезатереть первый обработчик, забив на все остальные. Это очень сильно усложняет эксплуатацию, так как придется подбирать правильные адреса. Также проверяется значение адреса следующего элемента в последнем обработчике цепочки - он должен равняться 0xFFFFFFFF, как на скрине.

SAFESEH
SAFESEH - технология защиты, уровня компилятора, при которой, в заголовках PE файла программы, создается специальная таблица, с заранее предустановленными, относительными адресами обработчиков. Произошедшее исключение, во время работы, проверяется по этой таблице и если не совпадает - программа завершается. SAFESEH включается одноименным ключом компиляции. Чтобы посмотреть на нее в живую, откроем нашу программу в PE Bear от прекрасной hasherezade.

SEH%20table.png

Здесь мы видим количество обработчиков SEHandlerCount и их адреса SEHandlerTable.

Outro
Даже познакомившись с таким малым количеством технологий, можно сделать вывод, что их сила - в комбинации. И чем их больше - тем лучше. Только защита на всех уровнях (компилятор, ОС, процессор) поможет усложнить атаки. Радует, что рассмотренные механизмы стары и все еще действенны. Злоумышленники каждый год находят что-то новое, и защита прогрессирует соответственно, повышая порог вхождения, что не может не радовать. Считаю, что основная суть защиты SEH раскрыта, а в детали погружаться не было задачи. В отличие от обилия статей в интернете, я включил описание эксплуатации, так как в отрыве от нее, непонятно, что защищаем.

Вообще, если честно, это все я пишу для себя, чтобы возвращаясь к определенной теме, не читать по 10-50 закладок в браузере, а быстро пробежаться по основным моментам. Еще мне пришла мысль, из-за того, что я наверное один из немногих пишу статьи в вики - они не индексируются. Ну да ладно.

Cтатья для моего телеграм канала Order Of Six Angles.
Автор Thatskriptkid / @OrderOfSixAngles
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх