函数名前面的指针_一个指针引发的“血案”

5fc459e78187ead945e58c973fa819ad.gif

脚本引擎开发者在设计GC(Garbage Collect,简称GC)时追踪指针不善导致的UAF(Use-After-Free),是一类常见的漏洞。本文通过一个例子来向读者介绍这类漏洞的成因与分析思路。

漏洞描述

CVE-2018-8353是谷歌的Ivan Fratric发现的一个jscript漏洞,该漏洞在2018年8月被修复。这是一个UAF漏洞,Ivan Fratric在披露页清晰地描述了该漏洞的成因:

24ed57e225d69b06e9beb83789a763e2.png

通俗一点说就是RegExp类的lastIndex成员没有被加入GC追踪列表,如果给它赋值,在GC时会导致lastIndex处存储的指针变为悬垂指针。后续再访问lastIndex时,即造成一个典型的Use-After-Free场景。

jscript模块目前已发现多个类似漏洞,例如CVE-2017-11793,CVE-2017-11903,CVE-2018-0866,CVE-2018-0935,CVE-2018-8353,CVE-2018-8653,CVE-2018-8389,CVE-2019-1429

本文试图通过CVE-2018-8353一窥这类漏洞的成因,并在此基础上分析谷歌PoC中的信息泄露利用代码。读者将会看到一个GC导致的UAF如何被转化为高质量的信息泄露漏洞。

PoC

以下为Ivan Fratric给出的PoC,下一小节将通过该PoC分析漏洞成因。

d618977227b76bebbec59b01e83673a3.png

UAF

@0Patch团队已通过补丁分析发现,x86下lastIndex位于RegExpObj对象的+A8偏移处,如下:

c125f531077e56bc7edd3ff347d67334.png

现在RegExpObj::Create函数内下断点,在RegExpObj对象创建完成后,对其偏移+A8处下一个硬件写入断点,这个偏移处存储一个VAR结构体,此结构体在x86下大小为0x10。重点观察+B0处的数据变化。

为了更清晰地解释成因,笔者并没有开启页堆,但开启了用户模式下堆申请的栈回溯,以下为调试日志:

2d73b165d97d4fce24c22bf6b8727b28.png 22ab850517a19ea87aac9a3f137f75e8.png cf1a7c7512c979993db1e85316576f04.png c768d1769cca7de922c976c881f57c5d.png

重占位

到这里已经获得一个非常好的UAF,接下来的问题是:如何使用它?

从调试日志中可以看出,用来存储VAR变量的内存块是从GcBlockFactory::PblkAlloc申请的,x86下其申请大小固定为0x648(《Garbage Collection Internals of JScript》这篇文章有解释为什么x86下这个大小是0x648):

5253e42d62c454f6dfb62363408c4777.png

如果要重用被释放的内存,得在GC后迅速用大小为0x648的内存申请去占用之。如何做到?

一个比较好的方法是借助NameList。jscript对象在创建成员变量时,如果成员变量的名称过长(谷歌的文章中说这个长度阈值为4),会在NameList::FCreateVval函数内单独申请内存,以存储对应的成员变量,并且会以第一个成员名称的长度去申请特定大小的内存,而相关计算公式是固定的。

3f3474d48f50cde8e572a9fd6c18d8e6.png

通过逆向调试,可以得到x86下的计算公式:

cfd7984068957d842cd696f2cdc382cb.png

现在,令alloc_size=0x648,解上述方程,可得到x=0x178(0n376)。于是可以通过下面的代码重用被释放的内存:

3ddc5e1027a8e7484412f60d73b17eeb.png

在调试器中观察验证重用:

261b4b2cc6d19eb4bf937ac19c676fe3.png f256493eb43d108d2218053b5174fff4.png a2377a1ba142558528f47c64a6d6846f.png 39ef01abffad53dd9b37234d27e544da.png

从UAF到信息泄露

