天融信关于VLC Media Player 2.2.8 Use After Free漏洞分析

而 MinGW 应用的是 DWARF 款式调试信息而非 PDB 款式,以是在 Windbg 中调试时,依然无法看到符号信息,但这依然方便了我们应用 IDA 静态阐发

一、背景先容

VLC 是一款自由、开源的跨平台多媒体播放器及框架它支持浩繁音频与视频解码器及文件款式和种种流式协议,也能作为流式办事器在 IPv4 或 IPv6 的高速收集连接下应用

本文主要阐发 VLC Media Player 2.2.8 版本的 MKV 款式视音频分离器 (demuxer) 中存在的一个 UAF 破绽,实验情况为最新版本的 Win10 64 位, 主要从破绽细节和破绽的使用两大年夜部分对破绽进行阐发

在破绽细节部分,主要分为以下三个方面:

1. 若何找到破绽触发点?

2. 若何确定 UAF 工具?

3. 阐发破绽成因

在破绽使用部分,下面这些问题都将获得阐明:

1. 若何经由过程正确的 Heap Spary 部署 shellcode?

2. 若何从新使用 UAF 工具?

3. 若何 bypass DEP&ASLR?

MKV

MKV 款式属于 Matroska 开源多媒体容器标准中的一种它是建立在 EBML 说话的根基上,这是一种类似于 XML 款式的可扩展二进制元说话,应用可变长度的整数存储,以节省空间EBML 元素都有自己的级别,每一个高一级的元素由多少次一级的元素组成全部 MKV 文件 从整体看可分为 2 大年夜部分:EBMLHeader 和 Segment前者包孕了文件的版本、文档类型等相关信息;后者则保存了媒体文件的视频和音频的实际数据,又可以再分为 SeekHead、Chapters、Attachments、Cluster 等多少子元素

1.1 破绽描述

天融信阿尔法实验室颠末深入阐发关于该破绽主如果源于 VLC 播放器用于处置惩罚 MKV 款式的视音频分离器模块——libmkv_plugin.dll 中, 该模块的一个 matroska_segment_c 工具在开释后被重引用可以经由过程构造大年夜量和该工具大年夜小相同的数据块,终极使这块内存从新被分配,后面再引用该工具时,便可节制法度榜样指针,着末经由过程正确的 Heap Spray 部署 shellcode、ROP 绕过 DEP,造成随意率性代码履行

1.2 受影响的系统版本

VLC media player

1.3 破绽编号

CVE-2018-11529

二、破绽细节

因为 VLC Media Player 的 release 版本没有调试信息,以是我们可以在官网下载源码自行编译带有调试信息的法度榜样别的要留意的的一点是,官方保举应用 MinGW 在 Linux 情况下交叉编译 VLCMedia Player

2.1 破绽触发点

首先我们必要找到破绽触发的位置,应用 Windbg 自带对象 gflag 开启页堆:

将 vlc.exe 附加到 Windbg 上,打开 poc.mkv,在此处触发了非常,可以确定破绽存在于 libmkv_plugin.dll 模块,再结合 IDA 和源码阐发,得知详细位置是在函数 int Control(demux_t_0 *p_demux, int i_query, va_list args) 中

现在关闭页堆,从新打开 vlc.exe,并在刚才非常的位置下断点,再次打开 poc.mkv,可以看到 [ebp+28h] 处的值为 0×22000020,

查看 ebp 处的内容,发明恰是 PoC 中构造的 UAF_object 的内容,是以可以基础确定,ebp 中保存的是指向 UAF 工具指针

当然这里并不是破绽的触发点,UAF 破绽的触发一样平常都是要再次引用该工具的某个措施以是我们可以在 [ebp+28h] 处设置一个内存造访断点(也便是上图中 0×22000020 的位置):ba r4 0f4069e8

继承运行法度榜样,断在了函数 matroska_segment_c::UnSelect() 中:

继承以后履行几步便会触发破绽,弹出谋略器

这里的 call 指令调用了一个位于 vlc.exe 模块中的地址,结合 PoC 来看也是 ROP 链第一个 gadget 的地址

