威胁建模:摆脱随机挖洞的命运

威胁

楔子:聊聊Web与二进制安全

经常有人会说“Web安全门槛低,二进制安全门槛高”,先不讨论它是对是错,但至少这让多数人意识到:开始搞Web安全时,xss弹个框,sql注入写个单引号,sqlmap跑一下就拖库了,这种总是令人感觉那么容易(懂这些就敢对外自称“高级渗透测试工程师”的大有人在);搞二进制的就没那么容易出效果了,因为得先学C、汇编,而且一些自动化工具也没web多,就算费九牛二虎之力搞出来,也没黑个站酷炫。

所以,搞Web的人总是比搞二进制的人要多得多。

个人觉得,Web是个前期容易出效果,中后期容易遇瓶颈的安全分支,因为不知道该往哪使力,相当于“前期英雄”;二进制是个前期不容易出效果,且水很深,难达瓶颈的安全分支,因为你很容易知道高山在哪里,即使很多时间只能仰望,相当于“后期英雄”

在搞Web渗透测试时,不知道大家是否有过这种感觉:

  1. 看到别人发的漏洞文章,发现原来这么简单,自己明明有能力发现,之前怎么没挖到呢?
  2. 感觉力不从心,有点“随机挖洞”的感觉。

其实这都是一些技术或方法论上的瓶颈,二进制上也会有“随机挖洞”的感觉的,只是没Web上那么容易碰到,所以拿web举例。

STRIDE:突破瓶颈的方法论

某天在网上看到一篇关于STRIDE威胁建模的文章,这是微软总结出来的一套方法论,其实就是一些威胁的助记符,帮助大家在做安全测试时能够更加全面系统。虽然它也不能100%覆盖所有系统/软件面临的安全威胁,但至少能覆盖个90%。

img

上图是STRIDE所代表的6种威胁,无论在做Web渗透测试,还是软件安全测试,先通过对测试目标做功能上的分析,再按STRIDE模型对其进行全面的分析,最后就是实际测试验证。



image-20180603223947071

STRIDE本质上并不会提高你的技术能力,但可以改变你挖洞的思维方法,更加系统全面,摆脱随机挖洞的命运。

关于STRIDE更详细地信息,可以参考以下这本书《威胁建模:设置和交付更安全的软件》,看前半本就可以了,后半本有点啰嗦,且可操作性不强。

威胁建模

安全圈乱象杂谈

【声明】:本文纯属个人言论,与公司立场无关。

玩名词概念

技术命名,将一些旧知识赋予个新名词进行炒作,比如APT,还有可视化概念,一串串攻击流飞来飞去。

漏洞命名,似乎已成为圈内常态,不管漏洞危害如何,先取个名字,再以名字单独地注册个域名。

BlackHat Pwnine上专门设置有一个“最名不副实漏洞奖”,就是用来批评这类恶意炒作的漏洞。

会议式社交

国内与国外的安全会议的最大区别就是,国内以吃喝聊天、拉关系为主要目的,国外以技术交流为主;

这几年,国内已经有越来越多的安全会议、安全沙龙,每次会前,朋友圈的邀请函就会被刷爆,那些常年占据朋友圈的会棍,我一般都是拉黑再说……

不单是会议,特殊节日,你也会发现朋友圈清一色的各公司的礼品晒照!!!

炒作营销

“世界第一”、“中国第一”、“全球最强”等各种违反广告法的公关稿随处可见,尤以某数字公司最为推崇。

安全圈的营销类标题大体可以分为两种,”碰瓷”(蹭热点、名人,比如Hashcat破解密码之强,连FBI都惊呆了)和”意外”(这文章的标题就是这套路,破解256个字符,意不意外,惊不惊喜),满满是《爆款文案》中的套路:

普通标题:《火辣健身 App 深度产品分析报告》
碰瓷式标题:《同样是健身 App,它比 Keep 到底 NB 在哪儿》
意外式标题:《改了两个版本就融资 1500 万,这款产品如何做到的》

