ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【转】PDF文件格式:基本结构【2020年更新】

2022-04-08 14:00:18  阅读:252  来源: 互联网

标签:包含 对象 文件格式 2020 引用 PDF 文档 我们


       转自《https://resources.infosecinstitute.com/topic/pdf-file-format-basic-structure/》

 

我们都知道有很多攻击是攻击者在 PDF 文档中包含一些 shellcode。该 shellcode 在如何分析 PDF 文档并将其呈现给用户以在目标系统上执行恶意代码时使用了某种漏洞。

下图显示了在流行的 PDF 阅读器 Adob​​e Acrobat Reader DC 中发现的漏洞数量,该阅读器于 2015 年发布,成为 2017 年 10 月结束对 Acrobat XI 的支持后唯一支持的 Acrobat Reader 版本。漏洞数量越来越多这些年。最重要的漏洞是代码执行漏洞,攻击者可以利用该漏洞在目标系统上执行任意代码(如果 Acrobat Reader 尚未修补)。

图 1: Adobe Acrobat Reader DC 漏洞

这是我们应该定期更新 PDF 阅读器的一个重要指标,因为最近发现的漏洞数量相当惊人。

PDF文件结构

每当我们想发现软件中的新漏洞时,我们应该首先了解我们试图发现新漏洞的协议或文件格式。在我们的案例中,我们首先应该详细了解 PDF 文件格式。在本文中,我们将了解 PDF 文件格式及其内部结构。

PDF 是一种可移植的文档格式,可用于呈现包含文本、图像、多媒体元素、网页链接等的文档。它具有广泛的功能。PDF 文件格式规范在此处公开提供,任何对 PDF 文件格式感兴趣的人都可以使用。仅 PDF 文件格式就有将近 800 页的文档,因此阅读这些文档并不是一时兴起的事情。

PDF 的功能不仅仅是文本:它可以包含图像和其他多媒体元素、受密码保护、执行 JavaScript 等等。PDF文件的基本结构如下图所示:

图 2: PDF 结构

每个 PDF 文档都包含以下元素:

标题

这是 PDF 文件的第一行,指定文档使用的 PDF 规范的版本号。如果我们想找出答案,我们可以使用十六进制编辑器或简单地使用xxd命令,如下所示:

[普通]
#xxd temp.pdf | 头-n 1
0000000:2550 4446 2d31 2e33 0a25 c4e5 f2e5 eba7 %PDF-1.3.%……
[/plain]

temp.pdf PDF 文档使用 PDF 规范 1.3。'%' 字符是 PDF 中的注释,因此上面的示例实际上表示第一行和第二行是注释,对于所有 PDF 文档都是如此。以下字节取自以下输出:2550 4446 2d31 2e33 0a25 c4e5,对应于 ASCII 文本“%PDF-1.3.%”。下面是一些使用不可打印字符的 ASCII 字符(注意“.”点),它们通常用于告诉一些软件产品该文件包含二进制数据,不应被视为 7 位 ASCII文本。当前版本号的格式为 1.N,其中 N 的范围为 0-7。

身体

在 PDF 文档的正文中,有一些对象通常包括文本流、图像、其他多媒体元素等。正文部分用于保存向用户显示的所有文档数据。

外部参照表

这是交叉引用表,其中包含对文档中所有对象的引用。交叉引用表的目的是允许随机访问文件中的对象,因此我们不需要阅读整个 PDF 文档来定位特定对象。每个对象由交叉​​引用表中的一个条目表示,该条目总是 20 字节长。让我们举个例子:

[普通]
XREF
0 1
0000000023 65535 F
3 1 0000025324
00000 N 00000000332 00001 00000034 00001 00003 4 0000025518 00002
4
0000025518 00002 000002500 0011 000000003324 00000201025001 000031 0000025001 00001 000000000000 0011 00003 4 0000025518 000000 00000000332 00001 0000000024 00001 0000000024 0011 0000002025001 000010N [/普通]





我们可以通过简单地使用文本编辑器打开 PDF 并滚动到文档底部来显示 PDF 文档的交叉引用表。在上面的示例中,我们可以看到我们有四个小节(请注意仅包含两个数字的四行)。这些行中的第一个数字对应于对象编号,而第二行表示当前小节中的对象数量。每个对象由一个条目表示,该条目长 20 个字节(包括 CRLF)。

