借助历史版本,逐步揭秘不为人知的Adobe Reader调试符号
2020-03-29 10:30:00 Author: www.4hou.com(查看原文) 阅读量:144 收藏

导语:对于发行历史比较悠久的成熟软件,其中的一大资源可能是程序的旧版本或当前支持其他平台的版本。尽管这些旧版本目前对普通用户来说几乎没有用,但其中可能包含对漏洞研究者来说非常宝贵的组件。

前言

由于一些闭源客户端应用程序的源代码和其他辅助工具(例如调试符号)往往是无法直接访问的,这将阻碍研究人员对于客户端应用程序的安全性分析。正因如此,研究人员需要对软件进行完整的黑盒逆向工程,以便更好地了解其内部结构,并重构无法获知的上下文信息,这对于识别安全漏洞、对于崩溃的分类和去重来说是必要的。而逆向的过程可能会非常艰巨,除了测试程序安全性所花费的时间之外,研究人员还要额外花费大量的时间。

而另一方面,研究人员往往会有效利用所有可用的资源。对于发行历史比较悠久的成熟软件,其中的一大资源可能是程序的旧版本或当前支持其他平台的版本。尽管这些旧版本目前对普通用户来说几乎没有用,但其中可能包含对漏洞研究者来说非常宝贵的组件。在许多案例中,应用程序的核心部分往往并没有改变,或者只是略有改变。因此,我们如果能发现任何辅助信息,也许在某种程度上能适用于最新版本。鉴于以上思路,我建议所有安全研究人员在进行复杂的逆向之前首先开展一些“侦查”步骤,通过侦查也许能为以后节省大量时间和精力。

在本文中,我们首先针对1997-2013年之间发布的Unix家族系统中较旧版本的Adobe Reader附带的调试符号进行分析。这里的调试符号将有助于在逆向工程时了解Reader各个组件的内部工作原理,同时有助于在模糊测试的过程中对崩溃进行分类、去重等自动化任务。并且,在对macOS版本进行仔细研究后,我发现实际上有更多的调试元数据已经被放入到公开发行的安装程序之中,其中的一些数据是唯一的(其他符号源中不包含),或者比最新的Unix符号还要更新一些。考虑到这些新数据的价值,我们会在本篇文章的最后与各位读者分享。需要说明的是,我们可以从官方FTP服务器ftp.adobe.com以及HTTP服务器ardownload.adobe.com下载90年代至今的Adobe Reader历史版本。

Adobe Reader调试符号

我们通常需要的特定信息类型是调试符号。顾名思义,调试符号用于帮助开发人员调试应用程序,并且根据其不同类型,可能会显示出函数、枚举和源文件的内部名称,以及完整的函数原型、结构布局和其他有趣的数据。即使是最基本的符号类型(仅包括函数名称)也非常有帮助,因为借助这些可以观察汇编代码每个区域的特定用途,并且可以在对崩溃进行分类的过程中实现栈跟踪。

在Winodws上,Microsoft Visual C++(MSVC)编译器在外部.pdb文件中生成符号,例如:在与Program.exe相同的目录中创建的输出Program.pdb文件。据我所知,Adobe从未发布过与可执行程序和库相对应的pdb文件。较早的编译器还可以选择将DBG格式的符号嵌入到可执行文件中,但是我在Reader中没有发现任何符号,由此看来Windows版本似乎没有包含任何调试符号。

但是,在Linux、macOS和其他UNIX系列系统上,可以将符号直接嵌入可执行文件中,这样就使得厂商在发布已编译的软件时,可能会有意或无意地将符号“共享”出去。值得关注的是,这样的信息已经在社区中传播过一段时间(详情参考@nils 2013年发表的推文,以及Sebastian Apelt 2016年在XFA的演讲)。我们发现,这一点非常值得关注,并且可以更加广泛地让大家了解。

