原标题:Ogg音频格式文件的样本构造(CVE-2018-5146)

*严正声明:本文仅限于技术讨论与分享,严禁用于非法途径

下面的所有分析都是在Firefox 59.0 32位上进行的。由于笔者是刚入门的小白,水平有限,文章有错误或者写法不当的地方,请各位师傅斧正。

一.概述

这是一个firefox的漏洞,位于libvorbis库中,在音频合成过程的深层次位置,想要触发它只能由具有residue 1结构的音频文件。

Vorbis是一种音频压缩格式,它的相关数据会被封装到一个OGG文件中;OGG则是一种多媒体文件格式。

下图是用010Editor中的ogg.bt模板读取出来的。可以看到这个ogg文件有3个页。后面的分析,都将使用这个下图这个文件,姑且取名为TestOgg;下面的分析过程都是建立在一个正常的ogg样本之上的,这里就是TestOgg。

(更详细的ogg vorbis格式将在下面介绍)

二.环境搭建

系统:Windows7 32位

工具:Visualstudio 2013,一个正常的ogg vorbis音频文件。

因为需要使用到OggVorbis音频文件,而这个文件的格式又比较陌生。那么就需要一个能够帮助我们解析这个文件的工具。去官网发现有一些打包好的工程,和一些编译好的EXE文件。

这个名叫ogginfo .exe的文件就可以静态解析Ogg Vorbis文件

然后下载这些工程,在本地自行配置环境,编译一下ogginfo,成功之后就可以打断点进行调试一步一步分析了。

当然,这个编译过程可能不会成功,会报错,想要解决也行,这里我直接写下我最后成功的操作:

0.官网下载libogg-1.3.3,libvorbis-1.3.5,然后从某hub上下载的vorbis-tools-master。因为从官网下载的这个tools工程有问题,而且没有解决掉。

1.把libogg-1.3.3的include / ogg文件夹,和libvorbis-1.3.5下include / vorbis文件夹放到tools的include下面。

2.编译 libogg_static,出现下面问题,如下解决一下就行。

3.编译vorbis_static,先把libogg-1.3.3的include / ogg文件夹放到include下。成功后得到libvorbis_static.lib,改名为libvorbis.lib即可。

4.然后打开tools工程,把刚刚编译好的libogg_static.lib,libvorbis.lib的路径添加进去。

编译一下ogginfo,成功。

5. 将ogginfo设置为Startup Project,然后给它随便一个ogg文件作为参数就可以进行调试,我使用的TestOgg只有3个ogg页。

6.调试中比较关键的一个函数就是:oggpack_read

按F11跟进这个函数的时候就会提示你选择一个framing.c文件,选择如下这个src目录就行。

那么在解析vorbis的时候关键的函数是:vorbis_synthesis_headerin:

这里会先解析pack的类型,然后按类型来一一解析其中的数据,下面构造poc的时候会详细说明这些结构。

到此静态分析的环境搭建完成了;下面的源码分析和调试都是在这个环境搭建完成的基础上进行的,但是主要目的是帮助构造poc。

三.漏洞函数的分析

漏洞的具体位置在vorbis_book_decodev_add这个函数中,在工程中的具体位置如下:

源码如下:

问题出在高亮部分那段for循环。这个for循环的条件是j dim,里面的操作是把t数组赋给a数组;而a数组的下标则是i,在上一个for循环可以看到i < n。那么,这就存在当book->dim的值大于n时,t数组对a数组的赋值就会超过a数组的正常范围。a数组就在此时越界。

那么,a数组是什么,t数组又是什么?

从图上可以看到a数组是作为一个参数被传递进来的,而t数组是则是通过计算得来的

3.1 a数组

那么这里尝试寻找a数组,看看是从哪里来。

在源码中搜索漏洞函数名字,找到如下的调用,这个叫_01inverse的函数是漏洞函数的上一层。

那么继续寻找_01inverse,来到下面的位置:

红框标出的地方,就是漏洞函数的在这一层定义的结构。需要注意的是,目前要关注的数据是a数组,反应在这里就是float *这个位置的数据。在这个_01inverse的函数里,往下看可以发现下面这一段调用:

float *位置的数据传入的是in[ j] + offset,offset在上面计算了,那现在就是去找in[ j ]。in [ ]这个数组回头看 _01inverse的函数头部:

in[ ]是在第三个参数的位置。那么再回到res1_inverse:

in[ ]虽然在这里有赋值的过程,但不是我们要找的,这里的赋值也只是in自己给自己的数据在赋值,没有实质上的变化,需要找到是in[ ]是从哪里来的。

接下来去找res0_inverse:

residue1_exportbundle:

这次去寻找_residue_P的时候,会找到多个位置。我们要找的是_residue_P -> inverse这样的调用。所以来到如下的位置:

pcmbundle参数就是in[ ],从代码里去搜索pcmbundle。

pcmbundle在这里被分配了空间,它就相当于是in[ ];但是这还没有完。下面有另一处赋值:

这里的pcmbundle[ ]相当于in[ ],所以in[] =vb->pcm[ ]。继续去搜索vb->pcm:

vb->pcm也会搜索到很多地方,但是下图才是正确的位置。因为根据漏洞函数的触发流程只可以知道_mapping_P是会被调用到的一层。

vb->pcm[ ]的大小由vb->pcmend*sizeof(*vb->pcm[i])决定,vb->pcmend的值由ci->blocksizes[vb->W],而ci->blocksizes的数据来自vorbis的第一个头部Identification header中。

所以a数组的大小是可控的。而分配内存的函数_vorbis_block_alloc实际上是_ogg_malloc :

回溯a[ ]的过程,再进一步分析,也就搞清楚出了漏洞触发的过程,整理如下:

3.2 t数组

t [ ]由book->valuelist,entry,book->dim的值计算得到。

由ZDI的公告可以知道book->dim的值来自文件中某一位置的数据。而book->valuelist,entry并不清楚。这里将不回溯去寻找t [ ],只需要明白一点就是通过修改book->dim可以影响到t[ ]的数据,且触发的漏洞的一个条件是book->dim > 8。

四.如何构造POC

在了解漏洞的具体情况之后,下面就该来分析ogg vorbis的格式,构造能触发漏洞的音频文件了。去查看官方文档的说明,可以发现文件的格式不是按byte对齐的,如果不借助前面搭建的环境想要一步一步分析清楚是相当麻烦的一件事。

首先,对ogg文件格式进行介绍。

4.1 OGG

Ogg是以页为单位的,每一个页都有如下的固定结构:

1.Capture_pattern:页标识,是ASCII字符,既:OggS,4字节大小。

2.Stream_structure_version:版本ID,默认为0,1字节大小。

3.header_type_flag:当前页的类型,1字节大小。

可以是:

0×01:表示本页与前一页属于同一个逻辑比特流的同一个Packet;若没有设置,则是一个新的Packet。

0×02:表示本页是逻辑比特流的第一页bos;若没有设置则不是。

0×04:表示本页是逻辑比特流的最后一页eos;若没有设置则不是。

4.granule position:编码的相关参数,8字节大小,可以设置为全0。

5.serial number:当前页的流ID,4字节。

6.page sequence:页面序列号,用来判断页面有无丢失,4字节大小。

7.page checksum:包含头部的页面校验和,4字节。

8.page_segments:segment_table中出现的个数,最大为255,1字节。

9.segmentLen: 记录每个segment_table长度的数组,它是一个数组,大小由segmentable的个数决定。假设只有一个segment_table且长度为0x1E。那该位置就是1字节,且数据是1E。

10.segment_table:段表,存放数据的地方,大小在0-255字节。

需要注意一下的是:page checksum,这个需要根据文件数据的改动而改,不然文件就是一个无法识别的错误文件。

4.2 Vorbis

根据vorbis的标准可知,它有3个标识头,头部之后的所有数据包都是音频数据包。

标识头依次是:identification header,comments header,setup header。

这3个头部都还有一个公共的头:Common header,结构如下:

1) [packet_type]: 包类型, 8字节大小;

01: 表示 identification header

03: 表示 comments header

05: 表示 setup header

2) 0×76,0x6f,0×72,0×62,0×69,0×73:ASCII 字符,既:vorbis,6字节。

4.2.1 Identification header

官方的介绍如下:

1)[vorbis_version]=read32bitsasunsignedinteger

2)[audio_channels]=read8bitintegerasunsigned

3)[audio_sample_rate]=read32bitsasunsignedinteger