我并不完全反对PR,但我反对那些夸张不符事实的PR,以及惋惜那些以PR带动技术发展的不幸!

快餐式的付费知识

最近好多个平台提供付费知识的功能,算是一项靠名气快速套现的捷径,随便开个课或拉个群收费,几天就可能几十万收入,令人羡慕!

但反观付费的用户,你以为真的付费就一定可以获得知识吗?

对于技术而言,那都是常年积累的过程,非一两小时言语所有传授;
对于知识而言,个人思考所得的才叫知识,而非单纯地从别人口中得到;
对于学习而言,学以致用才是目的,而非纯理论学习。

所以,我不相信靠知识付费,就能够让你的认识、思维、技术有本质提高!

况且,有那么多比付费知识更廉价的书籍(可参考2018年安全从业人员书单推荐),完全可以更系统化地学习。

我不反对知识付费,但是系统性学习比碎片化学习更重要!
我不反对碎片化学习,但别妄想能靠它实现知识速成!

最近在网上看到一个故事,与各位分享下:

爱因斯坦提出相对论后,震惊世界,
于是被很多大学邀请去做报告,
爱因斯坦因此而被弄得疲惫不堪。
有一天,司机对他说:“你太累了,今天我帮你作报告吧?”
爱因斯坦问:“你能行吗?”
司机说:“我闭着眼睛都能背出来。”
那天司机上台,果然讲得滴水不漏。
但刚想下台时,一位博士站了起来,
然后提了一个非常深奥刁钻的问题。
司机不知怎么作答,幸好脑瓜转得快:
“你这问题太简单了,我司机都能回答。”
爱因斯坦站起来,几句话就解决了问题。
博士惊呆了:“没想到他的司机也远胜于我。”
但在回去的路上,司机对爱因斯坦说:
“我知道的只是概念,你懂得的才是知识。”

2018年信息安全从业者书单推荐

知乎上经常有人推荐一些程序员书单,也比较全面,但系统化的安全书单很少,虽然在一些安全新媒体上也有人推荐过一些,但上面不是广告就是有啥新书就推荐啥,甚至让人怀疑作者是不是搞技术的,可能大多没看过自己推荐的书。因此自己结合自己10多年来读过一些觉得比较好的书或业界公认的名著,整理出此份信息安全书单,仅供参考。
后续更新可关注GitHub:https://github.com/riusksk/secbook

计算机行业是否需要学历?

—— 记《软技能:代码之外的生存指南》读后感

最近在读一本写给开发者,但又跟技术、代码无关的书籍叫《软技能:代码之外的生存指南》,主要讲了关于理财、就业、健身、自我营销、精神等诸多软技能的书籍,如果你是个单纯痴迷于技术的IT民工,建议还是看一下。

此书涉及面太广,本文主要就聊下学历方面的话题,估计在知乎上谈得最多的就是要不要考研的问题了。

计算机行业应该算是对学历要求最低的行业之一了,但也不能说完全没用。

事物总是具有两面性,如果只是单纯地说需要或者不需要学历,那都是片面的。

单纯说需要或者不需要学历都是片面的

结果自身的一些经历和中国国情,总结下有无学历的一些优缺点,也欢迎大家补充:

是否考研读博

这个问题只有自己才能找到答案,因为只有自己才知道自己更需要什么。

先说考研读博,关键看你的目的是什么。

比如你想留校从事科研教育工作,或者进入政府银行、中科院等体制内的单位,那高学历几乎是必须的。

像现在招行的招人条件是必须985/211的大学,否则可能连面试机会都没有。

而如果是进体制外的一些企业,比如BAT等互联网公司,研究生的起步薪资比本科生高,但那只是给的辛苦费,并不是对你技术能力的认可。

这些互联网企业一般要求本科以上学历,然后更看重工作经验,比如硕士毕业生是T1.3(助理工程师),而工作3年的本科生,快点是有可能到T3.1(高级工程师),在人脉、技术、薪资上已经不是一个量级了。

