Order Of Six Angles

Main Logo

Х.Р.А.М Сатаны

Home | RU | Translations | Art | Feed.xml

26 March 2020

tags: windows - defence

Механизмы защиты 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 - это механизм 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 также сообщает об этом:

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

Обработчики представлены в виде структуры 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 - указатель на код обработчика

Вид в дебагере:

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

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

Вот так, очень очень кратко, работает 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:

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

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

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

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

Схематично:

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

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

Обнуление регистров

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

SEHOP

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

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

SAFESEH

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

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

Outro

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

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

Всем спасибо!

Вверх