前一小节已经在合适的时机控制了被Free的内存,接下来要通过这个UAF漏洞实现信息泄露,以得到被重用内存的起始地址。

NameList::FCreateVval点

NameList::FCreateVval函数内在申请成员变量名内存时,若成员名长度超过一定值,就会额外申请内存去存储这些名称。第一个成员名可以用来控制申请的内存大小,相关计算过程已经在前面说明。后面的成员名称只要长度合适,就可以在第一个成员名称初始化时申请的内存中使用剩余的部分,从而用来布控内存。

在x86环境下,通过逆向NameList::FCreateVval函数,发现每个成员名称前面会额外留0x30大小的空间作为头部,用于初始化各种数据。每次成员名称进行申请时,还会按照下图的计算公式按4字节对齐并保存与返回相关偏移:

296d1c541c446a95040359eac909fb90.png

整个计算公式比较复杂,但设计思路很简单,笔者在这里描述一遍,读者大致了解即可:x86下,第一个成员名初始化时,先申请(2x+0x32)*2+4的内存大小,得到内存后,最初的0x30作为头部使用,用来初始化各种数据,包括本次字符串长度,指向下一个成员名头的指针(这个指针会后面的成员名初始化时被更新),然后因为是第一个成员,按照公式直接加4字节进行对齐,所以从前面的调试日志也可以看到,第一个成员名从+0x34开始被复制。只要第一次申请的内存空间够,第二个成员名按照base+offset+4的方式进行内存地址获取,然后前0x30又是头部,接着再开始复制,以此类推。

泄露被重用内存首地址

接下来是泄露被重用内存的首地址。

由于被重用的内容之前存储着lastIndex引用的VAR数据,所以只要用长度及内容合适的字符串设计类成员名称,就可以控制指定地址处的VAR结构。

从这里开始,使用Ivan Fratric在附件中给出的infoleak.html代码,为便于展示,去除了部分注释:

bc4d93c80a3a5295ed7e1a553a4fda11.png

name1用来申请大小为0x648的内存。name2可调节,用来对齐。name3用来指定类型,以泄露特定偏移处的一个指针,这个后面再会提及。name4用来布控0x1337对应的VAR,用于jscript代码中的条件判断。

上面的小节中只关心了name1,现在开始来具体设计name4,name3,name2。

  • 锁定偏移值

首先得计算垂悬指针指向的VAR结构在被重用内存的偏移值。Ivan Fratric的适配的是x64的版本,原poc在笔者的环境中运行后0x1337对应的i为十进制的115。

x64与x86的原理一致,以x86的版本进行说明。既然x64环境中对应的i为115。x32环境中,也以115为例进行偏移计算。在上述代码中在第115个RegExpObj对象创建时下断点,相关方法在前面UAF小结已经描述,这个偏移很容易计算得到。

笔者的环境中这个偏移每次固定为0x3d8,如下:

a59e64e11316f5561f0c8dd03fffd8b1.png
  • 设计name

现在来设计name,在每个成员名称初始化时,都会有0x30的头部,在这个头部的+0x24处是一个指针(这个指针要到初始化下一个成员名时才会被初始化),指向下一个变量名的0x30头部,下图中字体为红色的即为这些指针。如果能读取其中一个指针,减去其相对内存起始地址的偏移,就可以得到被重用内存的首地址。

下图中字体颜色为橙黄的是被拷贝的成员名称,每个名称最后会多拷贝两个0x00。字体颜色为蓝色的是每个成员名称的实际长度(转化为unicode后的长度)。字体颜色为红色上面已经进行解释。字体背景为灰色的一个个0x30内存区域为name2、name3、name4三个成员名的头部。

字体背景为黄色高亮的区域,实验时发现会与name3的值相同(意思就是给3得3,给5得5)。后面需要借助这个值来读取它后面偏移8字节的一个红色指针。