至于那些特殊offer的就不在此讨论范围之内了。

也不是说读研没用,或者专科的没机会,腾讯里面也有专科毕业的高工,正如前面所说的,学历不足就得靠工作经验去弥补了,只要你能力出足还是有机会的。

现在AI(人工智能)很火,如果想从事这方面工作,对学术理论研究要求更高,继续求学可能更合适。

现在很多互联网公司,对AI岗位的要求,很多是直接要求博士以上学历的,待遇也很有竞争力。

不过互联网发展如此迅速,AI火个两三年也差不多了,不可能一直如此受热棒的,现在才考虑读博可能就赶不上这波热潮了,这个还需要用发展的眼光来看待。

至于那些害怕就业,要延缓就业压力的,是否考研读博都一样,就业压力都需要面临的。

还有些只是想过来体验人生,你怎么选都是对的^_^。

持续学习才是王道

难道读完研或博士毕业了就不用学习了吗?决定一个人走得多远,关键还是在能否保持持续学习的心态,因此,个人是比较崇尚自学的学习方式,但这要求自律性较高。

贴一张在看雪上看到的帖子,不看下,永远不知道别人有多努力,共勉!

总结

整体而言,拥有学历并不是什么坏事,就中国国情而言,学历还是需要的,至少拥有本科学历会更好,至于是否考研读博,可参考上一条自行选择。

读书心悟

”读史使人明智,读诗使人聪慧,演算使人精密,哲理使人深刻,道德使人高尚,逻辑修辞使人善辩。 “

———— 培根《论读书》

为什么读书?

”你的梦想是什么?“
”长大以后我要当一名科学家。”

相信很多人小时候都有过类似经历,我也如此,而它在当初我幼小的心灵中种下这样一颗种子:读书是为了当一名科学家

上了中学之后,老师和家长都会告诉我们要好好读书,将来才能考上一所好大学,有个好文凭才能找到好工作,以后才能坐办公室,而不用下地干活。此时才意识到,读书是为了有个好文凭,找份好工作,至少不用干苦力活。

大二那年,遇到一位家长,他始终认为读大学也是为了一个文凭,其它都不重要。

大学毕业后,从医学专业转行进入计算机安全行业,发觉那学历文凭于己没有太大用途了,只要是个本科就够了,这也是国情所定。此时才意识到,读书是为了丰富个人修养,是为了拥有一技之长,能够靠它在社会上生存的技能。

后来随着阅历的增长,慢慢意识到,其实读书的目的总结起来就一句话:为了拥有更多的选择权

正如龙应台在《亲爱的安德烈》中所述说的那样:

孩子,我要求你读书用功,不是因为我要你跟别人比成绩,
而是因为,我希望你将来会拥有选择的权利,
选择有意义、有时间的工作,
而不是被迫谋生。
当你的工作在你心中有意义,
你就有成就感。
当你的工作给你时间,
不剥夺你的生活,
你就有尊严。
成就感和尊严,给你快乐。


读完即忘,为何仍要读书?

也许经常有人会问,书读完就忘记了,那读书还有什么用呢?

如果是我,我会这样回答:”每日三餐吃完又拉,为何又还要吃饭呢?也许吃几天饭没能看出什么变化,但小孩子吃到成年呢?那变化够明显吧。读书亦是如此,短期看不出变化,但长期它会慢慢变成我们血肉的一部分,相伴一生!

王阳明《传习录》中有一段话:

一友问:“读书不记得,如何?”
先生曰:“只要晓得,如何要记得?要晓得已是落第二义了,只要明得自家本体。若徒要记得,便不晓得;若徒要晓得,便明不得自家的本体。”

深以为然,其实读书不用非得记得,能够理解书中道理即可。非得背诵强记,往往都是用来应付考试的手段。

《传习录》中说到,”如读书时,良知知得强记之心不是,即克去之;有欲速之心不是,即克去之;有夸多斗靡之心不是,即克去之。“

若能以不为考试、不为显摆、不为私欲,只以无心之心而读书,则足矣!

