0X00 漏洞描述
CVE-2014-4113是一个经典的内核提权漏洞 问题出在win32k.sys中 在exploit-db中可以直接找到exp 所以我这里从exp分析(其实是想从补丁分析来着 但没找到补丁。。。)
漏洞出现在win32k!HandleMenuMessages流程中 当该函数调用win32k!xxxMNFindWindowFromPoint获取一个指向win32k!tagwnd的指针后 没有正确的判断返回值 导致将返回错误代码当作指针 直接使用它作为餐宿调用win32k!xxxSendMessageTimeout对win32k!tagwnd对于的窗口发送消息 当win32k!xxxSendMessageTimeout对win32K!tagwnd内容可控时 可以劫持Rip 执行我们的shellcode
0X01 分析环境
目标系统:Windows 7 32位
调试器:WinDbg
反汇编器:IDA Pro
0x02 漏洞分析
从exp中剥离出来一部分代码来进程crash 从而了解漏洞点及其执行流
poc:
1 | #include<stdio.h> |
运行之后查看栈回溯 发现了漏洞触发的关键流程
在IDA中加载win32k.sys 定位HandleMenuMessages(这里我理解的是 如果有补丁文件的话 我们也可用Bindiff分析到此处的函数修改 我的意思是 我是如何定位到这里的 直接从这里分析的)
也可以这样定位 我们可以看到是esi出了点问题 所以根据偏移 在IDA中找到对应的函数地址
然后找到对应的参数为P 而P是这个函数的形参 所以需要找到调用该函数的函数
使用IDA的交叉引用功能配合Winddbg可以找到SendMessage函数
继续寻找可以找到SendMessageTimeout函数 里面调用了SendMessage函数 从而找到P的值即为V13 而V13的是又是MNFindWindowFromPoint函数的返回值 所以需要跟进该函数看看
进入函数后发现 该函数的返回值为-1或者-5
等等 -5即就是0xFFFFFFFB 这个值我们似乎在哪里见过 回到Windbg看看 可以发现第一个参数的值为0xFFFFFFFB
来看看这个V13的传参流程
1 | -->MNFindWindowFromPoint的返回值为-5 |
现在回到SendMessageTimeOut里面 可以看到 有个call函数直接将esi(V13或者P)的值作为调用 如果我们将shellcode放在这个地址 即就是call dword ptr 5B 不就可以完成利用了???
0x03 漏洞利用
现在知道shellcode的存储地址了 如何去执行呢 利用的思路大致如下
分配0页内存
NtAllocateVirtualMemory函数用来在指定进程的虚拟空间申请一块内存 我们使用这块内存来存储我们的shellcode 当然我们分配完内存后 需要将shellcode存在0x5b的地址
1 | //Loads ntdll.dll into the processes memory space and returns a HANDLE to it |
创建菜单
用来执行后续的函数
1 | WNDCLASSA wnd_class = { 0 }; |
设置HOOK函数
1 | HHOOK setWindowsHook = SetWindowsHookExA(WH_CALLWNDPROC, HookCallback, NULL, GetCurrentThreadId()); |
调用TrackPopupMenu
谷歌之后也可找到该函数的作用 在指定位置显示快捷菜单 并跟踪菜单项的选择 用来弹出菜单 定义 看到有人提到用 顺便说一下这里
1 | TrackPopupMenu( |
WM_LBUTTONDOWN模拟点击菜单
用来让刚才创建的菜单对象中的WndProc执行
1 | LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { |
其中的DefWindowProca函数如下 可确保处理每条消息 为应用程序不处理的任何窗口消息提供默认处理 返回值是消息处理的结果
PostMessage函数将消息在消息队列中 与创建指定窗口的线程关联的消息返回 而无需等待线程处理消息。
销毁窗口
使用DestroyWindow来销毁刚才创建的窗口
1 | DestroyWindow(main_wnd); |
定义如下
1 | BOOL DestroyWindow( |
shellcode
老生常谈的内容了~
1 | int __stdcall ShellCode(int parameter1, int parameter2, int parameter3, int parameter4) |
至此 我们基本算是了解了利用过程 去创建一个菜单 模拟使用菜单 让菜单中的WndProc得到执行 收到内核发送的1EB消息 再使用EndMenu销毁菜单 并返回0xFFFFFFFB shellcode已经指向此地址了 即可得到利用
首先下个断点
1 | ba e1 win32k!xxxHandleMenuMessages |
断下来之后查看0x5b的内容 进一步查看该地址的内容 发现是个跳转表
给该地址下个断点然后运行一下 得把前面那个断点先清除了 单步执行后可以发现shllcode的地址了
利用
0x04补丁对比
使用Bindiff工具对比补丁文件
其实这个函数并没有多大的改变 跳转发生了变化 导致无法提权
这里我喜欢再回到IDA里看伪代码的变化 更加的清晰明了 if的判断条件发生了变化 patch版本增加了IsMFMWFPWindow函数
IsMFMWFPWindow函数的作用是返回0或者1 用来限制0 -5 -1
0x05经验总结
这是一个经典漏洞 反正大佬们都这么说的 确实 可以从里面get到不少东西 有些地方也和前面学习到的HEVD很相似 关于0页内存分配 有些需要记下来
0页内存是我们经常需要使用到的技巧 主要运用在以下情况
1 | --> 使用null指针 |
使用ZwAllocateVirtualMemory函数分配内存 函数参数如下
1 | NTSTATUS ZwAllocateVirtualMemory( |
如此使用 记住即可 即跟shellcode一样
1 | HMODULE hnt = LoadLibraryA("ntdll.dll"); |
需要注意的是:
1 BaseAddress传1
2 RegionSize为8192 一个内存页(不够也可以? 分配内存时不够的一个页的话不是会分给一个页?)
3 AllocationType中要包括MEM_TOP_DOWN 指从上向下分配内存 由于BaseAddress为1 这样分配成功后的地址范围就是0xFFFFE001 (-8191) 到 1 0地址也包括其中 这时再向0指针写数据并执行 都没问题了
好像还没搞完 后面再补补~