前 10 个字节是对象从 PDF 文档开头到该对象开头的偏移量。接下来是一个空格分隔符,另一个数字指定对象的世代号。之后,还有另一个空格分隔符,后跟一个字母“f”或“n”,表示对象是空闲还是正在使用。

第一个对象的 ID 为 0,并且始终包含一个生成编号为 65535 的条目,该条目位于空闲对象列表的头部(注意字母“f”表示空闲)。交叉引用表中的最后一个对象使用代号 0。

第二小节的对象 ID 为 3 并包含一个元素,即对象 3 从文档开头的偏移量 25324 字节处开始。第三小节有四个对象,其中第一个对象的 ID 为 21,从文件开头的偏移量 25518 开始。其他对象的后续编号为 22、23 和 24。 

所有对象都标有“f”或“n”标志。标志“f”表示该对象可能仍然存在于文件中,但被标记为空闲,因此不应使用它。这些对象包含对下一个空闲对象的引用以及对象再次变为有效时要使用的代号。标志“n”用于表示有效且已使用的对象,其中包含距文件开头的偏移量和对象的世代号。

请注意,对象 0 指向表中的下一个空闲对象,即对象 23。由于对象 23 也是空闲的,因此它本身指向表中的下一个空闲对象,即对象 24。但对象 24 是表中的最后一个空闲对象。文件,所以它指向对象零。如果我们用每个对象编号来表示上述交叉引用表,它将如下所示:

[平原]
外部参照
0 1
0000000023 65535˚F
3 1
0000025324 00000Ñ
21 1
0000025518 00002Ñ
22 1
0000025632 00000Ñ
23 1
0000000024 00001˚F
24 1
0000000000 00001˚F
36 1
0000026900 00000Ñ
[/纯]

当对象被释放时,对象的代号会增加,因此如果对象再次变为有效(将标志从“f”更改为“n”),代号仍然有效,而无需增加它。对象 23 的代号为 1,所以如果再次生效,代号仍为 1,但如果再次删除,代号将增加到 2。

已增量更新的 PDF 文档中通常存在多个小节,否则应仅存在一个以数字 0 开头的小节。

预告片

PDF 预告片指定读取 PDF 文档的应用程序应如何找到交叉引用表和其他特殊对象。所有 PDF 阅读器都应该从文件末尾开始阅读 PDF。下面给出了一个示例预告片:
预告片
<<
/Size 22
/Root 2 0 R
/Info 1 0 R
>>
startxref
24212
%%EOF
PDF 文档的最后一行包含“%%EOF”文件字符串的结尾。在文件标记的结尾之前,有一行带有startxref字符串,它指定从文件开头到交叉引用表的偏移量。在我们的例子中,交叉引用表从偏移量 24212 字节开始。在那之前是一个预告片字符串,它指定预告片部分的开始。预告部分的内容嵌入在 << 和 >> 字符中(这是一个接受键值对的字典)。 

我们可以看到预告片部分定义了几个键,每个键都用于特定的操作。预告片部分可以指定以下键:

  • /Size [整数]:指定交叉引用表中的条目数(也计算更新部分中的对象)。使用的数字不应该是间接参考。
  • /Prev [integer]:指定从文件开头到上一个交叉引用节的偏移量,如果有多个交叉引用节,则使用该偏移量。该数字应该是一个交叉引用。
  • /Root [dictionary]:指定文档目录对象的引用对象,这是一个特殊对象,包含指向不同种类的其他特殊对象的各种指针(稍后会详细介绍)。
  • /Encrypt [dictionary]:指定文档的加密字典。
  • /Info [dictionary]:指定文档信息字典的引用对象。
  • /ID [array]:指定构成文件标识符的两字节未加密字符串数组。
  • /XrefStm [integer]:指定解码流中从文件开头到交叉引用流的偏移量。这仅存在于混合引用文件中,如果我们还想打开文档,即使应用程序不支持压缩引用流,也会指定该文件。

我们必须记住,如果我们稍后更新 PDF 文档,则可以修改初始结构。更新通常会在文件末尾附加其他元素。

增量更新

PDF 在设计时考虑了增量更新,因为我们可以将一些对象附加到 PDF 文件的末尾,而无需重写整个文件。因此,可以快速保存对 PDF 文档的更改。PDF文档的新结构如下图所示:

图 3: PDF 结构

我们可以看到 PDF 文档仍然包含原始的标题、正文、交叉引用表和预告片。此外,PDF 文档中还添加了其他正文、交叉引用和预告片部分。附加的交叉引用部分将仅包含已更改、替换或删除的对象的条目。已删除的对象将保留在文件中,但将标有“f”标志。每个预告片都需要以“%%EOF”标签结尾,并且应该包含 /Prev 条目,该条目指向前一个交叉引用部分。

在 PDF 版本 1.4 及更高版本中,我们可以指定文档目录字典中的版本条目以覆盖 PDF 标题中的默认版本。

例子

让我们展示一个简单的 PDF 示例并对其进行分析。让我们从这里下载一个示例 PDF 文档并对其进行分析。打开此 PDF 文档后,它看起来如下所示:

图 4: PDF 文档示例

交叉参考和预告片部分如下图所示:

图 5:交叉引用和预告片部分

为清楚起见,已减少交叉引用部分。交叉引用部分包含一个子部分,该部分本身包含 223 个对象。尾部部分从字节偏移 50291 开始,包括 223 个对象,其中根元素指向对象 221,信息元素指向对象 222。

在下一节中,我们将了解 PDF 结构的基本数据类型。

PDF 数据类型

PDF 文档包含以下描述的八种基本类型的对象。这些类型是:布尔值、数字、字符串、名称、数组、字典、流和空对象。可以标记对象,以便它们可以被其他对象引用。带标签的对象也称为间接对象。

布尔值

有两个关键字:true和false表示布尔值。

数字

PDF 文档中有两种类型的数字:整数和实数。整数由一个或多个数字组成,可选地前面有一个加号或减号。整数对象的示例如下所示:

  • 123 +123 -123

实际值可以用一个或多个数字表示,带有可选的符号和前导、尾随或嵌入的小数点(句点)。下面是一个实数的例子:

  • 123.0 -123.0 +123.0 123. -.123

名称

PDF 文档中的名称由 0x21 – 0x7E 范围内的 ASCII 字符序列表示。例外是字符:%、(、)、<、>、[、]、{、}、/ 和 #,它们必须以斜线开头。字符的另一种表示形式是它们的十六进制等效值,前面是字符“#”。名称元素的长度有限制,可能只有 127 个字节长。

写名字时,必须用斜线来介绍名字;斜杠不是名称的一部分,而是一个前缀,表示后面是表示名称的字符序列。如果我们想使用空格或任何其他特殊字符作为名称的一部分,则必须使用两位十六进制表示法对其进行编码。

名称示例见下表:

图 6: PDF 名称(来源

字符串

PDF 文档中的字符串表示为由括号或尖括号包围的一系列字节,但最大长度为 65535 个字节。任何字符都可以用 ASCII 表示,也可以用八进制或十六进制表示。八进制表示要求字符以 ddd 的形式写入,其中 ddd 是八进制数。十六进制表示要求字符以 <dd> 的形式写入,其中 dd 是十六进制数。

下面是一个表示嵌入括号中的字符串的示例:

  • (我的字符串)

下面是一个表示嵌入尖括号中的字符串的示例(下面的十六进制表示与上面相同,读作“mystring”):

  • <6d79737472696e67>

我们还可以在表示字符串时使用特殊的知名字符。它们是:n 表示换行,r 表示回车,t 表示水平制表符,b 表示退格,f 表示换页,( 表示左括号,) 表示右括号和反斜杠。

数组

PDF 文档中的数组表示为一系列 PDF 对象,这些对象可能是不同的类型,并用方括号括起来。这就是为什么 PDF 文档中的数组可以保存任何对象类型,如数字、字符串、字典甚至其他数组的原因。数组也可以有零个元素。数组用方括号表示。下面给出了一个数组的示例:

  • 123 123.0 真(我的字符串)/我的名字]

字典

PDF 文档中的字典表示为键/值对表。键必须是名称对象,而值可以是任何对象,包括另一个字典。字典中的最大条目数为 4096 个条目。字典可以用双尖括号 << 和 >> 括起来的条目来表示。下面给出了一个字典的例子:
<< /mykey1 123

     /mykey2 0.123

     /mykey3 << /mykey4 真

                         /mykey5 (我的字符串)

                    >>

