
CVE-2020-28948/CVE-2020-28949漏洞其实属于PEAR的Archive_Tar模块,Archive_Tar模块是用于在php中创建 提取和添加tar文件的工具类。由于drupal中集成了带漏洞的Archive_Tar模块。
CVE-2020-28948
Archive_Tar过滤不严导致phar反序列化:
class Archive_Tar extends PEAR{/*** @var string Name of the Tar*/public $_tarname = '';/*** @var boolean if true, the Tar file will be gzipped*/public $_compress = false;/*** @var string Type of compression : 'none', 'gz', 'bz2' or 'lzma2'*/public $_compress_type = 'none';/*** @var string Explode separator*/public $_separator = ' ';/*** @var file descriptor*/public $_file = 0;/*** @var string Local Tar name of a remote Tar (http:// or ftp://)*/public $_temp_tarname = '';/*** @var string regular expression for ignoring files or directories*/public $_ignore_regexp = '';/*** @var object PEAR_Error object*/public $error_object = null;/*** Format for data extraction** @var string*/public $_fmt = '';/*** @var int Length of the read buffer in bytes*/protected $buffer_length;/*** Archive_Tar Class constructor. This flavour of the constructor only* declare a new Archive_Tar object, identifying it by the name of the* tar file.* If the compress argument is set the tar will be read or created as a* gzip or bz2 compressed TAR file.** @param string $p_tarname The name of the tar archive to create* @param string $p_compress can be null, 'gz', 'bz2' or 'lzma2'. This* parameter indicates if gzip, bz2 or lzma2 compression* is required. For compatibility reason the* boolean value 'true' means 'gz'.* @param int $buffer_length Length of the read buffer in bytes** @return bool*/public function __construct($p_tarname, $p_compress = null, $buffer_length = 512){parent::__construct();$this->_compress = false;$this->_compress_type = 'none';if (($p_compress === null) || ($p_compress == '')) {if (@file_exists($p_tarname)) {if ($fp = @fopen($p_tarname, "rb")) {// look for gzip magic cookie$data = fread($fp, 2);fclose($fp);if ($data == "\37\213") {$this->_compress = true;$this->_compress_type = 'gz';// No sure it's enought for a magic code ....} elseif ($data == "BZ") {$this->_compress = true;$this->_compress_type = 'bz2';} elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') {$this->_compress = true;$this->_compress_type = 'lzma2';}}} else {// probably a remote file or some file accessible// through a stream interfaceif (substr($p_tarname, -2) == 'gz') {$this->_compress = true;$this->_compress_type = 'gz';} elseif ((substr($p_tarname, -3) == 'bz2') ||(substr($p_tarname, -2) == 'bz')) {$this->_compress = true;$this->_compress_type = 'bz2';} else {if (substr($p_tarname, -2) == 'xz') {$this->_compress = true;$this->_compress_type = 'lzma2';}}}} else {if (($p_compress === true) || ($p_compress == 'gz')) {$this->_compress = true;$this->_compress_type = 'gz';} else {if ($p_compress == 'bz2') {$this->_compress = true;$this->_compress_type = 'bz2';} else {if ($p_compress == 'lzma2') {$this->_compress = true;$this->_compress_type = 'lzma2';} else {$this->_error("Unsupported compression type '$p_compress'\n" ."Supported types are 'gz', 'bz2' and 'lzma2'.\n");return false;}}}}$this->_tarname = $p_tarname;if ($this->_compress) { // assert zlib or bz2 or xz extension supportif ($this->_compress_type == 'gz') {$extname = 'zlib';} else {if ($this->_compress_type == 'bz2') {$extname = 'bz2';} else {if ($this->_compress_type == 'lzma2') {$extname = 'xz';}}}if (!extension_loaded($extname)) {PEAR::loadExtension($extname);}if (!extension_loaded($extname)) {$this->_error("The extension '$extname' couldn't be found.\n" ."Please make sure your version of PHP was built " ."with '$extname' support.\n");return false;}}if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {$this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" ."a8checksum/a1typeflag/a100link/a6magic/a2version/" ."a32uname/a32gname/a8devmajor/a8devminor/a131prefix";} else {$this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" ."Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" ."Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";}$this->buffer_length = $buffer_length;}
该构造方法主要是用来打开压缩包,或在传入$p_tarname不存在时候创建压缩包,然后在214行会把p_tarname赋值给_tarname
$this->_tarname = $p_tarname;
然后看解压函数extract


跟入_openread

844行把tarname传给v_filname,然后847行往下就是通过前面获取压缩包的后缀来用不同函数获取里面的内容并赋值给_file。

跟入

1943行translatewinpath把前面穿过来的路径重新转义后赋值给$p_path
这里如果$p_path在前面没有穿值的话这里则为空,$p_path决定了后面是否能造成反序列化

在1977行获取压缩包滴头部信息后,跟入1981行的readheader

1693行从刚刚读取的内容中通过unpack函数提取内容赋值给$v_data

1727赋值后,跟入1728行的函数

可以看到这里说通过strpos匹配文件名中是否包含phar://,由于strpos区分大写,可通过PHAR://绕过

可以看到,如果在前面传入$p_path的话,会早2059或2060行进行路径拼接(upload/PHAR://XXXX.phar),会导致在2065行无法通过file_exists触发反序列化,因此该漏洞需要不穿入路径。
演示:
phar包可自己通过前台头像上传啥的改改后缀上传

通过python生成一个tar包
后台功能点


CVE-2020-28949
任意文件重写
和前面一样

只过滤了phar,其他协议没有过滤,由于tar类是使用fopen来创建文件,fopen支持file协议,可通过file:///协议来创建文件,达到重写的效果主机上的passwd文件,这里以重写/tmp/test123为例

xx.txt内容如下

上传后

//分析这个漏洞滴时候电脑充电时候全程充满静电,我全场被电。。
hxd们 点个关注8