读书方法

春节刚好读完一本叫《如何阅读一本书》,虽没想像中那般好,但书中所提的4种阅读层次还是可以当作参考的。

  • 基础阅读:小学至初中的语文功底就足够了,即能够识字,读懂字面意思即可。

  • 检视阅读:换言之,即快速阅读,该层次可分为2种方法。
    1、系统化略读:比如通过书名、目录、序言、作者介绍、随手阅读几个段落等方式来判断该书的分类、主题以及内容质量,我一般就是通过此方式来判断买不买书的。对于一些专业书籍,就需要一定的知识背景才能有更好地做出判断。
    2、快速性通读:即不求甚解地快速通读全书,若有必要,再有侧重地细读一遍。

  • 分析阅读:原书写得有点复杂,个人觉得没必要,只要能够分析清楚书本的主题内容、全书结构、理解书中观点或原理,带着问题去书中求解,看能从中找到答案吗,读完之后能够准确地评价该书,这就差不多了。

  • 主题阅读:最高层次的阅读方式,根据同一主题内容去阅读多本同类书籍,因此也叫比较阅读。往往运用在主动研究某个领域所采取的方法。

比如想研究移动安全,我可能会去china-pub上查找相关书籍,通过检视阅读的方式去挑选出一些相关的安全书籍,然后就是采用分析阅读的方式一本本读下去,必要的话做一些笔记,完成之后也即是完成主题阅读方式了。可以看到,越高级的阅读层次其实已经包含了更低级层次的阅读方式。

为了规划自己的读书计划,我会用iPhone上的”提醒事项“这个app来记录和规划自己每月的阅读书单,当然其它一些需要记录提醒事项也会一并写上。一来可以方便随时记录想看的好书;二来可以提醒自己。

后记

中小学时代,读书大多是为了考试,总是有些不情不愿的,所以那时并不是那么喜欢读书。

上大学之后,由于兴趣开始自学计算机,记得当时整个暑假就拿着《深入理解计算机系统》和《ASP从入门到精通》两本书每天不停地学习,后来也会找一些社科历史等文科书籍来阅读。

渐渐地才意识到,其实我是喜欢读书的,只是被应试教育所毒害过。

所以能够以兴趣为导向,无欲无求,以无心之心而读书,或许才是最佳的读书状态。

附录

1、《购书心得》:关于信息安全方面的相关书籍推荐

2、《给计算机初学者的书籍推荐》:给初学计算机专业的同学一些基础课相关的经典书籍推荐。

2017年刷过的CVE

