Статья Уязвимость DEP в подсистеме WoW64

1. Вводная часть
2. Атрибуты вирт.страниц в записях РТЕ
3. Характеристики в заголовке РЕ-файла
4. Заключение



1. Вводная часть

Системный механизм безопасности DEP (Data Execution Prevention, предотвращение выполнения данных) в своё время наделал много шуму, и как оказалось он имеет некоторые нюансы. Всё сказанное ниже касается только 32-битных приложений Win, для запуска которых на системах х64 предусмотрена прослойка WoW64 (Windows on Windows).

Если проследить от куда у DEP ноги растут, то получаем следующую картину. На нижнем уровне имеем таблицу дескрипторов GDT, флаги в которой задают атрибуты целым сегментам памяти, например кода и данных в юзер-спейс (кольцо 3), кода/данных в пространстве ядра (кольцо 0), а так-же структуре РЕВ (см. селектор 50 ниже). На команду dg (dump gdt) отладчик WinDbg отзывается таким логом:

Код:
0: kd> dg 0 80
                                                        P  Gr  Pr  Lo
Sel         Base               Limit           Type     l  an  es  ng  Flags
----  -----------------  -----------------  ----------  -  --  --  --  --------
0000  00000000`00000000  00000000`00000000  <Reserved>  0  By  Np  Nl  00000000
0008  00000000`00000000  00000000`00000000  <Reserved>  0  By  Np  Nl  00000000
0010  00000000`00000000  00000000`00000000  Code RE Ac  0  By  P   Lo  0000029b
0018  00000000`00000000  00000000`ffffffff  Data RW Ac  0  Pg  P   Nl  00000c93
0020  00000000`00000000  00000000`ffffffff  Code RE Ac  3  Pg  P   Nl  00000cfb
0028  00000000`00000000  00000000`ffffffff  Data RW Ac  3  Pg  P   Nl  00000cf3
0030  00000000`00000000  00000000`00000000  Code RE Ac  3  By  P   Lo  000002fb
0038  00000000`00000000  00000000`00000000  <Reserved>  0  By  Np  Nl  00000000
0040  00000000`0379c000  00000000`00000067  TSS32 Busy  0  By  P   Nl  0000008b
0048  00000000`0000ffff  00000000`0000f800  <Reserved>  0  By  Np  Nl  00000000
0050  ffffffff`fffde000  00000000`00003c00  Data RW Ac  3  By  P   Nl  000004f3
0058  00000000`00000000  00000000`00000000  <Reserved>  0  By  Np  Nl  00000000
0060  00000000`00000000  00000000`ffffffff  Code RE     0  Pg  P   Nl  00000c9a
0068  00000000`00000000  00000000`00000000  <Reserved>  0  By  Np  Nl  00000000
0070  00000000`00000000  00000000`00000000  <Reserved>  0  By  Np  Nl  00000000
0078  00000000`00000000  00000000`00000000  <Reserved>  0  By  Np  Nl  00000000
0080  Unable to get descriptor
0: kd>
------------------------------------
Pl  : Privilege   (0=Kenel, 3=User)
Gran: Granularity (By=Byte, Pg=Page)
Pres: Present     (Np=No, P=Yes)
Long: Size        (Nl=32, Lo=64 bit)

Однако после перехода в РМ включается пейджинг (страничная организация памяти), и таблица GDT уходит уже на второй план, уступая место 4-уровневой таблице страниц "PageTable". То-есть большие сегменты вирт.памяти делятся на более мелкие страницы размером по 4 КБ. Записи "РТЕ" в этой таблице описывают атрибуты страниц и соответствие вирт.адреса физ.кадру памяти PFN (Page Frame Number). Так вот механизм DEP включается именно старшим битом(63) в РТЕ, который известен как "Бит XD" в библии Intel'a (eXecute Disable), или "NX" в доках AMD (No-eXecute).


2. Атрибуты вирт.страниц в записях РТЕ

