CVE2015-0057漏洞样本构造探索

cssembly 2015-02-15 10:15:00

0x00 前言


微软最新的补丁包修补了CVE2015-0057的提权漏洞,同一天,漏洞的发现者发表了分析文章《One-Bit To Rule Them All: Bypassing Windows’ 10 Protections using a Single Bit》,看完文章,想尝试构造一下样本,原本以为很简单,结果期间遇到了几个问题,分享出来,希望能与大家一起讨论。

0x01 分析


由于分析文章里提到漏洞是由xxxEnableWndSBArrows引起的,就通过CreateWindowEx创建ScrollBar,然后调用EnableScrollBar,执行到xxxDrawScrollBar,按照分析文章里的说明,完整流程如下:

结果发现可以执行到xxxGetColorObjects,但是总是没法走到xxxDefWindowProc,因为(((_WORD )P + 0x15) & 0x3FFF) == 0x29A总是成立,最后google了一下,发现这里是判断当前窗体是不是ScrollBar。

((_WORD )P + 0x15)表示FNID,是通过NtUserSetWindowFNID在创建窗体时设置的,可以在reactos的代码中看到,windows包含了如下的FNID。

+// FNID's for NtUserSetWindowFNID
+#define FNID_BUTTON      0x02A1
+#define FNID_COMBOBOX    0x02A2
+#define FNID_COMBOLBOX   0x02A3
+#define FNID_DIALOG      0x02A4
+#define FNID_EDIT        0x02A5
+#define FNID_LISTBOX     0x02A6
+#define FNID_MDICLIENT   0x02A7
+#define FNID_STATIC      0x02A8
+#define FNID_IME         0x02A9

NtUserSetWindowFNID中,可以看到这里会对(_WORD )(v2 + 0x2A)处的值进行设置,(_WORD )P + 0x15与等价(_WORD *)(v2 + 0x2A)。

看来直接通过创建ScrollBar是不能执行到xxxDefWindowProc的。通过内核调试器,在此处设置断点,在屏幕上点动各种窗体,当打开任务管理器,点击显示所有用户进程时,触发了断点。查看此时的调用栈,看到了CListView!!!看来可能可以通过MFC的CListCtrl来触发xxxDefWindowProc函数的执行。

重载了CListCtrl,当ListCtrl显示的东西过多时,就会出现ScrollBar,这样在CListCtrl里处理一下afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)消息,就能够处理CListCtrl里的ScrollBar的消息,进而释放掉ScrollBar窗体。当点击CListCtrl里的横向ScrollBar时,就会触发CListCtrlEx::OnHScroll,这次再调用EnableScrollBar,就能执行到xxxDefWindowProc函数。

void CListCtrlEx::OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar )
{
    hScroll = this->m_hWnd; 
    BOOL bEnable = ::EnableScrollBar( this->m_hWnd , SB_HORZ , ESB_DISABLE_BOTH );
    CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}

不过又遇到了新的问题,signed int __stdcall xxxLoadUserApiHook()函数里总是没法执行到xxxLoadHmodIndex,显然这个函数是与UserApiHook相关的,(1 << gihmodUserApiHook) &_(_DWORD )(_((_DWORD )gptiCurrent + 46) + 0xBC)的条件总是成立,好吧,断点设置在win32k!xxxLoadUserApiHook+0x2b处,结果程序启动的时候,加载user32.dll,调用InitUserApiHook,最终会执行几次xxxLoadHmodIndex,之后就不再调用了。

重新看了一下分析文章,核心就是在用户态回调时,释放掉窗体,因此只要找到另一条路径,在xxxEnableWndSBArrows返回前,将窗体释放就可以了。在xxxDefWindowProc里,会调用到SfnDWORD,该函数中就存在一个回调,如下图。KeUserModeCallback的第一个参数2表示的是ApiNumber,也就是说最终会通过用户态的KiUserCallbackDispatcher调用USER32!__fnDWORD函数,如果能够在调用该函数的时候,将窗体释放掉,也是能够达到触发漏洞的效果的。

定义一个forFree函数,在KeUserModeCallback回调到USER32!__fnDWORD时,调用这个函数将窗体对象释放掉,第一次调用DestroyWindow( hScroll )释放窗体,第二次则对窗体是否被释放进行验证。通过在USER32!__fnDWORD处调用forFree,可以看到调用EnableScrollBar最终触发了forFree执行。注意USER32!__fnDWORD会被频繁调用,因此要进行区分。

void forFree()
{
    DestroyWindow( hScroll );
    DestroyWindow( hScroll );
    return ;
}

第一次调用DestroyWindow后,可以看到函数执行成功,返回值eax=1。再次调用DestroyWindow后,win32k!ValidateHwnd就无法根据hScroll的值来获取到对应的内核窗体结构,返回值eax=0。从xxxDefWindowProc返回之后,实际上窗体已经不再与之前传递给EnableScrollBar的句柄对应了。

不过我在32位win7上,并没有找到分析文章里提到的OR操作和AND操作。

0x02 结语


上面就是我对CVE2015-0057漏洞样本构造过程的探索,与分析文章文章里的内容存在差异,有什么不对的地方,希望能与大家交流,我的邮箱:cssembly@gmail.com。

评论

W

winterFire 2015-02-16 21:05:02

卧槽,还是由我来消灭0回复吧,一进来就被迎面而来的杀气所吹倒...顶楼主,收藏了~

路人甲 2015-02-26 10:47:30

没有看懂是 哈哈

路人甲 2015-02-27 11:14:20

可以先hook ClientLoadLibrary,不让其load Theme dll,然后在xxxLoadUserApiHook中就可以调用到xxxLoadHmodIndex了。

C

cssembly 2015-02-27 11:47:07

之前我在想是不是可以通过创建scrollbar,挂钩NtUserSetWindowFNID函数,设置其FNID,我发现调用CreateWindow的时候会调用xxxLoadUserApiHook,不过不知道挂钩的话会不会出问题,没时间所以就没试过了

路人甲 2015-02-27 17:02:45

我试过hook callback里面的ClientLoadLibrary,是可以重现作者的完整流程。你文章里也提到可以走其他路径,也可以,还是比较灵活的。

C

cssembly

天资不足,勤奋有余!

twitter weibo github wechat

随机分类

Windows安全 文章:88 篇
MongoDB安全 文章:3 篇
事件分析 文章:223 篇
其他 文章:95 篇
硬件与物联网 文章:40 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录