>>

流对象由字节序列表示,长度可能不受限制,这就是为什么图像和其他大数据块通常表示为流的原因。流对象由字典对象表示,后跟关键字流 ,后跟换行符和结束流。

流对象的示例如下所示:
<<

/类型 /页面

     /长度 23 0 R

     /过滤器 /LZW解码

>>

溪流

endstream
所有流对象应为间接对象,流字典应为直接对象。流字典指定流的确切字节数。在数据之后应该有一个换行符和 endstream 关​​键字。

所有流字典中使用的常用关键字如下(请注意,长度条目是强制性的):

  • 长度:PDF 文件的多少字节用于流数据。如果流包含过滤器条目,则长度应指定编码数据的字节数。
  • 类型:字典描述的 PDF 对象的类型。
  • 过滤器:将在处理流数据时应用的过滤器的名称。可以按照应用的顺序指定多个过滤器。
  • DecodeParms:Filter 指定的过滤器使用的字典或字典数组。此值指定在应用过滤器时需要传递给过滤器的参数。如果过滤器使用默认值,则不需要这样做。
  • F:指定包含流数据的文件。
  • FFilter:在处理流的外部文件中找到的数据时应用的过滤器的名称。
  • FDecodeParms:由 FFilter 指定的过滤器使用的字典或字典数组。
  • DL:指定解码流中的字节数。如果有足够的磁盘空间可用于将流写入文件,则可以使用此方法。
  • N:存储在流中的间接对象的数量。
  • First:第一个压缩对象在解码流中的偏移量。
  • 扩展:指定对形成继承树的其他对象流的引用。

对象流中的流数据将包含 N 对整数,其中第一个整数表示对象编号,第二个整数表示该对象在解码流中的偏移量。对象流中的对象是连续的,不需要按照对象编号的递增顺序存储。字典中的第一个 条目标识对象流中的第一个对象。

我们不应该在对象流中存储以下信息:

  • 流对象
  • 代号不等于 0 的对象
  • 文档的加密字典
  • 对象流字典中长度条目的间接对象
  • 文档目录、线性化字典、页面对象

在 PDF 1.5 中,交叉引用信息可以存储在交叉引用流中而不是交叉引用表中。每个交叉引用流包含与交叉引用表和尾部等效的信息。

空对象

空对象由关键字“null”表示。

间接对象

首先,我们必须知道,PDF文档中的任何对象都可以被标记为间接对象。这为对象提供了唯一的对象标识符,其他对象可以使用该标识符来引用间接对象。间接对象​​是用关键字“obj”和“endobj”表示的编号对象。endobj 必须出现在它自己的行中,但 obj 必须出现在对象 ID 行的末尾,即间接对象的第一行。对象 ID 行由对象编号、世代编号和关键字“obj”组成。间接对象​​的示例如下:
2 1 obj

12345

endobj
在上面的示例中,我们创建了一个新的间接对象,其中包含数字 12345 对象。通过将对象声明为间接对象,我们可以在 PDF 文档交叉引用表中使用它,并在文档中的任何页面、字典等中重用它。由于每个间接对象在交叉引用表中都有自己的条目,因此可以非常快速地访问间接对象。

间接宾语的宾语标识符由两部分组成;第一部分是当前间接对象的对象编号。间接对象​​不需要在 PDF 文档中按顺序编号。第二部分是代号,对于新创建的文件中的所有对象,它都设置为零。当对象更新时,此数字稍后会增加。

我们可以通过间接引用来引用间接对象,它由对象编号、世代编号和关键字R组成。要引用上述间接对象,我们必须编写如下内容:

  • 2 1 转

如果我们试图引用一个未定义的对象,我们实际上是在引用一个空对象。

文件结构

PDF 文档由 PDF 文件正文部分中包含的对象组成。PDF 文档中的大多数对象都是字典。文档的每一页都由一个页面对象表示,该对象是一个包含对页面内容的引用的字典。页面对象连接在一起并形成一个页面树,它在文档目录中使用间接引用进行声明。

PDF文档的整个结构可以用下图表示[1]:

图 7: PDF 文档的结构(来源

在上图中,我们可以看到文档目录包含对页面树、大纲层次结构、文章线程、命名目的地和交互表单的引用。我们不会详细介绍每个部分的功能,但我们将只介绍最重要的部分,即页面树。

文件目录

从上图中我们可以看出,Document Catalog 是 PDF 文档中对象的根。我们已经说过,是 Trailer PDF 部分中的 /Root 元素指定了文档目录。文档目录包含对定义文档内容的其他对象的引用。它还包含声明文档将如何在屏幕上显示的信息。文档目录中的条目如下:

  • /Type:目录描述的 PDF 对象的类型(在我们的例子中,这是 Catalog,因为这是文档目录对象)。
  • /Version:构建文档所依据的 PDF 规范的版本。
  • /Extensions:本文档中有关开发者扩展的信息。
  • /Pages:对作为文档页面树根的对象的间接引用。
  • /Dests:对作为命名目标对象根的对象的间接引用。
  • /Outlines:对作为文档大纲层次结构根的大纲目录对象的间接引用。
  • /Threads:对表示文档的文章线程的线程字典数组的间接引用。
  • /Metadata:对包含文档元数据的元数据流的间接引用。

我们可以看到许多其他条目是文档目录的一部分,但这里不会对其进行描述。读者可以查看我们的资源以获取详细信息。文档目录的示例如下所示:
1 0 obj

<< /类型 /目录

/第 2 页 0 R

/PageMode /UseOutlines

/大纲 3 0 R

>>

结束对象

页面树

文档的页面是通过页面树访问的,它定义了 PDF 文档中的所有页面。树包含表示 PDF 文档页面的节点,这些节点可以有两种类型:中间节点和叶节点。中间节点也称为页树节点,而叶节点称为页对象。 

最简单的页面树结构可以由一个直接引用所有页面对象的页面树节点组成(因此所有页面对象都是叶子)。

页树中的每个节点都必须具有以下条目:

  • /Type:此对象描述的 PDF 对象的类型(在我们的例子中是Pages ,因为我们正在讨论页面树节点)。
  • /Parent:应该存在于所有页面树节点中,除了根目录,该条目不得存在。此条目指定其父项。
  • /Kids:应该出现在除叶子之外的所有页面树节点中,并指定可从当前节点直接访问的所有子元素。
  • /Count:指定在后续页树中作为该节点后代的叶子节点的数量。

我们必须记住,页面树与 PDF 文档中的任何内容都没有关系,例如页面或章节。

页面树的基本示例如下所示:
2 0 obj

<< /类型 /页数

/儿童 [ 4 0 R

10 0 转

24 0 转

]

/计数 3

>>

结束对象

4 0 对象

<< /类型 /页面

>>

结束对象

10 0 对象

<< /类型 /页面

>>

结束对象

24 0 对象

<< /类型 /页面

>>

endobj
上面的页树定义了ID 为 2 的Root对象,它有 3 个子对象,对象 4、10 和 20。我们还可以看到,页树的叶子是指定文档单页属性的字典. 在为每个文档页面定义属性时,我们可以使用多个属性。

我们已经了解了 PDF 文档的基本结构及其数据类型。如果我们想开始在 PDF 阅读器中发现漏洞,我们需要以 PDF 阅读器无法处理并崩溃的方式更改 PDF 文档。通常,如果我们可以让 PDF 阅读器崩溃,我们就发现了一个安全漏洞,我们可以使用它在目标机器上执行任意代码。

一个例子

在本文中,我们将看一个非常简单的 PDF 文档示例。首先,我们需要创建 PDF 文档,然后我们将尝试对其进行分析。要创建 PDF 文档,我们首先创建一个非常简单的 .tex 文档,其中包含下图中可以看到的内容:

图 8:简单文档

我们可以看到 .tex 文档并没有真正包含太多内容。首先,我们将文档定义为一篇文章,然后在开始 和结束文档中包含文章的内容。我们将包含一个带有标题(简介)的新部分,并包含静态文本“Hello World!”。 

我们可以使用pdflatex命令将 .tex 文件编译成 PDF 文件,并指定 .tex 文件的名称作为参数。生成的 PDF 如下图所示:

图 9:结果

我们可以看到,PDF 文档确实包含的内容并不多,只有我们实际包含的文本,没有图片、JavaScript 或其他元素。

示例 1

让我们看一下 PDF 文档结构,它显示在下面的输出中:
%PDF-1.5

%ÐÔÅØ

3 0 对象 <<<

/长度 138

/过滤器 /FlateDecode

>>

溪流

端流

结束对象

10 0 对象 <<<

/长度1 1526

/长度2 7193

/长度3 0

/长度 8194

/过滤器 /FlateDecode

>>

溪流

端流

结束对象

12 0 对象 <<<

/长度1 1509

/长度2 9410

/长度3 0

/长度 10422

/过滤器 /FlateDecode

>>

溪流

端流

结束对象

15 0 对象 <<<

/制片人 (pdfTeX-1.40.12)

/创作者(特克斯)

/创建日期 (D:20121012175007+02'00')

/ModDate (D:20121012175007+02'00')

/被困 /假

/PTEX.Fullbanner(这是 pdfTeX,版本 3.1415926-2.3-1.40.12(TeX Live 2011)kpathsea 版本 6.0.1)

>> 结束对象

6 0 对象 <<<

/类型 /ObjStm

/N 10

/前 65

/长度 761

/过滤器 /FlateDecode

>>

溪流

端流

结束对象

16 0 对象 <<<

/类型 /外部参照

/索引 [0 17]

/尺码 17

/W [1 2 1]

/根 14 0 R

/信息 15 0 R

/ID [<1DC2E3E09458C9B4BEC8B67F56B57B63> <1DC2E3E09458C9B4BEC8B67F56B57B63>]

/长度 60

/过滤器 /FlateDecode

>>

溪流

端流

结束对象

起始外部参照

20215

%%EOF
创建这样一个简单的 PDF 文档需要很多必要的元素,所以我们可以想象一个非常复杂的 PDF 文档的外观。我们还需要记住,为了清晰和简洁,所有编码的数据流都被删除并替换为三个点。

让我们介绍每个 PDF 部分。标题如下图所示:

图 10: PDF 标题

身体可以在下图中看到:

图 11: PDF 正文

外部参照部分如下图所示:

图 11: PDF 外部参照

最后,预告片部分如下所示:

图 12: PDF 预告片

我们介绍了 PDF 文档的所有部分,但我们仍然需要进一步分析它们。PDF 文档的 header 是标准的,我们真的不需要讨论它,让我们将 body 部分留到后面。 

这就是为什么我们必须首先看一下外部参照部分。我们可以看到文件开头到外部参照表的偏移量为20215字节,十六进制形式为0x4ef7。如果我们看一下使用 xxd 工具可以获得的文件的十六进制表示,我们可以看到下图中显示的内容:

图 13:文件的十六进制表示

突出显示的字节正好位于距文件开头偏移 20125 个字节的开头。前面的 0x0a 字节是新行,当前的 0x31 字节代表数字 1,这正是外部参照表的开头。这就是为什么外部参照表用 ID 16 和代号 0 的间接对象表示的原因。(所有对象都应该是这种情况,因为我们刚刚创建了 PDF 文档并且还没有更改任何对象。如果我们查看整个 PDF 文档,我们可以看到这显然是正确的;所有对象的世代号为零。)

间接对象​​的 /Type 将此分类为外部参照表。/Index 数组包含本节中每个子节的一对整数。第一个整数指定小节中的第一个对象编号,第二个整数指定小节中的条目数。在我们的示例中,对象编号为零,本小节中有 17 个条目。这也由 /Size 指令指定。请注意,此数字比该小节中任何对象编号的最大数字大一。/W 属性指定一个整数数组,表示交叉引用条目中字段的大小,表示字段为一字节、二字节和一字节。

之后是 /Root 元素,它指定 PDF 文档的目录目录为对象编号 14。/Info 是包含在对象编号 15 中的 PDF 文档的信息目录。/ID 数组是必需的,因为 Encrypt entry 存在并包含构成文件标识符的两个字符串。这两个字符串用作加密算法的输入。 

/Length 指定加密密钥的长度(以位为单位);该值应为 40 到 o128 范围内的 8 的倍数(默认值为 40)。在我们的例子中,加密密钥的长度是 60 位。/Filter 指定此文档的安全处理程序的名称;这也是用于加密文档的安全处理程序。在我们的例子中,这是 FlateDecode,它使用 zlib/deflate 压缩方法对数据进行编码。

我们可以看到外部参照表的另一部分是压缩的,所以我们无法真正读取它。当然,我们可以对压缩数据应用一些 zlib 解压缩算法,但有更好的选择。如果工具已经存在,我们为什么要为此编写程序?使用 pdftk,我们可以使用以下命令修复 PDF 损坏的外部参照表:

  • # pdftk in.pdf 输出 out.pdf

之后,out.pdf 文件包含以下外部参照和预告片部分:

图 14:外部参照和预告片

显然,/Root 和 /Info 对象编号以及其他内容也发生了变化,但我们得到了定义外部参照表的预告片和外部参照关键字。我们可以看到外部参照表中有 14 个对象。

我们也可以继续尝试解码其他部分,但这超出了本文的范围。接下来,我们将检查未编码的文档。

示例 2

让我们看一下可在此处访问的示例 PDF 文档。一些流对象是加密的,但现在不那么重要了。由于我们已经知道如何处理 PDF 文档,所以我们不会在简单的东西上丢失太多单词。 

让我们在 gvim 之类的文本编辑器中打开该 PDF,然后查看预告片部分。我们现在必须知道,所有 PDF 文档都应该从头到尾阅读。预告片如下图所示:

图 15: PDF 预告片

让我们也只展示几个对象的外部参照(为了清楚起见,其余的都被丢弃了):

图 16: PDF 外部参照

我们可以看到PDF文档的/Root包含在ID为221的对象中,并且在对象222中还有额外的信息。对象221是整个文档中最重要的对象,所以我们来展示一下:

图 17:对象 221

我们可以看到该对象确实是 Document Catalog。Page Tree 对象是 212,Outlines 对象是 213,Names 对象是 220,OpenAction 对象是 58。除了 Page Tree 对象我们还没有讨论任何其他类型,所以我们将继续 Page Tree 讨论只要。

ID 为 212 的 Page Tree 对象如下图所示:

图 18:页面树对象

所以 212 对象包含 PDF 文档的实际页面。它包含 10 页,完全正确(如果我们用任何 PDF 阅读器打开 PDF 文件并检查页数,我们可以检查出来)。 

我们知道 Kids 属性指定了可以从当前节点直接访问的所有子元素。在我们的例子中,有两个直接子节点,其对象 ID 为 66 和 135。对象 66 如下所示:

图 19:对象 66

对象 66 包含 ID 为 57、69、75、97、108 和 120 的其他子元素。

图 20:对象 135

对象 135 进一步定义了对象 129、138、133 和 158。

如果我们计算所有元素,我们可以看到正好有 10 个元素,这意味着 10 页中有 10 页。这进一步意味着所有呈现的对象实际上都是 PDF 文档的实际页面,并且不包含任何其他子节点。 

所有呈现的对象都以类似方式声明,因此我们不会依次查看每个对象。相反,我们只看一个对象,即对象 57。对象 57 包含如下声明:

图 21:对象 57

我们可以看到该对象的类型是/Page,这直接暗示这是一个叶子节点,呈现PDF文档的其中一个页面。该 PDF 页面的内容可以在对象 62 中找到:

图 22:对象 62

我们可以看到PDF页面的实际内容是用FlateDecode编码的,这只是一个简单的zlib编码算法。

结论

我们已经看到了如何构建 PDF 文档的两个示例。利用我们获得的知识,我们可以开始生成不正确的 PDF 文档并将它们提供给各种 PDF 阅读器。如果某个 PDF 阅读器在阅读某个 PDF 文档时崩溃,则该文档包含 PDF 阅读器无法处理的内容。这意味着存在漏洞的可能性,需要进一步研究。

最后,如果证明存在漏洞,我们甚至可以编写一个包含恶意代码的 PDF 文档,当受害者在目标机器上使用易受攻击的 PDF 阅读器打开 PDF 文档时执行该代码。在这种情况下,整台机器可能会受到威胁,因为只要打开恶意 PDF 文档就可以执行任意恶意代码。

来源

漏洞统计信息

Adobe 支持政策:支持的产品版本,Adobe

文档管理 — 可移植文档格式 — 第 1 部分:PDF 1.7,Adobe (Archive.org)

参考:

[1]:PDF 文件格式,可访问:http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf。

标签:包含,对象,文件格式,2020,引用,PDF,文档,我们
来源: https://www.cnblogs.com/panjinzhao/p/16116523.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有