为了能完整描述实际情况,我决定对Adobe Reader各版本的可执行文件和库进行完整分析,我们所分析最早的版本可以追溯到1997年。我选择的组件是一些经常被研究或漏洞利用的组件,包括:Acroread(主程序)、AGM(Adobe图形管理器)、CoolType(打印引擎)、BIB(Bravo Interface Binder)、JP2K(JPEG2000核心库)和rt3d(3D Runtime)。

在以前,Adobe曾经针对各种基于Unix和类Unix的系统发布Reader,例如SunOS、IRIX、OSF/1、HP-UX、AIX和Linux。我们可以从HTTP服务器ardownload.adobe.com或FTP服务器ftp.adobe.com下载软件包的副本。具体而言,相对应的URL是ftp.adobe.com/pub/adobe/reader/unix和ftp.adobe.com/pub/adobe/acrobatreader/unix。在撰写本文时,第二个URL似乎无法访问,但其网站快照可以从https://web.archive.org获得。在其他位置,我们还找到了一些SunOS软件包。在获得了所有3.x版本的软件包之后,我列出了下表,该表中汇总了我的分析结果:

4.png

在2013年发布的5.5版本之后,Adobe Reader不再支持基于Unix的系统。如我们所见,上述所有模块在某些时间点(某些发布版本)中都包含可用的符号。对于CoolType,公共符号的最新版本是2005年的版本。对于其他组件,还存在2013年的最新版本。需要特别说明的是,即便是2005年版本的CoolType也非常有帮助,它帮助我理解了OpenType CharString解释器的内部结构,在2015年发现过一个字体相关的漏洞与之相关。

关于macOS,我最初认为针对该平台,只有JP2K和3D模块曾发布过调试信息。经过更详细的分析后,我发现这个假设是错误的,并且在Mac的Reader 7.x、8.x、9.x和DC版本中也找到了所有其他主要组件的符号。我们将在本文的后面部分重点分析macOS平台的Adobe Reader符号。

如何利用符号

我认为,符号有助于更好和更快地理解代码库,从而帮助我们对软件进行深入分析,或利用代码开展模糊测试。在这种情况下,我们实际上有两种选择:一是在审计和模糊测试期间,定位较旧版本的符号二进制文件,然后尝试在最新版本中重现结果;二是尝试将旧版本符号转移到新库中并对其进行操作。尽管后者听上去更靠谱一些(避免了潜在的误报和漏报),但我发现我们很难在不同时间,针对不同平台和使用不同编译器的两个相似模块之间移植符号。我已经测试过BinDiff和Diaphora。

另外一种选择是使用带有符号版本的并列视图,将名称复制到IDA,并检查特定的函数和对象。如果要对整个库进行这样的检查,往往会花费很多精力。举例来说,Reader中最新的JP2KLib.dll包含3300多个功能。但实际上,我们需要的只是符号中的一小部分。此外,随着我们逐步创建具有大量可识别符号的.idb,我们也可以将这些符号轻松的扩展到每个Patch Tuesday发布的新版本之中,因为相邻版本之间只有很小的变化。

我们来看一个例子。Project Zero Issue #1892是JP2KLib.dll库中的一个堆损坏漏洞。我们在Reader中打开poc.pdf文件后,WinDbg显示以下栈跟踪:

 [...]