到这里已经可以初步推想破绽成因,在工具 matroska_segment_c 被开释后,接着申请与其大年夜小相同内存去「占坑」,待后面重引用该工具中某个函数或成员时,就可以节制法度榜样履行流程

2.2 开释重引用的位置

接下来的问题是——matroska_segment_c 工具是在哪里被开释,又是在哪里被从新分配呢?考试测验应用!heap -p -a 查看分配此内存时的栈回溯信息,发明无法却显示

这是由于这里编译器采纳了一种称为 FPO(Frame Pointer Omission)的优化要领,它压缩或者省略了在栈上为函数创建栈帧指针的历程这样可以加速函数调用,然则在这种环境下,调试器可能不能基于 EBP 展开栈帧(除非它有启用了 FPO 模块相匹配的符号文件,而前面提到过,我们没有法子天生用于 Windbg 调试的 PDB 文件)

[1] [2] [3] [4]下一页

在哪里分配?

又由于法度榜样每次加载时堆的地址都是不固定的,也无法靠下断点来进行阐发,那么我们只能结合 IDA 和源码来阐发了经由过程交叉引用找到调用 matroska_segment_c::UnSelect() 的 Close() 函数,察看 matroska_segment_c *是从哪掏出的,历程收拾如下:

①将 p_this 强制转换为 demux_t*型并赋值给 p_demux

②从 p_demux 偏移 0×48 处掏出 demux_sys_t*型的布局赋值给 p_sys

③再从 p_sys 偏移 0x6C 处掏出 virtual_segment_c *型布局赋值给 p_vsegment

④调用 virtual_segment_c 的 CurrentSegment() 措施获得一个 matroska_segment_c *型指针

⑤着末调用 matroska_segment_c 的 Unselect() 措施触发破绽

再来看看定义该函数的 vlc/modules/demux/mkv/mkv.cpp 文件

文件中定义了 Open()、Close() 和 Control() 等函数,经由过程函数名和注释可以推想,法度榜样在处置惩罚 mkv 文件时,首先履行 Open() 函数而 Open() 和 Close() 都有相同的参数——p_this,在上面的阐发中,UAF 工具的指针恰是从该布局体中掏出的刚进入 Open() 函数时,工具的初始化应该都没有完成,我们只需监视 p_demux 布局在上述内存偏移中的变更,就能找到分配 UAF 工具的地方

在 IDA 找到 Open() 函数肇端地址,在 Windbg 中下断点,然后找到该函数参数 p_this 这个布局的位置,在其响应偏移处设置内存造访断点,察看这些位置内存的变更

中断到这里时,UAF 工具处于已经被创建了

在 IDA 中查看该地址,定位在 demux_sys_t::PreloadLinked(demux_sys_t*const this) 函数中,而这个函数又只在函数 Open(vlc_object_t *) 中被引用,着末很快就能在源码中找到分配该内存的位置:

在哪里开释?

描述一个堆块的数据布局是 HEAP_ENTRY,它位于一个堆块肇端处前 8 字节,而一个堆块的状态由占用状态变为余暇状态时,对付已开释的堆块,堆治理器定义了 HEAP_FREE_ENTRY 布局来描述,比拟 HEAP_ENTRY,它多了 8 个字节的 LIST_ENTRY 布局用于寄放余暇链表(Free List)的节点

那么要定位到该堆块开释的位置便是十分轻易了,我们只必要在 HEAP_ENTRY 处设置内存造访断点,当堆块开释时 HEAP_ENTRY 中某些标志位发生改变就能立即断下

按 F5 继承运行,不出所料的断在了 ntdll!RtFreeHeap 中,只必要 Step Out 几回,就能来到 libmkv_plugin.dll 模块中开释这块内存地方了

此时,堆块状态已经过 busy 变为了 free:

同样的,结合 IDA 在源码中找到开释堆块的位置

在哪里重用?

继承追踪堆块的变更,和上面的措施类似,就不再赘述了

2.3 破绽成因

在 libmkv_plugin.dll 模块的 static intOpen(vlc_object_t * p_this) 函数中,经由过程调用 ProloadLinked() 函数预加载所有链接段,并创建了一个 virtual_segment_c 工具实例,在该工具的构造函数中,matroska_segment_c 工具被初始化

