漏洞概述

Win32k.sys是Windows的系统驱动文件,在Win32k.sys中负责窗口管理,以及GUI进程/线程都将使用Win32k.sys。与其相关的用户模式模块是user32.dll和GDI32.DLL,由于用户模式应用程序的复杂性许多问题存在于Win32k.sys中。其中CVE-2014-4113就是Win32k.sys中的一个漏洞,该漏洞的根本问题是函数xxxMNFindWindowFromPoint的返回值验证不正确。
xxxMNFindWindowFromPoint函数执行后返回win32k!tagWND的地址结构或错误代码-1,-5。在该函数后面将调用函数xxxSendMessage,xxxSendMessage把xxxMNFindWindowFromPoint的返回值作为参数传递。当xxxMNFindWindowFromPoint返回win32k!tagWND地址的时候程序正常执行,但当返回-1,-5的时候传递给xxxSendMessage将造成蓝屏。

漏洞细节

通过对利用程序的分析发现在该漏洞利用代码中将调用PsLookupProcessByProcessId获取进程EPROCESS结构指针,在利用该结构中的TOKEN进行提权。在调用PsLookupProcessByProcessId的函数开头下断点,在内核模式调试虚要切换到应用层下断点。断下来后通过kv命令能看到程序的执行流程如下。
可以看到程序是在执行函数TrackPopupMenu触发了漏洞。通过函数调用栈,对esi值的来源进行反向跟踪,可以知道在Menu的消息处理函数xxxHandleMenuMessages(int a1, int a2, int a3)中调用xxxMNFindWindowFromPoint(int a1, int a2, unsigned int a3)时返回了异常的值,最终触发了漏洞。
通过对xxxMNFindWindowFromPoint的调用过程进行分析,找到异常的返回值是在SfnOUTDWORDINDWORD(int a1, int a2, int a3, int a4, int a5, int a6, char a7, int a8)中得到。异常的值最终是由KeUserModeCallback函数通过v27指向的值返回。
内核态的KeUserModeCallback函数最终会调用ntdll中的KiUserCallbackDispatcher函数来调用用户态回调函数,通过对KeUserModeCallback、KiUserCallbackDispatcher设置断点,可以看到第一次处理0x1EB(MN_FINDWINDOWFROMPOINT)消息是通过xxxSendMessageTimeout中调用的xxxCallHook来调用用户注册的钩子函数,在用户空间里函数调用了USER32中的__fnOUTDWORDINDWORD函数,最终调用fn函数。
程序在fn函数中通过SetWindowLongA设置PopupMenu的窗口消息处理函数,那么当xxxCallHook函数返回后,下图中的条件不成立,将执行xxxSendMessageToClient,该函数内将执行KeUserModeCallback,最终调用用户态函数sub_13013F3。
sub_13013F3函数中会调用 EndMenu 销毁菜单窗口并返回0xFFFFFFFB。应用层代码返回 0xFFFFFFFB,执行流返回到内核 win32k! xxxMNFindWindowFromPoint 上下文 ,0xFFFFFFFB与KeUserModeCallback函数通过v27返回的值相等,0xFFFFFFFB会做为xxxMNFindWindowFromPoint的返回值。为了确认,可以修改sub_13013F3函数返回值为0xFFFFFFFF。可以看到v27指向的值变成了0xFFFFFFF。
可见在PopupMenu的窗口消息处理函数处理0x1EB的消息时,没有判断消息函数的返回值,最终导致了漏洞。 该漏洞触发的完整过程如下:通过模仿点击事件,CreatePopupMenu创建的PopupMenu会收到0x1EB类型的消息,因为无法拿到PopupMenu的窗口句柄,程序并没有办法直接设置PopupMenu的窗口消息处理函数,因此首先通过SetWindowsHookExA注册钩子函数,在钩子函数中得到PopupMenu的窗口句柄,再通过SetWindowLongA设置PopupMenu的窗口消息处理函数,注册之后xxxSendMessageToClient将调用新的窗口消息处理函数,接收并处理0x1EB的消息。 在新的窗口消息处理函数中,对于消息号为0x1EB的消息,函数返回了0xFFFFFFFB,最终触发了漏洞。

漏洞利用

对于消息号为0x1EB的消息,函数返回了0xFFFFFFFB,而程序把该值作为win32k!tagWND结构处理,导致后边把0xFFFFFFFB作为win32k!ptagWND结构传给win32k! xxxSendMessage。在win32k! xxxSendMessage中会调用win32k!xxxSendMessageTimeout,在win32k!xxxSendMessageTimeout中当把0xFFFFFFFB作为win32k!tagWND结构处理时,会调用ptagWND+0x60处的函数,也就是call [0xFFFFFFB+0x60],即call [0x5B]。

通过call [0x5B]可以想到只要在0x5B的地址上布置shellcode的地址,在执行到call [0x5B]的时候就能跳转到shellcode代码上去执行。基本的利用流程如下。
1) 通过函数ZwAllocateVirtualMemory申请0页内存空间,在该空间建立一个畸形的win32k!tagWND结构的映射页,使得在内核能正确地验证。并将shellcode地址布置在0x5B。
2) 触发漏洞,并设置 xxxMNFindWindowFromPoint返回值为 -5(0xfffffffb) 。xxxSendMessage将把-5作为一个有效的地址。然后调用指向shellcode的函数指针。
3) Shellcode中将调用PsLookupProcessByProcessId获取EPROCESS信息,用系统进程system进程的EPROCESS.Token替换自己进程的EPROCESS.Token提升权限。
4) 创建一个子进程,将用系统程序权限执行。

点击收藏 | 0 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