• PHP审计之POP链挖掘


    PHP审计之POP链挖掘

    前言

    续上文中的php反序列化,继续来看,这个POP的挖掘思路。在其中一直构思基于AST去自动化挖掘POP链,迫于开发能力有限。没有进展,随后找到了一个别的师傅已经实现好的项目。

    魔术方法

    __wakeup() //使用unserialize时触发
    __sleep() //使用serialize时触发
    __destruct() //对象被销毁时触发
    __call() //在对象上下文中调用不可访问的方法时触发
    __callStatic() //在静态上下文中调用不可访问的方法时触发
    __get() //用于从不可访问的属性读取数据
    __set() //用于将数据写入不可访问的属性
    __isset() //在不可访问的属性上调用isset()或empty()触发
    __unset() //在不可访问的属性上使用unset()时触发
    __toString() //把类当作字符串使用时触发,file_exists()判断也会触发
    __invoke() //当脚本尝试将对象调用为函数时触发
    

    __call__callstatic

    现实情况下__call的利用居多,该魔术方法触发的条件是在对象上下文中调用不可访问的方法时触发。

    调用流程如下:

    $this->a() ==> 当前类a方法 ==> 父类a方法 ==> 当前类__call方法 ==> 父类__call方法
    

    如果触发__call方法,那么a,即方法名,会作为__call的方法的第一个参数,而参数列表会作为__call的方法第二个参数。

    来看到代码

    function __destruct(){
        $this->a->b();
    }
    

    这里有2个利用路径,一个是$this->a中构造一个存在方法的实例化类,另一种方式是找一个不存在b方法并且存在__call方法的类,当b不存在时,即自动调用__call

    __callstatic方法只有在调用到静态方法的时候才能触发

    __get__set

    不存在该类变量或者不可访问时,则会调用对应的__get方法

    $this->a ==> 当前类a变量 ==> 父类a变量 ==> 当前类__get方法 ==> 父类__get方法
    

    __get代码案例

    function __destruct(){
        echo $this->a;
    }
    

    调用不存在变量a,即会自动触发__get方法,

    数据写入不可访问的变量或不存在的变量即调用__set

    function __destruct(){
        $this->a = 1;
    }
    

    __toString

    把类当作字符串使用时触发

    $this->_adapterName = $adapterName;
    $adapterName = 'xxx' . $adapterName;
    

    POP链挖掘

    此前构思的自动化挖掘POP链的功能已经被其他师傅实现了,在此就不班门弄斧了,直接拿现成的来用。
    按照个人理解反序列化入口点一般为__wakeup__destruct__construct
    思路其实就是寻找__destruct方法,作为入口点,然后寻找一个回调函数作为末端。而中间需要寻找各种中间链,将其串联起来。串联的方法基本上就是一些魔术方法和一些自定义的方法。

    项目地址:https://github.com/LoRexxar/Kunlun-M

    cp Kunlun_M/settings.py.bak Kunlun_M/settings.py
    
    python kunlun.py init initialize
    
    python kunlun.py config load
    
    python kunlun.py plugin php_unserialize_chain_tools -t C:kyxscms-1.2.7
    

    结果:

     [20:28:51] [PhpUnSerChain] New Source __destruct() in thinkphp#library#think#Process_php.Class-Process
     [20:28:51] thinkphp#library#think#Process_php.Class-Process
     newMethod                        Method-__destruct()
     [20:28:51] thinkphp#library#think#Process_php.Class-Process.Method-__destruct
     MethodCall                       Variable-$this->stop()
     [20:28:51] thinkphp#library#think#Process_php.Class-Process.Method-stop
     MethodCall                       Variable-$this->updateStatus('Constant-false',)
     [20:28:51] thinkphp#library#think#Process_php.Class-Process.Method-updateStatus
     MethodCall                       Variable-$this->readPipes('Variable-$blocking', ' === Constant-DIRECTORY_SEPARATOR ? 627')
     [20:28:51] thinkphp#library#think#Process_php.Class-Process.Method-readPipes
     MethodCall                       Variable-$this->processPipes->readAndWrite('Variable-$blocking', 'Variable-$close')
     [20:28:51] thinkphp#library#think#console#Output_php.Class-Output
     newMethod                        Method-__call('$method', '$args')
     [20:28:51] thinkphp#library#think#console#Output_php.Class-Output.Method-__call.If
     FunctionCall                     call_user_func_array("Array-['Variable-$this', 'block']", 'Variable-$args')
     [20:28:51] [PhpUnSerChain] UnSerChain is available.
    

    这其实利用链就清晰了

    Process->__destruct ==>Process->stop ==>Process->updateStatus ==> Process->readPipes ==> Output->readAndWrite ==> Output->__call==> call_user_func_array()
    

    内容补充

    查看一下这三个方法的调用

    <?php
    
    class User{
    
        const SITE = 'uusama';
    
    
    
        public $username;
    
        public $nickname;
    
        private $password;
    
        private $id;
    
    
    
        public function __construct($username, $nickname, $password)
    
        {
    
            $this->username = $username;
    
            $this->nickname = $nickname;
    
    
    
        }
    
    
    
        // 定义反序列化后调用的方法
    
        public function __wakeup()
    
        {
    
            $this->password = '1234';
    
        }
        public function __destruct(){
            $this->id = '123';
        }
    
    }
    $user = new User('uusama', 'uu', '12345');
    $ser= serialize($user);
    var_dump(unserialize($ser));
    

    结果:

    object(User)[2]
      public 'username' => string 'uusama' (length=6)
      public 'nickname' => string 'uu' (length=2)
      private 'password' => string '1234' (length=4)
      private 'id' => null
    

    在反序列化中一般开发可能会在__wakeup中做一些过滤。

    参考

    浅析 PHP 反序列化漏洞的利用与审计

    如何自动化挖掘php反序列化链 - phpunserializechain诞生记

    结尾

    但该工具并没有达到我个人的预期,因为该工具中只是使用__destruct这单个方法作为反序列化的入口点。

  • 相关阅读:
    WebStorm破解版
    React Native实战一
    Button加在UITableViewHeaderFooterView的self.contentView上导致不能响应点击
    centos7在vmware上无法上网
    重定向和转发的区别
    http和https的区别
    Runtime Error! R6025-pure virtual function call 问题怎么解决
    myeclipse部署web项目部署按钮无效
    Collections工具类的使用
    泛型集合
  • 原文地址:https://www.cnblogs.com/nice0e3/p/15399507.html
Copyright © 2020-2023  润新知