英国安全公司 Secarma 的研究主管 Sam Thomas 本月在 Black Hat 和 BSides
安全会议上展示了 PHP
编程语言的安全漏洞,并指出该漏洞影响了所有接受用户资料的
PHP 应用程序和库,包括 WordPress
等内容管理系统(CMS),并将允许远程程序攻击。

又到了金三银四跳槽季,很多小伙伴都开始为面试做准备,今天小编就给大家分享一个网安常见的面试问题:PHP反序列化漏洞。

前言

序列化(Serialization)与反序列化(Deserialization)是所有编程语言都具备的功能,序列化将对象转换为字符串,以将数据迁移到不同服务器,服务或应用程序上,然后通过反序列将字符串还原到对象。

虽然PHP反序列化漏洞利用的条件比较苛刻,但是一旦被利用就会产生很严重的后果,所以很多公司都比较关注这个技能点,小伙伴们一定要掌握哦。

虽然这篇文章叫做PHP对象注入,但是本质上还是和PHP的序列化的不正确使用有关。如果你阅读了PHP中的SESSION反序列化机制对序列化就会有一个大致的认识。PHP对象注入其实本质上也是由于序列化引起的。

安全研究员 Stefan Essar 在 2009 年就透露了 PHP
中反序列化黑客控制的数据带来的风险,而相关的漏洞不仅存在于 PHP
中,还存在于其他编程语言中。 Thomas 公布的是 PHP
的新攻击技术,可用于各种场景,例如 XML External
Entity(XEE)漏洞或服务器端伪造请求(SSFR)漏洞等。

PHP序列化与反序列化介绍

基础知识

Thomas 表示,过去外界认为 XXE
漏洞带来的最大问题是信息外泄,但现在可出发程序执行。相关攻击分为两个阶段。
首先,将包含恶意对象的 Phar
存档上传到攻击目标的本地文件系统,然后触发一个基于 phar://
的文件操作,就可能导致恶意程序执行。

什么是序列化与反序列化

在php类中可能会存在一些叫做魔术函数,这些函数会在类进行某些事件的时候自动触发,例如__construct()会在一个对象被创建时调用,__destruct()会在一个对象销毁时调用,__toString当对象被当做一个字符串的时候被调用。常见的魔术函数有__construct()
__destruct()__toString()__sleep()__wakeup()

Thomas 已利用 PHP 的反序列化程序成功攻击了 WordPress 与 Typo3
内容管理平台,以及 Contao 所采用的 TCPDF 库。

维基百科中这样定义:序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。

举例如下:

澳门新葡萄京所有网站 1

概念很容易理解,其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化。

varr1."
"; } public function __construct(){ echo "__construct
"; } public function __destruct(){ echo "__destruct
"; } public function __toString(){ return "__toString
"; } public function __sleep(){ echo "__sleep
"; return array; } public function __wakeup(){ echo "__wakeup
"; }}$obj = new test(); //实例化对象,调用__construct()方法,输出__construct$obj->echoP方法,输出"abc"echo $obj; //obj对象被当做字符串输出,调用__toString()方法,输出__toString$s =serialize; //obj对象被序列化,调用__sleep()方法,输出__sleepecho unserialize; //$s首先会被反序列化,会调用__wake()方法,被反序列化出来的对象又被当做字符串,就会调用_toString()方法。// 脚本结束又会调用__destruct()方法,输出__destruct?>

(文/开源中国)    

那么序列化与反序列化有什么用处呢?

原理

举个例子:

为什么会用到序列话这样的方法?主要就是就是方便进行数据的传输,并且数据恢复之后,数据的属性还不会发生变化。例如,将一个对象反序列化之后,还是保存了这个对象的所有的信息。同时还可以将序列化的值保存在文件中,这样需要用的时候就可以直接从文件中读取数据然后进行反序列化就可以了。在PHP使用serialize()unserialize()来进行序列化和反序列化的。

比如:现在我们都会在淘宝上买桌子,桌子这种很不规则的东西,该怎么从一个城市运输到另一个城市,这时候一般都会把它拆掉成板子,再装到箱子里面,就可以快递寄出去了,这个过程就类似我们的序列化的过程(把数据转化为可以存储或者传输的形式)。当买家收到货后,就需要自己把这些板子组装成桌子的样子,这个过程就像反序列的过程(转化成当初的数据对象)。