序号 公司 产品 时间 类型 CVE号
1 Adobe Acrobat Reader 2017/02/16 Heap Overflow CVE-2017-2959
2 Foxit Foxit Reader 2017/03/02 Memory Corruption CVE-2017-5989
3 Apple macOS/iOS 2017/03/28 Memory Corruption CVE-2017-2407
4 Apple macOS/iOS 2017/03/28 Memory Corruption CVE-2017-2406
5 Apple macOS/iOS 2017/03/28 Out-of-Bounds CVE-2017-2487
6 Apple macOS/iOS 2017/03/28 Denial of Service CVE-2017-2417
7 Apple macOS/iOS 2017/03/28 Heap Overflow CVE-2017-2379
8 Adobe Acrobat Reader 2017/04/12 Memory Corruption CVE-2017-3039
9 Adobe Acrobat Reader 2017/04/12 Out-of-Bounds CVE-2017-3040
10 Adobe Adobe Digital Editions 2017/06/14 Memory Corruption CVE-2017-3096
11 Adobe Adobe Digital Editions 2017/06/14 Stack Overflow CVE-2017-3095
12 Adobe Adobe Digital Editions 2017/06/14 Stack Overflow CVE-2017-3094
13 Adobe Adobe Digital Editions 2017/06/14 Out-of-Bounds CVE-2017-3093
14 Apple macOS 2017/07/20 Stack Overflow CVE-2017-7033
15 Apple macOS 2017/07/20 Out-of-Bounds CVE-2017-7016
16 Apple macOS 2017/07/20 Out-of-Bounds CVE-2017-7015
17 Adobe Adobe Digital Editions 2017/08/09 Memory Corruption CVE-2017-11280
18 Adobe Acrobat Reader 2017/08/09 Memory Corruption CVE-2017-3016
19 Apple Xcode 2017/09/20 Memory Corruption CVE-2017-7137
20 Apple Xcode 2017/09/20 Memory Corruption CVE-2017-7136
21 Apple Xcode 2017/09/20 Memory Corruption CVE-2017-7135
22 Apple Xcode 2017/09/20 Memory Corruption CVE-2017-7134
23 Apple Xcode 2017/09/20 Memory Corruption CVE-2017-7076
24 Adobe Adobe Digital Editions 2017/11/15 Memory Corruption CVE-2017-11301
25 Adobe Acrobat Pro DC 2017/11/15 Memory Corruption CVE-2017-16394
26 Adobe Acrobat Pro DC 2017/11/15 Memory Corruption CVE-2017-16395
27 Adobe Acrobat Pro DC 2017/11/15 Memory Corruption CVE-2017-16399
28 Adobe Acrobat Pro DC 2017/11/15 Memory Corruption CVE-2017-16411
29 Adobe Acrobat Pro DC 2017/11/15 Memory Corruption CVE-2017-16410
30 Adobe Acrobat Pro DC 2017/11/15 Memory Corruption CVE-2017-16409
31 Adobe Acrobat Pro DC 2017/11/15 Memory Corruption CVE-2017-16408
32 Adobe Acrobat Pro DC 2017/11/15 Memory Corruption CVE-2017-11293

调试V8中JS对应的汇编代码

1、调试未优化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
gdb --args ~/v8/out/ia32.debug/d8 --print-code poc.js
bp v8::internal::FullCodeGenerator::MakeCode
run
c
finish
//跟c命令配对执行多次,直至打印出需要的汇编指令
Run till exit from #0 v8::internal::FullCodeGenerator::MakeCode (info=0xff873eb8) at ../src/full-codegen.cc:283
--- Raw source ---
……

--- Code ---
source_position = 197
kind = FUNCTION
name = evil_object.toJSON
Instructions (size = 128)
0x2b3301a0 0 55 push ebp
0x2b3301a1 1 89e5 mov ebp,esp
……
bp * 0x2b3301a0

2、调试优化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
gef>  bp v8::internal::Context::AddOptimizedFunctiongef>  run Thread 1 "d8" hit Breakpoint 1, v8::internal::Context::AddOptimizedFunction (this=0x1fe2a1083a79, function=0x1fe2a10ac281) at ../src/contexts.cc:413gef>  job function

0x1fe2a10ac281: [Function]
- map = 0xe7a59804099 [FastProperties]
- prototype = 0x1fe2a1084069
- elements = 0x367f8ed82241 <FixedArray[0]> [FAST_HOLEY_ELEMENTS]
- initial_map =
- shared_info = 0x1fe2a10abdd9 <SharedFunctionInfo Ctor>
- name = 0x1fe2a10abae9 <String[4]: Ctor>
- formal_parameter_count = 0
- context = 0x1fe2a1083a79 <FixedArray[188]>
- literals = 0x1fe2a10ac551 <FixedArray[1]>
-
code = 0x3e4da55066a1 <Code: OPTIMIZED_FUNCTION>
- properties = {
#length: 0x367f8edd2709 <AccessorInfo> (accessor constant)
#name: 0x367f8edd2779 <AccessorInfo> (accessor constant)
#arguments: 0x367f8edd27e9 <AccessorInfo> (accessor constant)
#caller: 0x367f8edd2859 <AccessorInfo> (accessor constant)
#prototype: 0x367f8edd28c9 <AccessorInfo> (accessor constant)
}