4)[bitrate_maximum]=read32bitsassignedinteger

5)[bitrate_nominal]=read32bitsassignedinteger

6)[bitrate_minimum]=read32bitsassignedinteger

7)[blocksize_0]=2exponent(read4bitsasunsignedinteger)

8)[blocksize_1]=2exponent(read4bitsasunsignedinteger)

9)[framing_flag]=readonebit

我用一个实例来对上面的参数进行说明,如下图:

这里可以看到图上有2个OggS头的标识。

图中的蓝色部分:01 76 6F …. 00 B8 01;就是common header + identification header的数据。

packet_type =01

76 6F 72 62 69 73 =vorbis

vorbis_version =00 00 00 00

audio_channels =02

audio_sample_rate =44AC 00 00

bitrate_maximum =00 00 00 00

bitrate_nominal =70 11 01 00

bitrate_minimum =00 00 00 00

blocksize[0–1] =B8

framing_flag =01

这之后就是下一个Ogg的页,这一页可以看到packet_type = 03,说明接着是comments header。

4.2.2 Comments header

同样给出官方的说明:

1)[vendor\_length]=readanunsignedintegerof32bits

2)[vendor\_string]=readaUTF-8vectoras[vendor\_length]octets

3)[user\_comment\_list\_length]=readanunsignedintegerof32bits

4)iterate[user\_comment\_list\_length]times{

[length]=readanunsignedintegerof32bits

thisiteration’susercomment=readaUTF-8vectoras[length]octets

}

5)[framing\_bit]=readasinglebitasBoolean

6)if([framing\_bit]unsetorend-of-packet)thenERROR

7)done.

继续用上面的例子来说明:

这已经是Ogg第二个页了,这里可以看到SegmentLen的长度是14,说明它的大小就是14字节。

蓝色的部分就是common header + comments header:

1、packet_type =01

2、766F 72 62 69 73 =vorbis

3、vendor\_length =1D 00 00 00 表示下面的字符串长度:0x1D= 29 字节

4、vendor\_string =00 58 69 70 … 30 36 32 32 共29字节,表示制作软件信息的字符串。

5、user\_comment\_list\_length=02 00 00 00表示用户注释字符串的个数为 2 ,也就是下面讲有2个字符串。

[user length ]=2B 00 00 00 表示第一个用户注释字符串长度是0x2B = 43。

usercomment:3D43 6F 70 … 6A 65 63 74这一段就是第一个用户注释了。特别的:3D 对应”=”,这个等号用于终止字段名;这里没有字段名。

[user length ]=14 00 00 00表示第二个用户注释字符串长度是0×14 = 20。

usercomment:7469 74 6C … 74 74 65 72第二个用户注释;同理,这里的字段名是“title”;

6、framing\_bit =011字节的Boolean类型数据; 若没有设置则会出错。

到这里Comments header就结束了。但是接下来数据可以看到是05,说明紧接着就是setupheader的结构。

4.2.3 Setup header

这个结构是最关键的部分,也是构造的难点所在。ZDI上所说的type 1 residue encoding结构就包含在这里。

这段数据比较复杂,就不能一位一位来进行说明,先给出这个部分的整体结构

下面将根据我们搭建的ogginfo项目来分析Setup header,同时会对照图中的数据一步一步说明。

1、将断点打在_vorbis_unpack_books,项目中解析的源代码如下图:

这里的截图由于函数过长不能完全显示出来,下面还剩下一个modesettings的结构。

2、打好断点,开始调试。

从图中可以看到程序走到了这个位置,且前面提到的blocksizes[ ]已经被计算出来了,注意这个值是可以影响a[ ]大小的。至于blocksizes[ ]的值可以在第一个vorbis头的解析函数中去查看是如何计算的,如下图:

接着回到刚刚断点的位置:

F11跟进oggpack_read函数:

在内存中查看对应的位置0x00140E83,这里的数据就是Setup header除去vorbis头部的后的部分了;注意红框的bits,这个参数决定了这里的数据从0x00140E83的位置读取多少位;图中bits =8,说明读取1个字节,那么读取的数据就是0x00140E83的第一个字节:0×22。

(前面提到这个oggpack_read函数很重要的原因就是ogg文件的读取不是按字节对齐的;它存在跨字节读取数据,这就使得分析起来比较复杂,所以我才搭建这个project来帮助分析。)