而序列化的危害就在于如果序列化的内容是用户可控的,那么用户就可以注入精心构造的payload。当进行发序列化的时候就有可能会出发对象中的一些魔术方法,造成意想不到的危害。

也就是说,序列化的目的是方便数据的传输和存储。

澳门新葡萄京所有网站 ,对象注入

在PHP应用中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。

本质上serialize()unserialize()在PHP内部实现上是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象、魔术函数以及序列化相关问题的时候导致的。

常见的序列化格式:二进制格式字节数组json字符串xml字符串

如果在一个程序中,一个类用于临时将日志存储进某个文件中,当__destruct()方法被调用时,日志文件被删除。

PHP序列化与反序列化

logfile.php

PHP通过string serialize ( mixed $value )和mixed unserialize ( string
$str )两个函数实现序列化和反序列化。

"; file_put_contents($this->logfilename,$text,FILE_APPEBD); } public function __destruct() { echo 'deletes'.$this->logfilename; unlink.'/'.$this->logfilename); }}?>

下面是比较典型的PHP反序列化漏洞中可能会用到的魔术方法:

在其他类中使用LogClass

void __wakeup

logLogin.php

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

logfilename = "login.log";$obj->logdata;?>

void __construct ([ mixed $args [, $… ]])

上面的这段代码就是一个正常的使用LogClass类来完成日志记录的功能。

具有构造函数的类会在每次创建新对象时先调用此方法。

下面显示的是存在对象注入漏洞的使用例子。

void __destruct

news.php

析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

name."is".$this->age."years old.
"; }}// 从用户接受输入发序列化为User对象$usr = unserialize;?>

public string __toString

上面显示的代码使用了LogClass对象同时还会从用户那里接受输入进行发序列化转化为一个User对象。

__toString 方法用于一个类被当成字符串时应怎样回应。例如 echo
$obj;应该显示些什么。

当我们提交如下的数据

此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR
级别的致命错误。

news.php?user=O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John”;}

PHP反序列化漏洞

这样的语句是可以正常使用的,也是程序员希望使用的方法。

漏洞成因

但是如果提交的数据为:

PHP反序列化漏洞又称PHP对象注入,是因为程序对输入数据处理不当导致的。

news.php?user=O:8:"LogClass":1:{s:11:"logfilename";s:9:".htaccess";}

先看一个例子:

那么最后就会输出delete .htaccess

澳门新葡萄京所有网站 2

可以看到通过构造的数据,导致执行了LogClass中的__destruct()方法然后删除了网站中重要的配置文件。

这个例子中,析构函数会回显$test的值,我们可以构造一个对象,控制$test的值,达到控制数据流的目的,实现反序列化漏洞的利用。

从上面这个例子也可以看出来,如果没有严格控制用户的输入同时对用户的输入进行了反序列化的操作,那么就有可能会实现代码执行的漏洞。

构造过程如下:

注入点

澳门新葡萄京所有网站 3

PHP对象注入一般在处在程序的逻辑上面。例如一个User类定义了__toString()用来进行格式化输出,但是也存在File类定义了__toString()方法读取文件内容然后进行显示,那么攻击者就有可能通过User类的反序列化构造一个File类来读取网站的配置文件。

澳门新葡萄京所有网站 4

user.php

利用方式

" . $this->filename ; return @file_get_contents; }}class UserClass { public $age = 0; public $name = ''; public function __toString() { return 'User '.$this->name." is ".$this->age.' years old. 
'; }}$obj = unserialize;echo $obj; //调用obj的__toString()方法?>

一、__wakeup绕过

正常情况下我们应该传入UserClass序列化的字符串,例如user.php?usr=O:9:"UserClass":2:{s:3:"age";i:18;s:4:"name";s:3:"Tom";},页面最后就会输出User Tom is 18 years old.

(CVE-2016-7124)

这也是一个理想的使用方法。

反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup的执行。

但是如果我们传入的数据为user.php?usr=O:9:"FileClass":1:{s:8:"filename";s:10:"config.php";},页面最后的输出是filename发生了变化==>config.php,执行了FileClass中的__toString()方法。

影响版本:PHP before 5.6.257.x before 7.0.10

这样就可以读取到config.php中的源代码了。

DEMO如下:

漏洞挖掘

澳门新葡萄京所有网站 5