gef> job 0x3e4da55066a1
0x3e4da55066a1: [Code]
kind = OPTIMIZED_FUNCTION
stack_slots = 5
compiler = crankshaft
Instructions (size = 218)
0x3e4da5506700 0 55 push rbp
0x3e4da5506701 1 4889e5 REX.W movq rbp,rsp
0x3e4da5506704 4 56 push rsi
0x3e4da5506705 5 57 push rdi
……
gef> bp *0x3e4da5506700
gef> c

Continuing.

Thread 1 "d8" hit Breakpoint 2, 0x00003e4da5506700 in ?? ()
……
code:i386:x86-64
0x3e4da5506700 push rbp $pc
0x3e4da5506701 mov rbp, rsp
0x3e4da5506704 push rsi
0x3e4da5506705 push rdi
0x3e4da5506706 sub rsp, 0x8
0x3e4da550670a mov rax, QWORD PTR [rbp-0x8]

CVE-2016-7595 Apple macOS/iOS CoreText OTL::GPOS::ApplyPairPos 越界访问漏洞分析

前言

2016年12月的Apple安全公告中(macOS公告iOS公告),修复4个由腾讯安全平台部终端安全团队报告的漏洞,其中有2个是字体解析造成的越界访问漏洞,影响 macOS/iOS/watchOS/tvOS等多个平台系统,本文主要分析其中的 CVE-2016-7595 字体漏洞【图1】。



图1

这个漏洞在报给Apple 17天后发布 macOS 10.2.2 测试版补丁,一个半月后发布安全公告和补丁(包括iOS、watchOS和tvOS),难得看见苹果这么积极一次。

###漏洞分析
此次漏洞是通过Fuzzing发现的,直接对比poc与原始文件的数据,可以发现其实就1个字节的差异(0x00 => 0x6C)【图2】:



图2

用ttx命令分析字体格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
╭─riusksk@MacBook  ~/Downloads ‹›
╰─➤$ ttx poc.ttf
Dumping "poc.ttf" to "poc#1.ttx"...
Dumping 'GlyphOrder' table...
Dumping 'head' table...
Dumping 'hhea' table...
Dumping 'maxp' table...
Dumping 'OS/2' table...
Dumping 'hmtx' table...
Dumping 'cmap' table...
Dumping 'fpgm' table...
Dumping 'prep' table...
Dumping 'cvt ' table...
Dumping 'loca' table...
Dumping 'glyf' table...
Dumping 'name' table...
Dumping 'post' table...
Dumping 'gasp' table...
/usr/local/lib/python2.7/site-packages/FontTools/fontTools/ttLib/tables/otTables.py:60: UserWarning: Coverage table has start glyph ID out of range: glyph27713.
warnings.warn("Coverage table has start glyph ID out of range: %s." % start)
An exception occurred during the decompilation of the 'GPOS' table
Dumping 'GPOS' table...
Dumping 'GSUB' table...
Dumping 'DSIG' table...

从上面的提示可以看出,是在解析GPOS表时,通过glyphIDCoverage表索引时导致越界了,其中glyph27713的数值正是0x6C41(27713),也就是上面图1中文件对比的差异值。虽然这是FontTools工具的错误,不代表Apple系统本身,但它跟Apple系统导致崩溃的是同一字节,从这可以直接得到导致崩溃的关键字节是glyphID值。

GPOS表

TrueType/OpenType字体格式中的GPOS表是用于为字体中文本布局及渲染提供glyph位置信息的表,表中各个字体结构如图3所示:



图3

GPOS表主要包含3个子表:ScriptList、FeatureList和LookupList,本次漏洞主要问题在LookupList子表中的PairAdjustmentPositioning中,PairAdjustmentPositioning子表(PairPos)被用于调整两个glyphs彼此之间的位置。

PairPos表下又包含多个PairSet数组,PairSet数组包含Coverage表中每个glyph对应的偏移量,并按Coverage Index来排序。