然后下面进行计算,具体计算可自行分析,不再过多介绍。

最终得到的:books = 34 + 1 = 35;

最后有个加一,所以cooks的值不可能设置为0。也就意味着可以把 0×22 改写为 0×00 。

为什么要改写为0?

因为我的目的是构造poc,那么这些结构的数量在保证正确的情况下,数量更小,方便对数据进行组织,出错也更容易修改,且构造出的poc文件更小。

然后跟进vorbis_staticbook_unpack函数:

这里就可以看到s->dim和s->entries的值是从文件什么地方读取的,dim值就是book->dim,但是这个entries不是t[ ]中的entry。

这里为了更好的说明,在windbg下调试最终构造出的poc可以看到:

此时运行到imul edx, eax

在我构造的文件中dim = 0×48,entries = 8。

这里eax = 48存放的就是dim值,edx = 0是entry的值。

3、residue结构

上面说过,漏洞文件出在type 1 residue encoding,那我们略过中间的time,floor结构的构造部分,这里我略过不代表不去构造,而是限于篇幅不再细细分析。

直接来到解析residue结构的位置:

这里有个residue_type的类型判断,所以构造的时候必须是“1”类型;后面可以跟进res0_unpack,这里面就是解析residue的具体结构了。

同理,下面的结构也就不再分析。

到此,对于POC如何构造就结束了。最后提示几点:

(1)、构造poc之前,我用了一个只有3个ogg页的正常的ogg文件来作为基础进行改动。

(2)、需要改动的部分实际上全在ogg第二页。

(3)、改动数据之后要记得修改checksum的值,这个checksum值不是常规的crc32计算出来的,但是我找到了它的crc32计算的源码,和编译好的exe程序。

(4)、在构造setup_header时,codebooks,time ,floor…等这些结构不一定是相邻的。比如:codebooks结构之后是:一部分其他数据+ t[ ]的数据,t[ ]数据之后才是time ,floor的结构。

(5)、在上面静态分析中,我们不可能走到漏洞函数的位置去,也就是说是无法调试漏洞函数。因为漏洞的触发是在音频合成的过程中,这是个动态的过程,上面只是静态的分析数据,也就是判断文件是不是一个正常的ogg vorbis文件而已。

虽然这样做十分的花时间,这也是一个比较笨的办法。不过好处就是清清楚楚的知道漏洞的中每个参数从文件中的什么位置去取得的,进行了怎么样的计算,改动什么位置的数据可以达到想要的目的。最终的构造出的poc也很小。

五.Exploit

这一块篇幅问题,就省略了。

六.POP Cmd

由于沙盒机制的存在,所以先关闭firefox的沙盒。

然后用xammp模拟一下http访问的情况,执行exp,弹出cmd。

七.Final

1.EXP的稳定性

上面所有的情况,都是在我测试的环境中。EXP中用了多次堆喷,在自己的环境中也大概只有50%左右的成功率。

2.alert标签的问题。

测试阶段会加上的alert标签。alert标签去掉之后,exp执行的过程有点不符合预期。查看内存的时候堆喷确实成功喷到了,但搜索内存的代码会出现找不到相关特征值的情况。

Alert标签加上才成功,我有以下的猜想:堆喷中用到了大循环,JS引擎可能会做优化,alert可能会影响优化过程。另一种想法:firefox可能能区分js代码中的dom部分与js部分,然后对这些进行异步执行。

*本文原创作者:Obiit ,本文属于FreeBuf原创奖励计划,未经许可禁止转载返回搜狐,查看更多

责任编辑:

创建ogg文件 c语言,Ogg音频格式文件的样本构造(CVE-2018-5146)相关推荐

  1. python小操作——读取文件夹内的任意格式文件到txt并排序

    概述: 懒得一个一个的对文件进行备注,想着先把所有的文件名放在一起,最后还想着加个序号 实现效果图: 代码: 1.0版本不太简便,以后有时间会对def Test()化简一下,精简一下代码. ####读 ...

  2. 如何将MP4视频文件转换成MP3音频格式

    2018年11月9日,美国漫威影业公司的大作<毒液:致命守护者>开始在中国上映,作为漫威在2018年最后的压轴巨作自然是非常不错的.在影片中很多激斗的场景也有共生体和宿主之间的对话,但是电 ...

  3. php修改音频文件_php如何实现音频格式转换

    php音频格式转换的实现方法:首先把ffmpeg放在phpStudy的WWW目录下:然后把ffmpeg下的bin目录添加到windows的环境变量中:最后通过ffmpeg转换音频格式即可. windo ...

  4. 【R语言 数据读取】R语言读取sav格式文件

    rm(list=ls()) gc()options(scipen = 200)#读取数据集 # haven包读取sav格式文件 library(haven)data <- read_sav(&q ...

  5. 第二十四天学Python:文件(3)XML格式文件的建立,用SAX解析

    尽管北方的春天比南方要晚很多,尽管这里经历两次气温骤降,尽管过程起伏跌宕,但是春天的脚步不会有丝毫地停顿,阵阵花香终会飘入房中. 该来的总会来的. /doge 没啥,就是感慨一下苦等许久的春天终于来了 ...

  6. kgtemp文件转换mp3_amr转换mp3格式文件

    MP3是非常标准的音频数字编码格式.区别于其他格式的优势在于,它可以在不降低音质的前提下,大幅的降低文件的大小.因此MP3格式应用范围极广,我们日常生活中用到的歌曲格式也多为MP3.今天要给大家说的是 ...

  7. r语言读取C盘的csv文件,R语言开发之CSV文件的读写操作实现

    在R中,我们可以从存储在R环境外部的文件读取数据,还可以将数据写入由操作系统存储和访问的文件.这个csv文件应该存在于当前工作目录中,以方便R可以读取它, 当然,也可以设置自己的目录,并从那里读取文件 ...

  8. c语言处理单色bmp文件,C语言处理单色BMP文件

    使用C 语言获得黑白BMP文档的参数而不使用其他函数. 使用C处理单色BMP照片 首先要了解BMP文件的格式: BMP图片的扫描方式 是从左到右,从下至上,对每个像素取值.一个扫描行所占的字节数为4的 ...

  9. html表格打印成pdf格式文件,使用html2pdf打印PDF格式文件中的数据作为PDF文件

    我想从表单文章中获取一些数据并将其输出到PDF2文件中,并在同一页面上使用html2pdf.但是,当试图将表单数据发布到同一页面时,它不会打开PDF文件,这可能是因为PHP文件的其余部分将出现在if( ...

最新文章

  1. graphviz.backend.ExecutableNotFound: failed to execute ['dot', '-Tpdf', '-O', 'Digraph.gv']
  2. android应用资源可以分为两大类,Android 应用资源(一)
  3. java enum(枚举)使用详解 + 总结
  4. 物流行业应用虚拟化解决方案
  5. java抽组件_Java实现的基于模板的网页结构化信息精准抽取组件:HtmlExtractor
  6. 安装Python package
  7. 企业如何抵御弱云密码带来的威胁
  8. matlab 求解 Ax=B 时所用算法
  9. [原]解决百度地图多个Marker和InfoWindow时总是打开最后一个InfoWindow的问题
  10. BZOJ-2768: [JLOI2010]冠军调查(超级裸的最小割)
  11. vue数据未加载完成前显示loading遮罩
  12. 关于深度学习的研究综述
  13. snyk 项目漏洞检测
  14. C# 解析CSV文件
  15. 数据结构与算法复习第一弹(快速排序)
  16. 科目三 流程 记录 LTS
  17. ArithmeticException算数异常
  18. IP代理池检测代理可用性
  19. 企业微信链接适配安卓ios移动端问题汇总
  20. MySQL对数据表进行分组查询(GROUP BY)

热门文章

  1. 线性判别分析(LDA)和她的家人们
  2. DynamicProgramming动态规划整理
  3. 人脑认知科学对人工智能的启示
  4. python123 https://github.com/jackfrued/Python-100-Days/tree/master/Day01-15
  5. matplotlib的安装
  6. 使用linux服务器怎么编译c++
  7. linux 卡在grub_安装Linux系统,Ubuntu时卡在这个界面不动了,有3个小时了。。显示:“正在运行update_grub”......
  8. 人脸对齐--Face Alignment by Explicit Shape Regression
  9. 【Harvest源码分析】GetFilteredSignal函数
  10. java产生死锁的主要原因_详解java中产生死锁的原因及如何避免