JP2KLib!JP2KCopyRect+0x17ce9:
1111cee9 c6040100        mov byte ptr [ecx+eax],0       ds:002b:fff3a008=??
 
 
0:000> k
 # ChildEBP RetAddr 
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0473cb28 1111cfea JP2KLib!JP2KCopyRect+0x17ce9
01 0473cb8c 1111b4ff JP2KLib!JP2KCopyRect+0x17dea
02 0473cbf8 1111898e JP2KLib!JP2KCopyRect+0x162ff
03 0473cd7c 1110d2af JP2KLib!JP2KCopyRect+0x1378e
04 0473cdf0 1110d956 JP2KLib!JP2KCopyRect+0x80af
05 0473ce54 1110dc90 JP2KLib!JP2KCopyRect+0x8756
06 0473ce78 11125e4a JP2KLib!JP2KCopyRect+0x8a90
07 0473ced8 5fafb5be JP2KLib!JP2KImageDecodeTileInterleaved+0x2a
08 0473cf64 5fac449b AcroRd32!AX_PDXlateToHostEx+0x32046e
09 0473d05c 5f9d828d AcroRd32!AX_PDXlateToHostEx+0x2e934b
0a 0473d0a0 089ada8c AcroRd32!AX_PDXlateToHostEx+0x1fd13d
[...]

效果似乎不是太好。在这里,唯一正确识别的符号是JP2KLib!JP2KImageDecodeTileInterleaved,它是导出的函数。但遗憾的是,相同的崩溃无法在macOS上复现,因此我们无法从该位置轻松获得符号化的栈调用,但是我们仍然可以使用AdobeJP2K Mach-O文件在Windows上重新创建符号。我们在IDA中打开JP2KLib.dll和AdobeJP2K,并从JP2KImageDecodeTileInterleaved入口点开始:

5.png

6.png

我们可以清晰地将sub_1004DC58重命名为IJP2KImage::DecodeTile,或者更具体地说,将其重命名为__ZN10IJP2KImage10DecodeTileEiiiiiP14IJP2KImageData,这是该方法的C++名称。我们继续:

7.png

8.png

在这里,有两个重载的IJP2KImage::DecodeTile方法,在我们的例子中,第一个方法在进程崩溃时被调用。我们可以重命名sub_1004D91F并在其中查看:

9.png

10.png

毫无疑问,sub_1004D159是重载的IJP2KImage::DecodeTile的另外一种实现。我们可以对其进行重命名,并继续相同的步骤,直到到达崩溃的位置。在这种情况下,我们必须在Linux符号化版本libJP2K.so的栈跟踪中查找最高级函数的名称,因为它是由macOS构建中的编译器进行内联的。

一旦在数据库中命名了我们感兴趣的所有函数,我们就可以将IDA调试器与WinDbg后端结合使用,以再次在Adobe Reader中打开PoC文件。现在,看起来好多了:

11.png

我们寻找了从IDA导出符号信息的方法,以便可以在WinDbg中直接使用。我们推荐以下几个项目:

FakePDB(https://github.com/Mixaill/FakePDB)

ret-sync(https://github.com/bootleg/ret-sync)

IDA2Sym(https://github.com/siberas/IDA2Sym)

llvm-pdbutil(https://llvm.org/docs/CommandGuide/llvm-pdbutil.html)

arpwn scripts(https://github.com/bitshifter123/arpwn)

遗憾的是,上述都没有完全符合我的期望。其中一些是PoC/Ad-Hoc类型的工具,另外一些会抛出奇怪的错误,另外一些代码需要人工进行修改。在这一方面,似乎有一些工具上的盲区。由于没有找到理想的解决方案,因此我们还是暂时使用IDA调试器。

我们来看看另一个示例,Issue #1888,这次崩溃发生在CoolType.dll中:

CoolType!CTCleanup+0x22e92:
51ebd2a0 89048e          mov dword ptr [esi+ecx*4],eax ds:002b:520d4000=00000000
 
 
0:000> k
 # ChildEBP RetAddr 
WARNING: Stack unwind information not available. Following frames may be wrong.
00 052fc0f0 51ebd214 CoolType!CTCleanup+0x22e92
01 052fc12c 51ebdabd CoolType!CTCleanup+0x22e06
02 052fc16c 51ec8219 CoolType!CTCleanup+0x236af
03 052fc1a0 51e68e68 CoolType!CTCleanup+0x2de0b
04 052fc8c4 51e64051 CoolType!CTInit+0x460e1
05 052fc9a8 51e9e7bb CoolType!CTInit+0x412ca
06 052fcb00 51e9e47f CoolType!CTCleanup+0x43ad
07 052fcb7c 51e769cd CoolType!CTCleanup+0x4071
08 052fcd44 51e7619f CoolType!CTInit+0x53c46
09 052fce14 51e75091 CoolType!CTInit+0x53418
0a 052fd1dc 51e74728 CoolType!CTInit+0x5230a
0b 052fd21c 51e73751 CoolType!CTInit+0x519a1
0c 052fd388 51e732e4 CoolType!CTInit+0x509ca
0d 052fd3dc 52192182 CoolType!CTInit+0x5055d
0e 052fd724 52190fc8 AGM!AGMInitialize+0x69352
0f 052fd884 5215bcd0 AGM!AGMInitialize+0x68198
[...]

同样,栈追踪非常模糊。更糟糕的是,我们没有找到一个明显的入手点,因为AGM.dll调用的第一个CoolType函数不是导出符号。但是,当我们人工查看这些函数的反汇编时,基于我此前对于字体引擎的经验,我们发现CoolType!CTInit+0x460e1实际上是OpenType CharString解释器,它是这个库中最大的函数,其名称为DoType1InterpretCharString。在确定了其中的一个函数之后,就可以向上或向下跟踪调用栈,以尝试匹配其他名称。在这种情况下,我们可以使用Reader 4和Reader 5中的CoolType符号、 Microsoft在DWrite.dll和fontdrvhost.exe映像中使用的符号、Apple在libType1Scaler.dylib库中使用的符号。它们都共享许多相同的OpenType处理代码。

我们对函数进行重命名,并使用poc.pdf在IDA调试器中再次运行Reader。这一次,在发生异常时,我们看到了以下的调用栈:

12.png

如上图所示,我们已经成功重构了大多数CoolType栈的跟踪条目。到这一步为止,我们还不能匹配ATMBuildBitMap的符号,因为它是通过无法跟踪的间接调用来实现调用的。并且,目前还无法识别出任何祖先(Ancestor)函数。尽管如此,对8个最高级别名称进行解码还是非常有帮助的,它可以帮助我们更好地理解受影响的代码,以及该代码为什么无法处理格式错误的数据。

分析核心Acrobat Mach-O映像

以下是macOS版本Reader六个核心组件的细分,其中明确了各组件在每个已发布软件版本中是否都包含符号。为简洁起见,该表格仅从2005年的7.x版本开始列举,在这个版本中,调试信息首次出现:

1.png

如表格注释所述,在8.x和9.x发行版本的子版本之间,某些模块的符号存在较大差异。这两个主要版本的详细表格如下所示:

2.png

显然,Reader Mach-O可执行文件中隐藏了许多有用的信息,其中8.x和9.x版本是调试数据较多的版本。与之前从Unix文件中提取的数据进行对比,我们得到以下信息:

1、3D模块(7.x版本)和CoolType字体引擎(9.x版本)采用stabs格式的专用符号。其中不仅包括函数和全局变量名称,还包括源文件路径、与汇编代码块相对应的源行号、局部变量名称、结构定义、枚举等。这类元数据为我们提供了一些非常详细而准确的信息,有助于掌握代码的工作原理。据我所知,这两个库的私有符号暂时无法从其他公开来源获得,因此这部分内容将帮助对内部工作原理感兴趣的研究人员了解此软件。

2、在8.x和9.x版本中,Acrobat、AGM、CoolType和BIB模块的符号通常比Unix上最新符号版本更新。此外,出于崩溃重复删除或根本原因分析的目的,我们发现在macOS上运行的Reader 9比在SunOS/Solaris上的Reader 9或在AIX/HP-UX上的Reader 7更容易运行,因为前者是更为常见的平台。

3、最后,在DC版本15.006.30033(2015年3月)到15.016.20045(2016年6月)之间,没有分离AGM组件,这也是与此前(从2007年开始,AIX/HP-UX 7.x版本的符号)相比更新的信息来源。

在其中,我们认为私有的CoolType符号可能会附带最为有趣和实用的信息。之所以这么说,是因为它不仅适用于Reader和其他Adobe产品,并且还适用于共享相同字体光栅化代码的所有其他库和系统组件,例如:Windows内核驱动程序(win32k.sys、atmfd.dll)、Windows DirectWrite库(dwrite.dll)、macOS字体库(libTrueTypeScaler.dylib、libType1Scaler.dylib)和JRE t2k字体光栅化程序(libt2k.so等)。接下来,我们将更深入地研究在AdobeCoolType的调试版本中找到的数据类型,尝试对其进行提取,并分析如何有意义地利用这些数据。

深入研究CoolType符号

在各种版本的Reader 9.x中,我们总共发现了17个独特的AdobeCoolType文件,其中包含stabs格式的调试数据。该格式主要是文本格式,因此,如果我们分析文件的末尾,我们将看到类似于以下转储的数据:

parseSeacComponent:f(0,1).h:P(0,17).stdcode:P(0,1).offset:r(0,3)....t1cParse:F(0,1).offset:p(0,3).aux:p(0,27).glyph:p(0,28).h:(0,19).offset:r(0,3).aux:r(0,27).glyph:r(0,28)....t1cDecrypt:F(0,1).lenIV:p(0,1).length:p(2,29).cipher:p(4,36).plain:p(4,36).lenIV:r(0,1).length:r(2,29).r:r(0,9).cnt:(0,3)..c1:(0,11).....t1cUnprotect:F(0,1).lenIV:p(0,1).length:p(2,29).cipher:p(4,36).plain:p(4,36).lenIV:r(0,1).length:r(2,29).single:r(0,11).r1:(0,9).r2:(0,9).cnt:(0,3)..c1:r(0,11).....:t(0,35)=*(0,36).ctlVersionCallbacks:t(0,36)=(2,19)._errstrs.4964.t1cErrStr:F(4,36).errcode:p(0,1).errstrs:V(0,37).errcode:r(0,1)...:t(0,37)=ar(9,27);0;18;(4,36)...

该代码片段展现了来自开源AFDKO库的名称,该库被编译到CoolType中。调试段的其他部分揭示了更多有趣且未被公开过的数据,例如Type1InterpretCharString的详细栈布局。在2015年发布的版本中,主要的Postscript CharString解释器函数存在一些问题:

Type1InterpretCharString:F(5,29).inst:p(38,37).pProcs:p(41,28).pCharDataDesc:p(0,48).pCharIO:p(41,15).pRunData:p(41,13).pPathProcs:p(0,49).pT1Args:p(0,50).etod_path:(38,117).etod_arg:(45,3).status:(0,51).temp_flags:(5,18).did_retry:(5,1)..moveto_lineto_index:r(5,29)...Exception:(13,3)..pPathProcs:(42,6)..substack:(0,52).psstack:(0,53).flexCds:(0,54).hints:(0,55).hint3:(0,56).vStemCount:(5,29).op_stk:(41,10).op_sp:r(5,37).sp:r(38,46).cp:(5,39).wid:(5,39).tempfcd:(5,39).dcp:(5,39).delta:(5,39).i:r(5,15).indx:(5,15).sby:(5,35).dmin:(5,35).subindex:(5,15).tfmLockPt:(0,57).flags:(5,18).accentClipBBox:(5,47).pTempClipBBox:(42,5).prevcp:(5,39).initcp:(5,39).

这里的信息量比较大——在Reader 9.5.5版本的AdobeCoolType文件(6.35MB大小)中,仅各种调试字符串就占据了1.78MB。遗憾的是,IDA Pro和Ghidra似乎没有解析stabs,因此尽管我们在这些反汇编程序中看到了函数和全局名称,但其类型(结构、共用体、枚举、类型定义)和局部变量名称将不会导入。

最终,我成功使用了OS X老版本的objdump和gdb实用程序,后者非常强大。在我的测试环境中,我在MacOS 10.6.8(Snow Leopard)上安装了Adobe Reader 9.5.5版本。然后,在gdb中启动Reader时,看到以下输出:

tests-Mac:~ test$ gdb /Applications/Adobe\ Reader\ 9/Adobe\ Reader.app/Contents/MacOS/AdobeReader
 
[...]
 
(gdb) r
Starting program: /Applications/Adobe Reader 9/Adobe Reader.app/Contents/MacOS/AdobeReader
Reading symbols for shared libraries .+++............................................................................................ done
Reading symbols for shared libraries ...
 
[...]
 
warning: Could not find object file "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/build/MT_Release_Symbols/cooltype5.build/Universal/MT_Release_Symbols.build/Objects-normal/i386/CT_CTS_OpenType.o" - no debug information available for "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/cts/cts_glue/CT_CTS_OpenType.cpp".
 
warning: Could not find object file "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/build/MT_Release_Symbols/cooltype5.build/Universal/MT_Release_Symbols.build/Objects-normal/i386/CT_CTS_StreamFromBuffer.o" - no debug information available for "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/cts/cts_glue/CT_CTS_StreamFromBuffer.c".
 
....... done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries ... done

调试器成功加载了应用程序和嵌入式符号,但提示缺少对象和源文件。如果存在对象和源文件,那么这样的构建版本通常就可以在开发人员的主机上进行调试。此时,我们可以使用以下gdb命令来方便地转储各种类型的常规信息:

info functions:列举在已加载模块中找到的所有函数,或打印有关特定函数的信息

info line:打印源代码文件中特定行的信息

info sources:列举源文件名

info types:列举所有已知类型

info variables:列举所有全局和静态变量名称

这已经提供给我们了很多信息。例如,我们可以回到最关注的函数并进行查找:

(gdb) info functions Type1InterpretCharString
All functions matching regular expression "Type1InterpretCharString":
 
File /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:
IntX Type1InterpretCharString(PFontInst, BCProcs *, T1CharDataDesc *, CharIO *, RunRec *, PathProcs * volatile, T1Args * volatile);
(gdb)

我们可以在其中设置一个断点,并了解它在源代码中的哪一行被声明:

(gdb) b Type1InterpretCharString
Breakpoint 1 at 0x7d853e5a: file /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c, line 7037.
(gdb)

我们可以在特定行上获取更多信息:

(gdb) info line /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:7037
Line 7037 of "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c" starts at address 0x7d853e5a  and ends at 0x7d853e80 .
(gdb)

最终,我们可以检查一种函数参数类型的定义,例如PFontInst结构指针:

(gdb) ptype PFontInst
type = struct _t_FontInst {
    Card16 gridRatio;
    Card16 xAlign;
    Fixed flatnessFactor;
    FixMtx tfmMtx;
    Card32 lenStemSnapV;
    Fixed stemSnapV[48];
    Card32 lenStemSnapH;
    Fixed stemSnapH[48];
   [...]
    Fixed accentHintedWidth;
    Fixed accentCSadjust;
    Fixed accentDSdx;
    Fixed blueStdHW;
    Card32 instructions;
    boolean boostNeeded;
    Card16 boostPhase;
    Card16 fontType;
} *
(gdb) print sizeof(struct _t_FontInst)
$2 = 1020
(gdb)

Issue #1888漏洞分析(CVE-2019-8048)

对全局信息的枚举可能会提供非常有帮助的信息,并能够解决人们在逆向字体光栅化程序过程中可能遇到的许多问题。但是,符号的真正作用在于实时调试期间可用的上先问信息。在之前的章节中,我们尝试利用Issue #1888的PoC触发崩溃并恢复栈跟踪。这是我们得到的结果:

3.png

当我们在macOS Adobe Reader 9.5.5版本中加载相同的PDF时,可以在gdb得到以下内容:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0xff978034
0x7d87e018 in FixOnePath (maxStem=void, cs=0x7d9dbe98, expansionFactor=3932) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:787
787     /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c: No such file or directory.
        in /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c
 
(gdb) bt
#0  0x7d87e018 in FixOnePath (maxStem=void, cs=0x7d9dbe98, expansionFactor=3932) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:787
#1  0x7d87ed67 in GlobalColoring (stems=void, counters=0x0, b3=0xbfffa858, expansionFactor=3932, numCounters=15, numStems=14, pClientHook=0xbfffa764) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:608
#2  0x7d85891b in Type1InterpretCharString (inst=0xd6a704, pProcs=0xbfffa748, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pRunData=0xbfffa728, pPathProcs=0xbfffa544, pT1Args=0xbfffa6c8) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:3874
#3  0x7d83b1d8 in BuildRuns (inst=0xd6a704, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pClipBBox=0x0, pRunData=0xbfffa728, pCallbackProc=0xbfffa748, pClientHook=0xbfffa764, rFlags=33) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/atmbuild.c:384
#4  0x7d83b39c in ATMBuildBitMap (pFontInst=0xd6a704, pCallbackProc=0x7d9d9e24, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pClipBBox=0x0, pBitMap=0xbfffa994, pClientHook=0xb83ab4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/atmbuild.c:625
#5  0x7d73bc9f in ATMCGetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#6  0x7d73b517 in Render::RenderToGlyphMap () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#7  0x7d739d0c in FontInstanceCache::GetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#8  0x7d739239 in FontInstanceCache::GetGlyphMaps () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#9  0x7d738670 in Text::GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#10 0x7d738149 in _CTTextGetTextGlyphs_GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
[...]

现在,这是一个更为详细的调用栈,我们可以看到ATMBuildBitMap的祖先(Ancestors)的名称,并且可以访问几乎完整的类型/本地信息。

Issue #1891漏洞分析(CVE-2019-8042)

再举一个例子,让我们来分析一下Issue #1891中报告的TrueType字体处理中的CoolType崩溃。在Reader中打开poc.pdf时,会出现以下崩溃:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x0a68d4cc
0x7d72a310 in fsg_QueryTwilightElement (pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c:915
915     /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c: No such file or directory.
        in /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c

再一次,我们可以获得非常详细的栈跟踪:

(gdb) bt
#0  0x7d72a310 in fsg_QueryTwilightElement (pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c:915
#1  0x7d729c93 in fs__NewTransformation (inputPtr=0xbfffc040, outputPtr=0xbfffbf70, useHints=1, pFontInst=0x83238d4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fscaler.c:474
#2  0x7d729b19 in fs_NewTransformation (inputPtr=0xbfffc040, outputPtr=0xbfffbf70, pFontInst=0x83238d4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fscaler.c:411
#3  0x7d72997e in SetGlyph (pCharDataDesc=0xbfffc264, inst=0x83238d4, procs=0x7d9d9e24, mem=0xbfffc1c0, in=0xbfffc040, out=0xbfffbf70) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/ttbuild.c:392
#4  0x7d73c331 in TTBuildBitMap (pFontInst=0x83238d4, pCallbackProc=0x7d9d9e24, pCharDataDesc=0xbfffc264, pCharIO=0xbfffc210, pClipBBox=0x0, pBitMap=0xbfffc2c4, pClientHook=0xb67fac) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/ttbuild.c:1128
#5  0x7d73bc9f in ATMCGetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#6  0x7d73b517 in Render::RenderToGlyphMap () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#7  0x7d739d0c in FontInstanceCache::GetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#8  0x7d739239 in FontInstanceCache::GetGlyphMaps () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#9  0x7d738670 in Text::GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#10 0x7d738149 in _CTTextGetTextGlyphs_GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
[...]

如果我们想更加深入地分析问题的根本原因,我们可以从创建未映射的0x0a68d4cc地址来开始。最高级fsg_QueryTwilightElement函数的参数为:

pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0

0x0a68d4cc和0xa69b008的地址非常接近,二者之间仅相差56124个字节,因此我们在猜想,前者是否是基于后者计算的?接下来,我们来分析第二个参数,看看能否找到与现有数据一致的线索:

(gdb) ptype PrivateSpaceOffsets
type = struct fsg_PrivateSpaceOffsets {
    uint32 offset_storage;
    uint32 offset_functions;
    uint32 offset_instrDefs;
    uint32 offset_controlValues;
    uint32 offset_globalGS;
    uint32 offset_FontProgram;
    uint32 offset_PreProgram;
    uint32 offset_TwilightZone;
    uint32 offset_TwilightOutline;
    fsg_OutlineFieldInfo TwilightOutlineFieldOffsets;
} *
(gdb) print *(fsg_PrivateSpaceOffsets *)0x8caad0
$2 = {
  offset_storage = 0,
  offset_functions = 192,
  offset_instrDefs = 1096,
  offset_controlValues = 1096,
  offset_globalGS = 3764,
  offset_FontProgram = 4124,
  offset_PreProgram = 7149,
  offset_TwilightZone = 4294911172,
  offset_TwilightOutline = 4294911220,
  TwilightOutlineFieldOffsets = {
    x = 65304,
    y = 326488,
    ox = 587672,
    oy = 848856,
    oox = 1110040,
    ooy = 1371224,
    onCurve = 0,
    sp = 65296,
    ep = 65298,
    f = 1632408,
    fc = 65300,
    maxPointIndex = 65296
  }
}
(gdb)

这两个结构字段看起来都非常大,它们看起来像是有符号的数字吗?

(gdb) print (int)((fsg_PrivateSpaceOffsets *)0x8caad0)->offset_TwilightZone
$3 = -56124
(gdb) print (int)((fsg_PrivateSpaceOffsets *)0x8caad0)->offset_TwilightOutline
$4 = -56076
(gdb)

我们终于找到了罪魁祸首。如果在反汇编程序中查看fsg_QueryTwilightElement函数,就可以确认这确实是在取消引用之前构造无效指针的方式。现在,我们就可以继续进行分析,以了解为什么这两个字段被分配了超出界外的值,从而掌握导致这一漏洞的根本原因,同时也能向厂商提供一些额外的信息。

总结

正如本次研究过程中所发现的,在进行闭源代码应用程序的安全研究期间,借助调试元数据可以为我们带来意想不到的收获。我希望这些新发现的Reader符号可以在未来简化一些安全研究人员的工作。对我而言,这也有效地表明了,侦查往往是脆弱性研究的一个重要组成部分,通过侦查获得的成果往往会远超出预期。

另一方面,如果Adobe官方能将这些符号预先提供给所有研究人员,那么就不需要再进行侦查的步骤。此前,我们没有讨论过macOS上的调试数据,意味着只有极少数“以攻促防”的安全研究人员才能了解到这一事实。因此,我们倡导闭源代码产品的厂商,通过主动共享符号的方式来改善其软件的可审查性,这一倡议与Microsoft的做法类似。通过这样的方式,可以促进公平竞争,以良好的开放性来确保客户端应用程序的安全性,改变原有晦涩难懂的风格。从长远来看,这将使用户受益。

本文翻译自:https://googleprojectzero.blogspot.com/2019/10/the-story-of-adobe-reader-symbols.html https://googleprojectzero.blogspot.com/2020/01/part-ii-returning-to-adobe-reader.html如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/4Y0k
如有侵权请联系:admin#unsafe.sh