Уязвимость CVE-2017-0263 уже использовалась в фишинговой рассылке.
win32k.sys
и тому, как данный процесс позволяет эксплуатировать указанную уязвимость.win32k!gMenuState
, которая представляет собой структуру win32k!tagMENUSTATE
:0: kd> dt win32k!tagMenuState
+0x000 pGlobalPopupMenu: Ptr32 tagPOPUPMENU
+0x004 flags: Int4B
+0x008 ptMouseLast: tagPOINT
+0x010 mnFocus: Int4B
+0x014 cmdLast: Int4B
+0x018 ptiMenuStateOwner: Ptr32 tagTHREADINFO
+0x01c dwLockCount: Uint4B
+0x020 pmnsPrev: Ptr32 tagMENUSTATE
+0x024 ptButtonDown: tagPOINT
+0x02c uButtonDownHitArea: Uint4B
+0x030 uButtonDownIndex: Uint4B
+0x034 vkButtonDown: Int4B
+0x038 uDraggingHitArea: Uint4B
+0x03c uDraggingIndex: Uint4B
+0x040 uDraggingFlags: Uint4B
+0x044 hdcWndAni: Ptr32 HDC__
+0x048 dwAniStartTime: Uint4B
+0x04c ixAni: Int4B
+0x050 iyAni: Int4B
+0x054 cxAni: Int4B
+0x058 cyAni: Int4B
+0x05c hbmAni: Ptr32 HBITMAP__
+0x060 hdcAni: Ptr32 HDC__
win32k!tagMENUSTATE
хранит, например, такую информацию, как кликнутая область экрана, номер последней отосланной меню команды, а также указатели на окна, по которым был совершен клик или которые были выбраны для перетаскивания (drag-and-drop). Сам список окон ниспадающего меню хранится в первом поле, pGlobalPopupMenu
, имеющем тип win32k!tagPOPUPMENU
:0: kd> dt win32k!tagPopupMenu
+0x000 flags: Int4B
+0x004 spwndNotify: Ptr32 tagWND
+0x008 spwndPopupMenu: Ptr32 tagWND
+0x00c spwndNextPopup: Ptr32 tagWND
+0x010 spwndPrevPopup: Ptr32 tagWND
+0x014 spmenu: Ptr32 tagMENU
+0x018 spmenuAlternate: Ptr32 tagMENU
+0x01c spwndActivePopup: Ptr32 tagWND
+0x020 ppopupmenuRoot: Ptr32 tagPOPUPMENU
+0x024 ppmDelayedFree: Ptr32 tagPOPUPMENU
+0x028 posSelectedItem: Uint4B
+0x02c posDropped: Uint4B
+0x030 ppmlockFree: Ptr32 tagPOPUPMENU
win32k!gMenuState
инициализируется в момент создания контекстного меню, то есть во время выполнения ранее упомянутой функции TrackPopupMenuEx
. Инициализация происходит при вызове win32k!xxxMNAllocMenuState
:1: kd> k
# ChildEBP RetAddr
00 95f29b38 81fe3ca6 win32k!xxxMNAllocMenuState+0x7c
01 95f29ba0 81fe410f win32k!xxxTrackPopupMenuEx+0x27f
02 95f29c14 82892db6 win32k!NtUserTrackPopupMenuEx+0xc3
03 95f29c14 77666c74 nt!KiSystemServicePostCall
04 0131fd58 7758480e ntdll!KiFastSystemCallRet
05 0131fd5c 100015b3 user32!NtUserTrackPopupMenuEx+0xc
06 0131fd84 7756c4b7 q_Main_Window_Class_wndproc (call TrackPopupMenuEx)
win32k!xxxMNEndMenuState
, ответственная за освобождение состояния меню:1: kd> k
# ChildEBP RetAddr
00 a0fb7ab0 82014f68 win32k!xxxMNEndMenuState
01 a0fb7b20 81fe39f5 win32k!xxxRealMenuWindowProc+0xd46
02 a0fb7b54 81f5c134 win32k!xxxMenuWindowProc+0xfd
03 a0fb7b94 81f1bb74 win32k!xxxSendMessageTimeout+0x1ac
04 a0fb7bbc 81f289c8 win32k!xxxWrapSendMessage+0x1c
05 a0fb7bd8 81f5e149 win32k!NtUserfnNCDESTROY+0x27
06 a0fb7c10 82892db6 win32k!NtUserMessageCall+0xcf
07 a0fb7c10 77666c74 nt!KiSystemServicePostCall
08 013cfd90 77564f21 ntdll!KiFastSystemCallRet
09 013cfd94 77560908 user32!NtUserMessageCall+0xc
0a 013cfdd0 77565552 user32!SendMessageWorker+0x546
0b 013cfdf0 100014e4 user32!SendMessageW+0x7c
0c 013cfe08 775630bc q_win_event_hook (call SendMessageW(MN_DODRAGDROP))
gMenuState.pGlobalPopupMenu
обновляется только в момент инициализации в функции xxxMNAllocMenuState
, но не обнуляется при уничтожении структуры.xxxMNEndMenuState
начинает выполнение с деинициализации и освобождения информации, связанной с ниспадающим меню. Для этого вызывается функция MNFreePopup
, к которой мы еще вернемся в следующем разделе. Основная задача MNFreePopup
заключается в уменьшении значений счетчиков ссылок (reference counters) на окна, относящиеся к данному ниспадающему меню. Уменьшение счетчика ссылок может, в свою очередь, приводить к уничтожению окна, когда счетчик ссылок на него опускается до нуля.xxxMNEndMenuState
обращением к флагу fMenuWindowRef
поля pGlobalPopupMenu проверяет, не осталось ли на основное окно ниспадающего меню ссылок. Этот флаг очищается в момент удаления окна, содержащегося в поле spwndPopupMenu
ниспадающего меню:3: kd> k
# ChildEBP RetAddr
00 95fffa5c 81f287da win32k!xxxFreeWindow+0x847
01 95fffab0 81f71252 win32k!xxxDestroyWindow+0x532
02 95fffabc 81f7122c win32k!HMDestroyUnlockedObject+0x1b
03 95fffac8 81f70c4a win32k!HMUnlockObjectInternal+0x30
04 95fffad4 81f6e1fc win32k!HMUnlockObject+0x13
05 95fffadc 81fea664 win32k!HMAssignmentUnlock+0xf
06 95fffaec 81fea885 win32k!MNFreePopup+0x7d
07 95fffb14 8202c3d6 win32k!xxxMNEndMenuState+0x40
xxxFreeWindow+83f disasm:
.text:BF89082E loc_BF89082E:
.text:BF89082E and ecx, 7FFFFFFFh; ~fMenuWindowRef
.text:BF890834 mov [eax+tagPOPUPMENU.flags], ecx
pGlobalPopupMenu
памяти, но обнуления самого указателя не происходит. Таким образом, получаем dangling pointer, который при выполнении определенных условий можно использовать в дальнейшем.uButtonDownHitArea
) в момент работы меню или выбраны для перетаскивания (поле uDraggingHitArea
).tagWND
. В той же статье описано и понятие kernel callbacks, которое нам далее понадобится. Количество действующих ссылок на окно содержится в поле cLockObj
структуры tagWND
.WM_NCDESTROY
.xxxMNEndMenuState
управление может быть передано на пользовательский код приложения, а именно — оконной процедуре уничтожаемого окна. Так происходит в случае, когда на окно, указатель на которое хранится в поле gMenuState.uButtonDownHitArea
, больше не остается ссылок. 2: kd> k
# ChildEBP RetAddr
0138fc34 7756c4b7 q_new_SysShadow_window_proc
0138fc60 77565f6f USER32!InternalCallWinProc+0x23
0138fcd8 77564ede USER32!UserCallWinProcCheckWow+0xe0
0138fd34 7755b28f USER32!DispatchClientMessage+0xcf
0138fd64 77666bae USER32!__fnNCDESTROY+0x26
0138fd90 77564f21 ntdll!KiUserCallbackDispatcher+0x2e
95fe38f8 81f56d86 nt!KeUserModeCallback
95fe3940 81f5c157 win32k!xxxSendMessageToClient+0x175
95fe398c 81f5c206 win32k!xxxSendMessageTimeout+0x1cf
95fe39b4 81f2839c win32k!xxxSendMessage+0x28
95fe3a10 81f2fb00 win32k!xxxDestroyWindow+0xf4
95fe3a24 81f302ee win32k!xxxRemoveShadow+0x3e
95fe3a64 81f287da win32k!xxxFreeWindow+0x2ff
95fe3ab8 81f71252 win32k!xxxDestroyWindow+0x532
95fe3ac4 81f7122c win32k!HMDestroyUnlockedObject+0x1b
95fe3ad0 81f70c4a win32k!HMUnlockObjectInternal+0x30
95fe3adc 81f6e1fc win32k!HMUnlockObject+0x13
95fe3ae4 81fe4162 win32k!HMAssignmentUnlock+0xf
95fe3aec 81fea8c3 win32k!UnlockMFMWFPWindow+0x18
95fe3b14 8202c3d6 win32k!xxxMNEndMenuState+0x7e
WM_NCDESTROY
обрабатывает оконная процедура окна класса SysShadow
. Окна этого класса предназначены для отрисовки тени и уничтожаются обычно вместе с окнами, эту тень отбрасывающими.gMenuState.pGlobalPopupMenu
, чтобы задействовать возможность воспользоваться данным указателем впоследствии. В попытке аллоцировать указанный блок памяти эксплойт совершает множество вызовов SetClassLongW
, устанавливая специальным образом сформированное наименование меню заранее созданным для этой цели классам окон:2: kd> k
# ChildEBP RetAddr
00 9f74bafc 81f240d2 win32k!memcpy+0x33
01 9f74bb3c 81edadb1 win32k!AllocateUnicodeString+0x6b
02 9f74bb9c 81edb146 win32k!xxxSetClassData+0x1d1
03 9f74bbb8 81edb088 win32k!xxxSetClassLong+0x39
04 9f74bc1c 82892db6 win32k!NtUserSetClassLong+0xc8
05 9f74bc1c 77666c74 nt!KiSystemServicePostCall
06 0136fac0 7755658b ntdll!KiFastSystemCallRet
07 0136fac4 775565bf user32!NtUserSetClassLong+0xc
08 0136fafc 10001a52 user32!SetClassLongW+0x5e
09 0136fc34 7756c4b7 q_new_SysShadow_window_proc (call SetClassLongW)
NtUserMNDragLeave
, которая, в свою очередь, совершает вложенный вызов (nested call) функции xxxMNEndMenuState
, т. е. очистка структуры gMenuState
начинает выполняться заново:2: kd> k
# ChildEBP RetAddr
00 9f74bbf0 8202c3d6 win32k!xxxMNEndMenuState
01 9f74bc04 8202c40e win32k!xxxUnlockMenuStateInternal+0x2e
02 9f74bc14 82015672 win32k!xxxUnlockAndEndMenuState+0xf
03 9f74bc24 82001728 win32k!xxxMNDragLeave+0x45
04 9f74bc2c 82892db6 win32k!NtUserMNDragLeave+0xd
05 9f74bc2c 100010a9 nt!KiSystemServicePostCall
06 0136fafc 10001a84 q_exec_int2e (int 2Eh)
07 0136fc34 7756c4b7 q_new_SysShadow_window_proc (call q_exec_int2e)
pGlobalPopupMenu
, которая производится вызовом MNFreePopup
, выполняющим уменьшение значений счетчиков ссылок на окна, содержащиеся в различных полях tagPOPUPMENU
. При этом содержимое данной структуры после предыдущего шага контролируется атакующим. Таким образом, при выполнении описанной цепочки действий злоумышленник получает примитив декремента (decrement primitive) на произвольный адрес ядра.tagPOPUPMENU.spwndPrevPopup
и примитив используется для декремента поля флагов одного из окон, что приводит к возведению у этого окна флага bServerSideProc
, означающего выполнение его оконной процедуры в ядре.NtUserMNDragLeave
такому окну вызовом SendMessage посылается сообщение, что приводит к выполнению произвольного кода в ядре (kernel code execution). Обычно, используя эту возможность, злоумышленник ворует токен системного процесса, получая системные привилегии. Именно это и происходит в описываемом эксплойте.win32k.sys
. Выставление флага bServerSideProc
у окна также является популярным методом получения возможности выполнения кода в ядре. И третье, при выполнении кода в ядре копирование ссылки на системный токен — наиболее удобный способ поднятия привилегий.win32k!gMenuState
при выполнении процедуры xxxMNEndMenuState
. Обошли стороной и то, что устанавливаемые при вызовах SetClassLong наименования меню должны, с одной стороны, представлять из себя юникодную строку, не имеющую нулевых символов, а с другой — являться легитимной структурой tagPOPUPMENU
. Это также означает, что и адрес окна в ядре, на которое будет указывать поле для декремента, не должен содержать нулевых символов wchar_t. Это всего лишь несколько примеров из довольно внушительного списка. gMenuState.pGlobalPopupMenu
, происходит ближе к окончанию функции xxxMNEndMenuState
, много позже вызовов MNFreePopup
и UnlockMFMWPWindow
, и сопровождается обнулением самого указателя. Таким образом, патч исключает сразу две причины, одновременное наличие которых приводило к появлению уязвимости.