这类洞一般都是很难挖掘的,虽然显示看起来很简单,但实际上需要的条件还是相当的苛刻的,而且找对象注入的漏洞一般都是通过审计源代码的方式来进行寻找,看unserialize()的参数是否是可控的,是否存在反序列化其他参数对象的可能。

澳门新葡萄京所有网站 6

防御

澳门新葡萄京所有网站 7

要对程序中的各种边界条件进行测试

二、注入对象构造方法

避免用户对于unserialize()参数是可控的,可以考虑使用json_decode方法来进行传参。

当目标对象被private、protected修饰时的构造方法。

总结

示例代码:

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

澳门新葡萄京所有网站 8

澳门新葡萄京所有网站 9

同名方法的利用

澳门新葡萄京所有网站 10

这个例子中,class B和class
C有一个同名方法action,我们可以构造目标对象,使得析构函数调用class
C的action方法,实现任意代码执行。

构造代码:

澳门新葡萄京所有网站 11

澳门新葡萄京所有网站 12

三、Session反序列化漏洞

PHP中的Session经序列化后存储,读取时再进行反序列化。

相关配置:

session.save_path=””//设置session的存储路径

session.save_handler=””//设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数

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

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

PHP中有三种序列化处理器,如下表所示:

澳门新葡萄京所有网站 13

示例代码:

澳门新葡萄京所有网站 14

命名为sess_Session_id。

存储内容为序列化后的session:test|s:4:”test”;

不同处理器的格式不同,当不同页面使用了不同的处理器时,由于处理的Session序列化格式不同,就可能产生反序列化漏洞。

下面演示漏洞利用:

澳门新葡萄京所有网站 15

该页面中有类demo3,开启session,并用php处理器处理session。

澳门新葡萄京所有网站 16

通过session.php设置session,通过generate.php构造实例。

由于session.php与demo3.php采用的序列化处理器不同,我们可以构造“误导”处理器,达到漏洞利用的目的。

实例构造:

澳门新葡萄京所有网站 17

访问demo3.php成功创建了一个类demo3的实例。

四、PHAR利用

1、PHAR简介

PHAR (“Php ARchive”) 是PHP里类似于JAR的一种打包文件,在PHP 5.3
或更高版本中默认开启,这个特性使得 PHP也可以像 Java
一样方便地实现应用程序打包和组件化。一个应用程序可以打成一个 Phar
包,直接放到 PHP-FPM 中运行。

2、PHAR文件结构

PHAR文件由3或4个部分组成:

stub //PHAR文件头

stub就是一个简单的php文件,最简文件头为:

是可有可无的,若使用?>,则;与?>间至多一个空格。

文件头中必须包含__HALT_COMPILER;除此之外没有限制。(PHP通过stub识别一个文件为PHAR文件,可以利用这点绕过文件上传检测)

manifest describing the contents
//PHAR文件描述该部分存储文件名、文件大小等信息,如下图所示。

澳门新葡萄京所有网站 18

图中标出的地方,存储了经serialize的Meta-data,有序列化过程必有反序列化过程,这就是我们的注入点。

the file contents

PHAR文件内容

[optional] a signature for verifying Phar integrity (phar file format
only) //可选的签名部分,支持MD5和SHA1

澳门新葡萄京所有网站 19

3、攻击方法

2018年Black Hat研究院Sam Thomas的议题:

It’s a PHP unserialization vulnerability Jim, but not as we know
it提供了一种新的php反序列化攻击姿势。PHAR文件的Meta-data可以是任何能够序列化的PHP对象,当PHAR文件被任何文件系统函数首次通过phar://协议解析时Meta-data部分会被反序列化,这个反序列化过程就是我们的攻击点,Meta-data部分填充payload。

漏洞利用条件:

在目标系统上投放一个装在payload的可访问的PHAR文件,通过文件系统函数利用phar://伪协议解析目标PHAR文件。

下面演示利用过程:

先创建一个PHAR文件。

注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。

澳门新葡萄京所有网站 20

访问phar.php,在同目录下生成phar.phar文件。

澳门新葡萄京所有网站 21

箭头标出Meta-data部分,可以看到为序列化后结果。

澳门新葡萄京所有网站 22

输出了之前打包的phar文件中,test.txt文件内容,并成功实例化TestObject对象,调用了析构函数。

由于PHP仅通过stub部分判断文件是否为PHAR文件,我们可以通过添加文件头、修改后缀的方式绕过上传检测。

示例代码:

澳门新葡萄京所有网站 23