但后面的 FreeUnused() 函数中却又开释掉落了 matroska_segment_c*指向的内存

上一页[1] [2] [3] [4]下一页

而在 static void Close(vlc_object_t *p_this) 函数中,p_vsegment 调用 CurrentSegment() 措施返回了一个指向 matroska_segment_c 的指针赋值给 p_segment,但该指针指向的工具 matroska_segment_c 已经被开释,p_segment 早已是一个悬空指针

三、破绽使用

3.1 Heap Spray支配 shellcode

一个 MKV 文件所有的音视频帧数据都寄放在 Cluster 这个布局中每个 Cluster 内可能有很多个 BlockGroup 组成,BlockGroup 内又由多少个 Block 组成这些 Block 内便是音视频的帧数据在 PoC 便是构造了数十个大年夜小为 0xfff000 的 Cluster,并将喷射数据放入每个 Block 中

当堆块进行大年夜量分配的时刻,堆地址的低位不会发生变更,仅有几个高字节是改变的,查看堆喷的内存块,可以看到每次打开 PoC 堆喷的地址总因此「018」结尾的,变更的老是前面的高位地址,以是我们只需以将每个 Block 的大年夜小节制为 0×1000,就能够准确获得喷射的位置以下图为例,0×22000020 所在堆块的 UserPtr 是 0x211e5020(0×22000020 – 0x211e5020) / 0×1000 = 0xE1B(3611) 刚好能被整除,也便是 0×22000020 的内容能被我们正确节制,由于它永世是一个 Block 的肇端地址

3.2 构造 UAF 工具

MKV 的 Attachment 部分主如果用于支持在文件中附加任何类型的文件,包括图片、网页、法度榜样等经由过程构造 500 个大年夜小与之前被开释的 matroska_segment_c 大年夜小相同的 attachments,使开释掉落的内存被从新分配

结合源码可以看到,着末触发破绽的函数并不是 UAF 工具的措施,而是从 UAF 工具中又掏出的其他,以是在上面堆喷的数据中不仅部署了 shellcode,还有 demux_sys_t、demux_t 和 es_out_t 这些布局终极触发破绽的 es_out_Del 函数的指针就定义在 es_out_t 这个布局中,回偏激看看堆喷数据中,该指针已被部署为了 ROP chains 的肇端地址

3.3 bypass DEP&ASLR

应用 Immunity debugger 的插件 mona,我们可以查见地度榜样所有运行的模块这里可以看到主法度榜样 vlc.exe 的 Rebase 是 False, 纵然有 ASLR,法度榜样每次打开其加载地址仍旧和曩昔一样的不会改变,以是我们可以在 vlc.exe 模块中探求可用的 gadget,构造 ROP chains

完备的 ROP 链如下:

不合于通俗的栈溢出,此时我们能节制的只有几个寄存器的值,而当前法度榜样的栈空间是弗成控的,以是在 ROP 链的开始使用 Stack Pivot, 将栈指针 esp 指向堆喷的内存中,相称于将堆作为栈来应用

将 edi 的值放入 edx, 剩下的三条指令没有实际感化,在栈上添补无用的数据来补偿被三个 pop 指令弹出的值

剩下的几条 gadget 在为后面部署栈的内容做筹备

上一页[1] [2] [3] [4]下一页

将上面寄存器中的值压入栈中

设置 VirtualProtectStub 的参数

转跳到 VirtualProtectStub 函数中,改动喷射内存的的履行权限

完成后转跳到 shellcode 继承履行

要实现破绽的使用,只需简单的改动 PoC 中的 shellcode 部分就行,下面是颠末改动的用于 MSF 的破绽使用模块

四、修复建议

今朝官方已修复该破绽,可从官网下载最新版本

上一页[1] [2] [3] [4]

赞(0) 打赏
分享到: 更多 (0)
免责申明:本站所有资料均来自于网络,版权归原创者所有!本站不提供任何保证,不保证真实性,并不承担任何法律责任

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云优惠网 更专业 更优惠

阿里云优惠券阿里云大礼包