четверг, 10 января 2013 г.

Dos в Win7/8

Обнаружил занятную вещь.
Если попытаться загрузить драйвер diskdump.sys или dumpata.sys из стандартного каталога с драйверами windows, то произойдет BSOD:

SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (7e)
This is a very common bugcheck.  Usually the exception address pinpoints
the driver/function that caused the problem.  Always note this address
as well as the link date of the driver/image that contains this address.
Arguments:
Arg1: c0000005, The exception code that was not handled
Arg2: 82645e85, The address that the exception occurred at
Arg3: 807df9f0, Exception Record Address
Arg4: 807df5d0, Context Record Address

Debugging Details:
------------------

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - <Unable to get error code text>
...
STACK_TEXT: 
807dfab8 965f339f 0052005c 00000000 00010000 nt!memset+0x45
807dfad8 827cf2e6 8647c2b8 86688000 00000000 Diskdump!DriverEntry+0x1b
807dfcbc 827d2d98 00000001 00000000 807dfce4 nt!IopLoadDriver+0x7ed
807dfd00 82689a6b 8a692cd0 00000000 84491640 nt!IopLoadUnloadDriver+0x70
807dfd50 82814f9e 00000001 41adab71 00000000 nt!ExpWorkerThread+0x10d
807dfd90 826bd1f9 8268995e 00000001 00000000 nt!PspSystemThreadStartup+0x9e
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x19

Смотрим код:

Diskdump!DriverEntry:
965f3384 8bff            mov     edi,edi
965f3386 55              push    ebp
965f3387 8bec            mov     ebp,esp
965f3389 53              push    ebx
965f338a 56              push    esi
965f338b 8b750c          mov     esi,dword ptr [ebp+0Ch]
965f338e 57              push    edi
965f338f bf00000100      mov     edi,10000h
965f3394 57              push    edi
965f3395 6a00            push    0
965f3397 ff7608          push    dword ptr [esi+8]
965f339a e805110000      call    Diskdump!memset (965f44a4)

Итак, падение происходит из-за обнуления памяти размером 10000h по невалидному адресу. Адрес виден в стеке: 0052005c

То, что он невалиден понятно по его виду, это юзермодный адрес, а Driver Entry выполняется в контексте системного потока. Но можно и проверить:

kd> !pte 0052005c
                    VA 0052005c
PDE at C0600010            PTE at C0002900
contains 0000000000000000
not valid

Откуда берется невалидный адрес? Он приходит из второго аргумента DriverEntry. По документации там всегда валидная копия юникодной строки, представляющей из себя registry path:
NTSTATUS DriverEntry( _In_ struct _DRIVER_OBJECT *DriverObject, _In_ PUNICODE_STRING RegistryPath )
Собственно, невалидный адрес и является частью этой юникодной строки:

5c 00 52 00 это \.R, первые 2 WCHAR от строки
\.R.E.G.I.S.T.R.Y.\.M.A.C.H.I.N.E.\.S.Y.S.T.E.M.\.C.o.n.t.r.o.l.S.e.t.0.0.1.\.s.e.r.v.i.c.e.s.\.D.i.s.k.d.u.m.p.

Теперь разберемся, почему драйвер diskdump.sys считает, что вторым агрументом придет какая-то структура, которую он должен заполнить, вместо registry path. После непродолжительных поисков находится виновник - драйвер crashdmp.sys, в который начиная с windows vista перекочевал из ядра весь код, связанный с крешдампами.

Драйвер crashdmp.sys загружает ядро в ф-ции IopLoadCrashdumpDriver(), где в качестве второго аргумента в DriverEntry приходит указатель на таблицу колбеков. К слову,  crashdmp.sys также портит память со строкой registry path, но до BSOD'а дело не доходит, по причине слишком длинной строки registry path.

Далее, в CrashdmpLoadDumpStack грузятся остальные драйвера из стека драйверов, в которые также передается вторым параметром в DriverEntry структура, которую они заполняют своими ф-циями.

Собственно, разработчики windows не учли тот факт, что драйвера из стека crash dump i/o path можно загрузить как обычный драйвер. Никаких проверок для предотвращения их загрузки нет. Поэтому и возможен такой локальный DOS.

Данного рода баг существует как минимум на win7 и win8 как х86, так и х64.

Теперь посмотрим, возможна ли тут EoP (elevation of privilege).

.text:0001239F                 push    dword ptr [esi+4Ch] ; size_t <= 00300074
.text:000123A2                 lea     ebx, [esi+0Ch]
.text:000123A5                 push    0               ; int
.text:000123A7                 push    dword ptr [ebx] ; void * <= 00470045
.text:000123A9                 call    _memset
.text:000123AE                 push    dword ptr [esi+4Ch] ; size_t <= 00300074
.text:000123B1                 push    0               ; int
.text:000123B3                 push    dword ptr [esi+10h] ; void * <= 00530049
.text:000123B6                 call    _memset
.text:000123BB                 mov     eax, [esi+8]                 <= 0052005c
.text:000123BE                 lea     ecx, [eax+10h]               <= 52006c
.text:000123C1                 mov     _DeviceExtension, ecx        <= 52006c
.text:000123C7                 and     dword ptr [eax], 0
.text:000123CA                 mov     dword ptr [eax+8], 2F0h
.text:000123D1                 mov     edx, _DeviceExtension
.text:000123D7                 lea     ecx, [eax+2F0h]
.text:000123DD                 mov     [edx+18h], ecx
.text:000123E0                 mov     ecx, edi
...
.text:000123F0                 mov     eax, _DeviceExtension
.text:000123F5                 mov     eax, [eax+18h]
.text:000123F8                 and     dword ptr [eax], 0 <======== bingo!

_DeviceExtension контролируемая атакующим структура, поле [eax+18h] контролируется, можно записать туда ядерный адрес, и он будет обнулен.

Реализация атаки через обнуление произвольного нулевого адреса давно известна, причем не только ресерчерами, но и security командой windows, которая в win8 прикрыла выделение памяти по нулевому адресу.

Однако, напомню, что дело происходит в Driver Entry, которая выполняется в контексте системного потока. То есть даже на win7 нельзя будет проэксплуатировать уязвимость с обнулением dword'а по нулевому адресу.

Кроме того, по этой же причине ( системный поток ), также не получится и просто добраться до инструкции, обнуляющей нужный адрес. Потому как для успешного выполнения нескольких ф-ций memset нужно, чтобы требуемые адреса были валидны. А они будут валидными, только если научиться в контексте системного процесса каким-либо образом выделить память. Мне таких способов не известно, боюсь, их вообще не существует. Для драйвера dumpata.sys картина примерно такая же.

Таким образом, реализация EOP невозможна, только DOS.