Чтобы наглядно продемонстрировать эту фишку можно провести небольшой эксперимент, использовав в качестве кролика, например, процесс калькулятора. Значит запустим calc.exe, и получив адрес его структуры EPROCESS, запросим атрибуты записи PTE страницы, куда отображается неисполняемая структура РЕВ процесса:

Код:
0: kd> !process 0 0 calc.exe
PROCESS fffffa80115c7b00
    SessionId: 1  Cid: 1198    Peb: 7fffffde000  ParentCid: 05d8
    DirBase: 2996d2000  ObjectTable: fffff8a0051027a0  HandleCount: 98.
    Image: calc.exe

0: kd> .process /p fffffa80115c7b00       <-----// Переключить контекст на calc.exe
Implicit process is now fffffa80`115c7b00

0: kd> !cmkd.ptelist -v 7fffffde000       <-----// Адрес структуры РЕВ
VA=000007FFFFFDE000
  PXE Idx=00F  Va=FFFFF6FB7DBED078 Contents=507000031AB28867 Hard Pfn=0031AB28  Attr=---DA--UWEV
  PPE Idx=1FF  Va=FFFFF6FB7DA0FFF8 Contents=33F0000178029867 Hard Pfn=00178029  Attr=---DA--UWEV
  PDE Idx=1FF  Va=FFFFF6FB41FFFFF8 Contents=00A00003ABC2A867 Hard Pfn=003ABC2A  Attr=---DA--UWEV
  PTE Idx=1DE  Va=FFFFF683FFFFFEF0 Contents=831000020D2AB967 Hard Pfn=0020D2AB  Attr=-G-DA--UW-V

Здесь видно, что вирт.адрес записи РТЕ равен 0xFFFFF683`FFFFFEF0, и это физ.фрейм в ОЗУ под номером Pfn=0020D2AB. Значение-же самой записи "Entry" указано в столбце "Contents", а в ядре её олицетворяет структура MMPTE_HARDWARE. Обратите внимание на последнюю строку "NoExecute=1" ниже - это и есть бит(NX), который представляет собой рубильник механизма DEP для данной вирт.страницы:

Код:
0: kd> dt _mmpte_hardware FFFFF683FFFFFEF0
nt!_MMPTE_HARDWARE
   +0x000 Valid            : 0y1
   +0x000 Dirty1           : 0y1
   +0x000 Owner            : 0y1
   +0x000 WriteThrough     : 0y0
   +0x000 CacheDisable     : 0y0
   +0x000 Accessed         : 0y1
   +0x000 Dirty            : 0y1
   +0x000 LargePage        : 0y0
   +0x000 Global           : 0y1
   +0x000 CopyOnWrite      : 0y0
   +0x000 Unused           : 0y0
   +0x000 Write            : 0y1
   +0x000 PageFrameNumber  : 0y000000000000001000001101001010101011 (0x20d2ab)
   +0x000 reserved1        : 0y0000
   +0x000 SoftwareWsIndex  : 0y00000110001 (0x31)
   +0x000 NoExecute        : 0y1

0: kd>

Активность DEP для процесса calc.exe подтверждает и системный "Диспетчер задач", или более продвинутый его форк "Process Hacker". А вот у страниц Total, AkelPad и WinWord бит DEP вместе с ASLR уже отключён:

dep_1.webp

Но во-всей этой кухне интересно другое. Мы знаем, что страницы могут иметь три основных атрибута - это чтение(R), запись(W), и исполнение(Е). Эти атрибуты задаются в аргументах таких функций как VirtualAlloc() и VirtualProtect(). Но если посмотреть на биты структуры MMPTE_HARDWARE, то среди них имеется лишь бит "Write" (страница доступна для записи), а вот бита "Execute" как-раз таки нет! От сюда следует, что любые страницы вирт.памяти доступны для исполнения, пусть это будет хоть секция-данных, хоть стек. Другими словами, система и сам процессор могут только запретить исполнение битом NX в РТЕ, хотя в дефолте буквально все страницы исполняемые.