PairSet下包含PairValueRecord指定每一glyph配对(pair)中的第二个glyph(SecondGlyph)的glyph名和索引值GlyphID(对应【图3】中glyphRefID),同时包含两个ValueRecord值去指定第一个glyph和第二个glyph的位置信息。

导致越界的漏洞正是用于索引的GlyphID(glyphRefID),用ttx解析原有正常字体文件生成的xml文件,如图4所示,index=”65”就是正常GlyphID值 00 41,如果随便给第1字节设置个值都会导致崩溃。



图4

调试

用lldb调试下,崩溃后的地址及栈回溯如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
(lisa)run poc.ttf 
Process 96714 launched
Process 96714 stopped
* thread #1: tid = 0x3f119d, 0x00007fffa7c01491 CoreText`OTL::GPOS::ApplyPairPos(OTL::LookupSubtable const*, TGlyphIterator&, OTL::Coverage const&) const + 411, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3007ddfae)
frame #0: 0x00007fffa7c01491 CoreText`OTL::GPOS::ApplyPairPos(OTL::LookupSubtable const*, TGlyphIterator&, OTL::Coverage const&) const + 411
CoreText`OTL::GPOS::ApplyPairPos:
-> 0x7fffa7c01491 <+411>: mov ax, word ptr [r14 + 2*rax + 0xa]
0x7fffa7c01497 <+417>: rol ax, 0x8
0x7fffa7c0149b <+421>: movzx eax, ax
0x7fffa7c0149e <+424>: lea rsi, [r14 + rax]
(lisa)register read rax
rax = 0x00000000ffffffff
(lisa)x $r14+2*$rax+0xa
error: memory read failed for 0x3007c5600

(lisa)bt
* thread #1: tid = 0x3f119d, 0x00007fffa7c01491 CoreText`OTL::GPOS::ApplyPairPos(OTL::LookupSubtable const*, TGlyphIterator&, OTL::Coverage const&) const + 411, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3007ddfae)
* frame #0: 0x00007fffa7c01491 CoreText`OTL::GPOS::ApplyPairPos(OTL::LookupSubtable const*, TGlyphIterator&, OTL::Coverage const&) const + 411
frame #1: 0x00007fffa7c05907 CoreText`OTL::GPOS::ApplyLookupSubtable(OTL::Lookup const&, unsigned int, OTL::LookupSubtable const*, TGlyphIterator&, OTL::Coverage const&) const + 85
frame #2: 0x00007fffa7c02c7b CoreText`OTL::GPOS::ApplyLookupAt(OTL::Lookup const&, TGlyphIterator&) const + 339
frame #3: 0x00007fffa7b68ff4 CoreText`OTL::GPOS::ApplyLookups(TRunGlue&, int, OTL::GlyphLookups&) const + 448
frame #4: 0x00007fffa7b68897 CoreText`TOpenTypePositioningEngine::PositionRuns(SyncState&, KerningStatus&) + 839
frame #5: 0x00007fffa7b66d05 CoreText`TKerningEngine::PositionGlyphs(TLine&, TCharStream const*) + 347
frame #6: 0x00007fffa7bbe59d CoreText`TTypesetter::FinishLayout(std::__1::tuple<TLine const*, TCharStream const*, void const* (*)(__CTRun const*, __CFString const*, void*), void*, std::__1::shared_ptr<TBidiLevelsProvider>*, unsigned int, unsigned char> const&, TLine&, SyncState) + 35
frame #7: 0x00007fffa7b5586d CoreText`TTypesetterAttrString::Initialize(__CFAttributedString const*) + 865
frame #8: 0x00007fffa7b552ea CoreText`CTLineCreateWithAttributedString + 59
frame #9: 0x00007fffb8be086e UIFoundation`__NSStringDrawingEngine + 10669
frame #10: 0x00007fffb8be69ca UIFoundation`-[NSAttributedString(NSExtendedStringDrawing) boundingRectWithSize:options:context:] + 605
frame #11: 0x00007fffb8bdcc43 UIFoundation`-[NSAttributedString(NSStringDrawing) size] + 59
frame #12: 0x0000000100047d70 Font Book`___lldb_unnamed_symbol1053$$Font Book + 368
frame #13: 0x00000001000476e9 Font Book`___lldb_unnamed_symbol1052$$Font Book + 89
frame #14: 0x00000001000475b0 Font Book`___lldb_unnamed_symbol1049$$Font Book + 774
frame #15: 0x000000010006a2d2 Font Book`___lldb_unnamed_symbol1860$$Font Book + 110
frame #16: 0x000000010005d4ee Font Book`___lldb_unnamed_symbol1545$$Font Book + 2651
frame #17: 0x00007fffa46b2451 AppKit`-[NSApplication _doOpenFile:ok:tryTemp:] + 253
frame #18: 0x00007fffa427f789 AppKit`-[NSApplication finishLaunching] + 1624
frame #19: 0x00007fffa427ed2a AppKit`-[NSApplication run] + 267
frame #20: 0x00007fffa4249a8a AppKit`NSApplicationMain + 1237
frame #21: 0x0000000100001527 Font Book`___lldb_unnamed_symbol1$$Font Book + 11
frame #22: 0x00007fffbb632255 libdyld.dylib`start + 1
frame #23: 0x00007fffbb632255 libdyld.dylib`start + 1