8db406c40e7bc9b8aa1df16a30c0a9f5.png
  • 最后一个注意点

因为要泄露某个红色指针,所以x86下必须保证这个红色指针之前8字节处的type为long型,这可以通过设计name3来实现。现在的问题是:VAR与某个特定的lastIndex对应起来?

幸运的是,通过调试观察发现,当连续申请VAR结构时,一个个大小为0x10的VAR似乎是从高内存往低内存次第排列。笔者用下图来通俗地解释一下VAR的分布(name2中b的数量被用来调节这里的对齐):

96f4108a74afea098679671c35282a64.png

所以,在x86下,如果找到了0x1337对应的regexps[i].lastIndex,就可以通过读取regexps[i+5].lastIndex来泄露相关指针,减去固定偏移就得到被重用内存的起始地址了。如下:

17d682cf44a9575c6392331aaf8c2a9a.png

到这里已经将这个UAF漏洞转为了信息泄露,泄露出一块(aaa...部分)完全可控的内存的首地址。如果读者之前看过笔者之前的一篇文章,就会明白这里已经将CVE-2018-8353转换为和CVE-2017-11906具有相同功能的信息泄露漏洞。

从信息泄露到RCE

此类信息泄露漏洞与其他堆溢出漏洞一起使用可以实现远程代码执行。笔者将这个漏洞的利用代码稍加改动,并配合CVE-2017-11907一起使用,可以在未打补丁的机器上完成概念验证。

考虑到CVE-2018-8653或CVE-2019-1429这类在野0day的利用方式,应该是用了更高级的利用手法,通过UAF直接实现了任意地址读写,通过单个UAF即可实现远程代码执行,并不需要其他漏洞进行辅助。

此类UAF漏洞后面一定还会出现,请大家做好防范工作。

参考文章

Issue 1587: Windows: use-after-free in JScript in RegExp.lastIndex

Garbage Collection Internals of JScript

团队简介

猎影威胁分析团队是安恒安全研究院一支专注于攻防技术研究的团队,团队由一支擅长攻防技术研究、APT分析、二进制研究的年轻队伍组成。欢迎有志于安全检测、二进制研究和攻防技术研究的小伙伴加入我们团队。

招聘二进制安全研究员

职位描述:

1、在日常可疑文件分析的基础,进行数据挖掘,寻找APT攻击事件;

2、分析客户反馈的可疑文件,编写分析报告,提供解决方案等;

3、负责热门安全事件、最新漏洞的分析,编写分析报告或poc代码等

4、研究新的检测方法,维护和完善APT检测等产品策略

5、协助内部威胁分析平台建设等

职位要求:

1、熟悉windows、Linux上调试手段,能够熟练使用常用逆向分析工具(IDA、WinDbg、OD等);

2、熟悉C/C++、汇编语言,至少熟悉一门脚本编程语言,能快速完成POC代码编写;

3、熟悉病毒、木马通信原理和常用技术以及常见加密算法等;

4、熟悉安全漏洞原理,有独立文档漏洞分析能力;

5、至少1年以上逆向分析、安全研究相关工作经验,能力优先不受工作年限限制;

6、具备大数据挖掘能力,对数据极度敏感,能够快速对数据进行关联分析;

7、思路清晰,善于主动思考,有创新、能独立分析和解决问题,具有良好的沟通能力和团队合作精神;

8、有漏洞分析、病毒木马分析、Web攻防、威胁情报挖掘、反APT攻击、机器学习相关、IOT、ICS等工作经验的优先。

联系方式:

xiaoyi.tu@dbappsecurity.com.cn

往期精选

围观

dcf03e80c036a322cd91be60ab5bf8c8.gif安恒信息荣获国家工业信息安全发展研究中心感谢信

热文

安恒信息2019年“时间账单”

热文

2019年,总有一个安恒瞬间,让你铭记于心

210b2f4f56b7c86e6e70e97424b16ed6.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值