DEP и ASLR – игра без правил. Часть 2

DEP и ASLR – игра без правил. Часть 2

Life-Hack [Жизнь-Взлом]/Хакинг

#Обучение 

Рассмотрим очередную "бомбу" под названием "Advanced Space Layout Randomization" ASLR, которая начиная с Win-7 призвана рандомно менять базу образа в памяти. Отметим, что все системные файлы Win запускаются с включённым механизмом ASLR, но у сторонних приложений он может быть отключён. Чтобы наши программы влились в эту струю, нужно указать это явно, посредством флага компиляции "/DYNAMIC_BASE".

Для образов прикладных программ, новая рандомная база вычисляется при каждом запуске исполняемого файла. В отличие от юзерских программ, базы системных образов вычисляется только один раз, при начальной загрузке операционной системы и используется во всей системе вплоть до её перезагрузки. Если-бы системные DLL заново отображались по разным адресам внутри различных процессов, их код нельзя было-бы использовать совместно. Загрузчику пришлось-бы корректировать указатели на функции API для каждого процесса в отдельности, превращая общие функции в закрытые данные процесса.

Механизм ASLR рандомно меняет базы трёх блоков памяти процесса – это: база образа, база стека, и база кучи Heap. Рандом образа - 8-битный, что позволяет выбрать одну из 256 возможных баз. А вот рандом стека и кучи уже 5-битный – такая разрядность ограничивает выбор до 32-х базовых адресов.

Все-кто хочет под управлением ASLR кочевать в памяти, должны иметь в своём РЕ-заголовке определённые флаги, которые задаются программистом при компиляции проекта. Первое поле размером 16-бит находится по-смещению РЕ.16h и называется "Characteristics". Набор этих флагов определяет глобальные свойства исполняемого файла, и каждый из 16-ти его бит несёт в себе определённую информацию. Основные его биты перечислены нихе: 

• Бит 0 – IMAGE_FILE_RELOCS_STRIPPED – таблетка от ASLR.

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

• Бит 1 – IMAGE_FILE_EXECUTABLE_IMAGE

Устанавливается, если файл является исполняемым экзе, и действительно может быть запущен

• Бит 4 – IMAGE_FILE_AGGRESIVE_WS_TRIM

Если взведён, то ОС принудительно урежет объём памяти для этого процесса, путём разбиения его на страницы. Этим битом могут похвастаться процессы-демоны, которые большую часть времени спят, пробуждаясь лишь несколько раз в день.

• Бит 5 – IMAGE_FILE_LARGE_ADDRESS_AWARE

Приложение способно работать с памятью, объёмом больше 2 Gb.​​

• Бит 12 – IMAGE_FILE_SYSTEM​

Если взведён, значит это системный файл типа драйвера.​​

• Бит 13 – IMAGE_FILE_DLL​

Взведённый бит является признаком DLL-библиотеки.​​

Из всей этой группы, в контексте ASLR примечательным является только бит (0), который разрешает релоки. У библиотек DLL он всегда сброшен, а у исполняемых файлов типа *.ехе – наоборот взведён. Это потому, что при запуске процесса на исполнение сначала в память загружается экзе, который сразу занимает предпочтительную базу, и только потом уже он подтягивает в своё пространство импортируемые либы – т.е. кто первым встал, того и база. Если DLL будет неперемещаемой, то может получиться конфликт базовых адресов EXE<->DLL.

Чтобы продемонстрировать состояние этих флагов, запустим программу "РЕ-Explorer". Она не то чтобы отображает флаги, но и позволяет редактировать их биты. Это одна из лучших утилит в своём роде, способная проникнуть в самые закрома исполняемых файлов:​

Однако это не единственный бит, от которого пляшет ASLR.. а точнее - он для него второстепенный. Прямым рычагом для рандомной релокации образа является слово по-смещение РЕ.5Eh с несовпадающим по смыслу именем "DLL-characteristics". Именно в этом поле хранятся ключи, которые мы задаём компилятору при сборке своего исходника в экзе. Как упоминалось выше, чтобы системный механизм ASLR подхватил наш файл, нужно указать компилятору ключ "/DYNAMIC_BASE" – иначе образ так и останется неперемещаемым, хоть в системе и будет активен ASLR. Бит-мап 16-битного поля РЕ.5Еh представлен ниже:​

C-подобный: 

0x0001  Зарезерв (должно быть ноль).
0x0002  Зарезерв.
0x0004  Зарезерв.
0x0008  Зарезерв.
0x0020  IMAGE_DLL_HIGH_ENTROPY_VA = ASLR с энтропией 24 бита (для х64).
0x0040  IMAGE_DLL_DYNAMIC_BASE = ASLR с энтропией 8 бит (для х32).
0x0080  IMAGE_DLL_FORCE_INTEGRITY = обязательная проверка целостности кода.
0x0100  IMAGE_DLL_NX_COMPAT = образ совместим с NX (механизм DEP).
0x0200  IMAGE_DLL_NO_ISOLATION = не изолировать образ.
0x0400  IMAGE_DLL_NO_SEH = запрет на использование в образе обработчиков исключений SEH.
0x0800  IMAGE_DLL_NO_BIND = не связываемый образ.
0x1000  IMAGE_DLL_APPCONTAINER = образ должен выполняться в AppContainer.
0x2000  IMAGE_DLL_WDM_DRIVER = образ является драйвером WDM.
0x4000  IMAGE_DLL_GUARD_CF = поддерживает защиту Control Flow Guard, CFG.
0x8000  IMAGE_DLL_TERMINAL_SERVER_AWARE = образ является терминальным сервером.