[r14 + 2*rax + 0xa]索引错误,很典型的数组越界指令。

通过设置条件断点去记录获取的GlyphID以及后面的将其传参给OTL::Coverage::SearchFmt2Binary函数后返回值,可以发现最后当GlyphID=0x55(85)时返回值0,最后触发崩溃,所以样本中的GlyphID只要>=0x55都会导致崩溃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(lisa) p "GlyphID" $eax
(unsigned int) $95 = 85
(lisa) c
Process 25648 resuming
Command #2 'c' continued the target.
(lisa) p "SearchFmt2Binary参数2" $esi
(unsigned int) $96 = 85
(lisa) c
Process 25648 resuming
Command #2 'c' continued the target.
(lisa) p "返回值" $eax
(unsigned int) $97 = 0
(lisa) c
Process 25648 resuming
Command #2 'c' continued the target.
Process 25648 stopped
* thread #1: tid = 0x43fc6, 0x00007fffcb443491 CoreText`OTL::GPOS::ApplyPairPos(OTL::LookupSubtable const*, TGlyphIterator&, OTL::Coverage const&) const + 411, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3007bc6fe)
frame #0: 0x00007fffcb443491 CoreText`OTL::GPOS::ApplyPairPos(OTL::LookupSubtable const*, TGlyphIterator&, OTL::Coverage const&) const + 411
CoreText`OTL::GPOS::ApplyPairPos:
-> 0x7fffcb443491 <+411>: mov ax, word ptr [r14 + 2*rax + 0xa]
0x7fffcb443497 <+417>: rol ax, 0x8
0x7fffcb44349b <+421>: movzx eax, ax
0x7fffcb44349e <+424>: lea rsi, [r14 + rax]

返回值为0时,经dec减1后为0xFFFFFFFF,以此为索引值,最后导致越界访问。

1
2
dec     eax     ; eax=0xFFFFFFFF
mov ax, [r14+rax*2+0Ah] ; 越界访问,导致崩溃

漏洞修复

苹果已经发布安全补丁,macOS用户可升级到10.12.2,iOS用户可升级到10.2。

对补丁进行比对,可以发现在漏洞函数OTL::GPOS::ApplyPairPos 中添加了判断【图5】,获取到的GlyphID值传递给OTL::Coverage::SearchFmt2Binary函数,当查找失败时会返回0,因此只要添加判断返回值是否为0,为0则直接跳走返回。



图5

处理流程

  1. 2016-10-23 通过邮件提交给Apple
  2. 2016-11-09 Apple确认漏洞,并在 macOS Sierra 10.12.2 beta 测试版中修复
  3. 2016-12-09 分配CVE号:CVE-2016-7595
  4. 2016-12-14 Apple发布安全公告,并推送补丁