В качестве пруфа напишем простое 32-битное приложение с процедурой "HelloWorld", которую поместим для исполнения сначала в секцию данных, а потом скопируем её и в стек. Обратите внимание, что механизм DEP при этом в системе включён для всех (см. Win+Break --> Доп.параметры --> Быстродействие --> DEP).

WinBreak.webp

C-подобный:
format   pe gui
include 'win32ax.inc'
entry    start
;//----------
.data
szText1  db      'Launch code from data section',0
szText2  db      'Launch code from STACK',0

;// Процедура для исполнения в секции-данных
align    4
startDep:
         invoke  MessageBox,0,szText1,0,0
         ret
         align   4
endDep:
;//----------
section '.code' code readable executable
start:
;// Первый вызов из секции данных - ОК!
        call    startDep

;// Теперь копируем процедуру в стек
        push    esp
        mov     ecx,endDep - startDep      ;// размер кода
        sub     esp,ecx                    ;// выделяем фрейм в стеке
        mov     dword[startDep+5],szText2  ;// изменить текст мессаги
        mov     esi,startDep               ;// источник для копирования
        mov     edi,esp                    ;// приёмник
        cld                            ;// прямой шаг
        rep     movsb                  ;// скопировать ECX-байт..
        call    esp                    ;// вызов процедуры из стека!
        add     esp,endDep - startDep  ;// восстановить стек,
        pop     esp                    ;// ..и указатель на него

        invoke  ExitProcess,0          ;// на выход
;//----------
section '.idata' import data readable
library  user32,'user32.dll', kernel32,'kernel32.dll'
include  'api\user32.inc'
include  'api\kernel32.inc'

Как видим, даже с активным DEP данные всё-равно доступны для исполнения (см.файл в скрепке)! Так в чём-же подвох? А дело в том, что мало иметь включённый DEP на уровне ЦП и системы, так ещё и само приложение должно быть скомпилированно с одноимённым ключом. У компоновщиков С++ и MASM этот ключ называется /NXCOMPAT, и он включён в дефолте так, что его можно только сбросить /NXCOMPAT[:NO].


3. Характеристики в заголовке РЕ-файла

А вот компилятор FASM не может собирать исходники с ключами, поэтому нужно указать его явно в поле "DLL Characteristics" опционального заголовка уже скомпилированного РЕ-файла. Для манипуляций подобного рода как-нельзя лучше подходит крутая софтина - этот процесс показан на скрине ниже. Родственная фишка рандомизации базы образа ASLR включается здесь битом "DLL can move" (в бинаре должна быть секция ".reloc"):

CffExp.webp

Код:
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 = образ является терминальным сервером.

Только теперь наше приложение будет иметь полноценную защиту от исполнения данных DEP, и если сейчас запустить его, то сразу-же получим крэш с ошибкой доступа 0xC0000005. От сюда следует, что сбросив всего один бит в х32 исполняемом файле мы можем полностью анулировать этого сторожа, после чего без проблем внедрять свой шелл в любое место процесса-жертвы, для последующего запуска от туда через Wow64.

HelloDep.webp


4. Заключение

DEP в WoW64 работает, но с ограничениями, т.к. это реализованный в либе Wow64.dll эмулятор, запускающий 32-бит приложения на 64-бит оси. Политика DEP может быть менее строгой или вовсе конфликтовать, если старые программы требуют выполнения кода из стека/кучи, что аппаратная защита ЦП в виде бита NX/XD блокирует. Для корректной работы 32-битных программ, DEP работает в режиме «Opt-Out» или принудительно отключается системой, чтобы избежать критических ошибок. Таким образом нельзя сказать, что DEP в WoW64 не работает полностью - просто он часто уходит в менее строгий режим, для совместимости с 32-битным софтом.
 

Вложения

Последнее редактирование:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Популярный контент

WAPT