Мда.. судя по характеристикам, адепты этой секты способны на многое, и ожидать от них можно чего угодно. Заполучив админские права, и привелегию "Debug" мы можем рекурсией обойти все файлы в системной папке и сбросить непригодные нам биты ASLR. Проблема открытых файлов и невозможности перезаписать их образ на диске, решается копированием дескрипторов открытых файлов функциями OpenProcess() с последующим DuplicateHandle(). В результате мы получим родительские права на объект, со-всеми вытекающими последствиями. 

Не знаю для чего это нужно, ведь базу образа можно найти и динамическим путём. Любая малварь поколения "Next" поступает именно так, а жёсткую привязку через РЕ-заголовок можно найти сейчас только на свалке истории. Однако для самообразования можно написать такую утилиту, которая будет искать все исполняемые файлы в текущем дире жёсткого диска, и выводить информацию о поддержки ими технологии ASLR. Тут просто поиск и проверка флага "DLL_DYNAMIC_BASE» в поле РЕ.5Еh : 

C-подобный: 

format   pe console
include  'win32ax.inc'
entry    start
;//------
.data
capt     db   13,10,' ASLR info v0.1'
         db   13,10,' ~~~~~~~~~~~~~~~~~~~',0
info     db   13,10,10,' File: ======== %s'
         db   13,10,'       PE.16h = %04X'
         db   13,10,'       PE.5Eh = %04X',0
aslr     db   '  <-- ASLR dynamicBase bit found',0

fData    WIN32_FIND_DATA       ;// структура для FindFile()
fName    =    fData +44        ;//   ..смещение имени-файла в ней
fMask    db   '*.exe',0        ;// маска для поиска = только ЕХЕ
frmt     db   '%s',0           ;// спецификатор для scanf()
hndl     dd   0                ;// хэндл поиска
buff     rb   512              ;// буфер под РЕ-заголовок

;//------
.code
start:  cinvoke  printf,capt   ;// шапка

;//--- Поиск подходящих кандидатов.. ---------
         invoke  FindFirstFile,fMask,fData    ;// ищем первый файл
         mov     [hndl],eax                   ;// запомнить хэндл поиска

;//--- Сбрасываем заголовок найденного файла в буфер -----
@scan:   invoke  _lopen,fName,0           ;// открываем файл
         push    eax                      ;// запомнить его хэндл
         invoke  _lread,eax,buff,512      ;// считываем 512 байт в свой буфер
         pop     eax                      ;// хендл на родину
         invoke  _lclose,eax              ;// файл больше не нужен..

;//--- Поиск флагов в буфере -----------------
         mov     esi,buff                 ;// ESI = голова буфа
         mov     esi,[esi+0x3c]           ;// указатель на РЕ-заголовок
         add     esi,buff                 ;// смещаемся к нему
         mov      ax,word[esi+0x16]       ;// AX = характеристики ЕХЕ
         mov      bx,word[esi+0x5e]       ;// ВХ = характеристики DLL
         and     eax,0xffff               ;// обнулить не нужное
         and     ebx,0xffff               ;// ...^^^
         push    ebx                      ;// запомнить флаги DLL
        cinvoke  printf,info,fName,eax,ebx   ;// выводим батву на консоль

;//--- Определяем поддержку файлом ASLR ------
         pop     ebx                      ;// ЕВХ = флаги DLL
         test    ebx,1000000b             ;// чекнуть бит "DLL_DYNAMIC_BASE"
         jz      @next                    ;// если он нуль..
        cinvoke  printf,aslr              ;// иначе: даём знать об этом!

@next:   invoke  FindNextFile,[hndl],fData   ;// продолжить поиск в дире..
         or      eax,eax                     ;// если EAX = 0, нет больше файлов
         jnz     @scan                       ;// повторить, если NoZero.

         invoke  CloseHandle,[hndl]  ;// прибить хэндл поиска
 @exit: cinvoke  scanf,frmt,capt     ;// ждём клаву..
        cinvoke  exit,0              ;//

;//=== Секция импорта ========================
data     import
library  msvcrt,'msvcrt.dll',kernel,'kernel32.dll'
import   msvcrt,printf,'printf',scanf,'scanf',exit,'exit'
import   kernel,FindFirstFile,'FindFirstFileA',\
         FindNextFile,'FindNextFileA',CloseHandle,'CloseHandle',\
         _lopen,'_lopen',_lread,'_lread',_lwrite,'_lwrite',_lclose,'_lclose'
end      data

По идее, чтобы прибить ASLR для избранных файлов, нужно обнаружив у него флаг "DynamicBase" сбросить его, тогда у механизма ASLR останутся несыгранные роли. Но чем он нам мешает? У рандомной смены базы тоже есть своя грация, над которой "художники" постарались на славу. М.Руссинович в седьмом издании своего бестселлера "Внутреннее устройство Windows" описывает эти механизмы на более глубоком уровне, вплоть до алгоритмов вычисления дельты для рандома и прочее. Так-что всем, кого интересует данная тема, советую почитать этот его том. А за сим всё.. до скорого.​

Источник


Report Page