审计之PHP反序列化漏洞详解(附实例)

Posted 猎户攻防实验室

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了审计之PHP反序列化漏洞详解(附实例)相关的知识,希望对你有一定的参考价值。

OWASP Top10 中反序列化已单独分类, php反序列化漏洞是一种常见的漏洞,本篇以源码原理角度分析,希望能深入浅出的让各位即使没有安全背景的读者明白什么是PHP反序列化漏洞,以及如何利用。

  如有错误不足之处,请不吝指教。

==== 上卷 —— 科普 ====

1. 前置知识

PHP类与对象

PHP魔术方法

PHP反序列化方法

1.1 PHP类与对象

先从最简单的开始。

在PHP中,定义一个类,和定义一个类的方法。

一个简单的例子:

1.2 PHP魔术方法

在PHP官方网站中的定义:

__construct() __destruct() __call() __callStatic() __get() __set() __isset() __unset() __sleep() __wakeup() __toString() __invoke() __set_state() __clone() __debugInfo() 等方法在 PHP 中被称为"魔术方法"(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。

咱们来简单讲解几个魔术方法

1. __construst()

2. __destruct()

3. __toString()

__construst() 方法在每次创建新对象时会被自动调用

__destruct() 方法在使用 exit() 终止脚本运行时也会被自动调用

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

代码:

审计之PHP反序列化漏洞详解(附实例)

输出结果:

审计之PHP反序列化漏洞详解(附实例)

1.3 PHP对象序列化

在PHP网站中的定义:

所有php里面的值都可以使用函数serialize() 来返回一个包含字节流的字符串来表示。unserialize() 函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

简单的理解序列化:就是把一个类的实例变成一个字符串。

简单的理解反序列化:把一个特殊的字符串转换成一个实例。

代码:

审计之PHP反序列化漏洞详解(附实例)

输出结果:

审计之PHP反序列化漏洞详解(附实例)

亮部分即为序列化后的对象。

反序列化的操作:

审计之PHP反序列化漏洞详解(附实例)

代码执行效果:

审计之PHP反序列化漏洞详解(附实例)

想要深入了解序列化后的字符的具体意义,请参考这个链接:

http://php.net/manual/zh/function.serialize.php


2. 小试牛刀

通过上面的前置知识,相信大家已经对PHP序列化相关的内容有一个初步的认识。下面来开始分析一个简单的例子。

审计之PHP反序列化漏洞详解(附实例)

这是一个WTFLog 类,调用 loginfo 方法会记录一条记录到access.log文件中,WTF的地方是,当这个类完成它的使命的时候(exit),会删除掉它所记录的文件。

重点来了...

这里有一个“正常”的业务,pets.php

审计之PHP反序列化漏洞详解(附实例)

该代码的业务目标是从用户处收集序列化后的数据,并进行一些操作。

该代码段的特点是:直接使用客户端可以控制的输入点($_GET['pet_serialized']),在不进行验证的情况下,直接实例化了这段代码!!

看我们如何利用这个危险的输入点:

还记得刚才的WTFLog吧!

先新创建一个新的poc.php,把 log.php 内的WTFLog类引入进来。

审计之PHP反序列化漏洞详解(附实例)

index.php 是一个很有用的文件,会输出一句话。

审计之PHP反序列化漏洞详解(附实例)

运行 poc.php ,得到我们想要的序列化后的WTFLog 字符串。

审计之PHP反序列化漏洞详解(附实例)

反序列化字符串:O:6:"WTFLog":1:{s:8:"filename";s:9:"index.php";}

这里利用这个字符串,去调用pets.php

审计之PHP反序列化漏洞详解(附实例)

本来要删除掉access.log这个文件,却删除了我们的重要文件index.php

验证一下:

审计之PHP反序列化漏洞详解(附实例)

到了这一步,漏洞已经利用完成。

回顾一下整个漏洞利用的过程:

1. 需要有一个漏洞触发点 pets.php 内的 $pet = unserialize($_GET['pet_serialized']);

2. 需要有一个相关联的,有魔术方法(会被自动调用)的类。log.phpWTFLog 内的__destruct函数)

3. 漏洞的效果取决于__destruct 这个魔术函数内的操作,这里的是可以操控的可以删除的log 的 filename。

4. 构建poc.php ,利用程序,先序列化后,从可控输入$_GET['pet_serialized']输入进去。

5. 完成。


大家从上面的过程中对PHP反序列化的方式及危害有了一个比较直观的理解了。那如何利用PHP反序列化这个漏洞来做更多的事情呢,这里需要看更多的魔术方法的自动触发的情景,根据不同的使用情景(代码实现方式),来定制我们的Poc,如果可控代码中,存在call_user_func() 这个函数,可以实现通过PHP反序列化漏洞来进行任意代码执行。


==== 下卷 —— 实战 ====


结合上一部分PHP反序列化的介绍,炒一下冷饭,给大家分析一下201710月份出的 Typecho 反序列化漏洞。

本文的目的是把反序列化漏洞讲清楚,奶妈级讲解方式,让你看明白。

1. 漏洞回顾

示例漏洞利用的过程:

1. 需要有一个漏洞触发点 :pets.php 内的 $pet = unserialize($_GET['pet_serialized']);

2. 需要有一个相关联的,有魔术方法(会被自动调用)的类。log.php (WTFLog 内的__destruct函数)。

3. 漏洞的效果取决于__destruct 这个魔术函数内的操作,这里的是可以操控的可以删除的log 的 filename。

4. 构建poc.php ,利用程序,先序列化后,从可控输入$_GET['pet_serialized']输入进去。

5. 完成。


2. 代码回溯

在本文编写时,Typecho早已发布了很多新版本,让我们从git logs 中,找到存在漏洞的版本。

审计之PHP反序列化漏洞详解(附实例)

根据漏洞发生时间:10月左右,commit message : 漏洞 来确定具体的commit是哪一个

审计之PHP反序列化漏洞详解(附实例)

可以看到e277141 是已经修复后的commit,那么242fc1a就是我们需要回溯到的版本。

通过git 方式下载仓库以后。切换到目标版本。

审计之PHP反序列化漏洞详解(附实例)


3. 漏洞分析

Typecho 反序列化漏洞存在于install.php 文件中,让我们按照之前的思路来分析一下。

1.%2 寻找可控的反序列化输入点。

install.php中,通过搜索 unserialize 关键词,发现两个相关点。

审计之PHP反序列化漏洞详解(附实例)

看下代码:

install.php

审计之PHP反序列化漏洞详解(附实例)

232行是漏洞关键输入点。那么Typecho_Cookie::get()是啥?

看下代码:

Cookie.php Typecho_Cookie

审计之PHP反序列化漏洞详解(附实例)

A: “获取指定的COOKIE值”

那么,232 行的意思是:反序列化了用base64解码后的key为__typecho_config 的 cookie 内容。

cookie 可控,还有一个关键点是如何进入这段代码。

一起来看一下

审计之PHP反序列化漏洞详解(附实例)

执行到232行漏洞触发点的前提:

1. 设置finish参数

2. 设置refer,来源于漏洞站就ok

第一步已经搞定。下面来分析第二步。

3.2 需要有一个相关联的,有魔术方法(会被自动调用)的类

我们重新来看下232行开始的代码。

审计之PHP反序列化漏洞详解(附实例)

序列化后的内容赋值给了$config

234行把$config['adapter'] , $config['prefix']作为参数传入了Typecho_Db类。

复习一下之前说过的magic method。

1. __construst()

2. __destruct()

3. __toString()

__construst() 方法在每次创建新对象时会被自动调用

__destruct() 方法在使用 exit() 终止脚本运行时也会被自动调用

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

查看Typecho_Db类查看下源代码。

Db.php

审计之PHP反序列化漏洞详解(附实例)

 

114 行,$adapterName 作为参数被传进来以后,在120行进行了字符串拼接操作,字符串拼接操作会触发实例化类的__toString()方法, 那我们的目标就可以放在__toString()这个magic method 的方向上了。

搜索__toString(),

审计之PHP反序列化漏洞详解(附实例)

当我们找到一个符合条件的类的时候,就要确定这个类的magic method 中执行了什么东西。在Typecho_Feed这个类的__toString()中,没有跟上文章中举例的一样的方法,这里就要另辟蹊径。

3.3 寻找能利用的利用点

审计之PHP反序列化漏洞详解(附实例)

在290行,我们可控的$item['auther']调用了一个screenName 这个属性。

在PHP中,如果该实例化的对象访问了一个‘不可访问’的属性的时候,就会调用__get()方法。

这时候我们需要再次寻找 包含 __get() 的类。

审计之PHP反序列化漏洞详解(附实例)

审计之PHP反序列化漏洞详解(附实例)

这里的__get()调用了get()

继续看get()

审计之PHP反序列化漏洞详解(附实例)

298行到310行都是一些赋值操作。

追踪一下_applyFilter()

审计之PHP反序列化漏洞详解(附实例)

终于见到了我们想要的可以执行操作的函数call_user_func

call_user_func把第一个参数作为回调函数调用

 通过这个函数,可以执行函数。

 

回溯一下参数传递。从后到前。

 

[Request.php]  call_user_func() 的参数 $filter 可控,来自

审计之PHP反序列化漏洞详解(附实例)

$value 参数可控,来自_applyFilter() 的参数 $value。

_appleyFilter() 参数来自get()方法的$key。

 

审计之PHP反序列化漏洞详解(附实例)

审计之PHP反序列化漏洞详解(附实例)

 

_get($key) 直接调用get($key)

 

看下Feed.php

[Feed.php]  $item['author']->screenName 调用了 _get()方法。

$item['auther']->screenName 的$item 来自 $this->_item

审计之PHP反序列化漏洞详解(附实例)

3.4 构建POC

通过第三点的分析,来构建Poc

再顺过来整理一遍。

install.php - > Db.php -> Feed.php -> Request.php

1. install.php 漏洞触发点,存在unserialize()函数,序列化后的结果传给了Typecho_DB()

2. Db.php 中 120行

$adapterName = 'Typecho_Db_Adapter_' . $adapterName;触发了__toString()方法。

3. 找到存在__toString()并有可以利用的Feed.php,在其中调用了

$item['author']->screenName,调用__get()方法。

4. 找到__get()方法的Request.php ,调用了get()->调用了_appleyFilter()->call_user_func()

5. 结束、

Poc:test.php

审计之PHP反序列化漏洞详解(附实例)

先访问:

审计之PHP反序列化漏洞详解(附实例)

设置cookie __typecho_config 为test的输出值

审计之PHP反序列化漏洞详解(附实例)

设置refer & url

审计之PHP反序列化漏洞详解(附实例)

成功写入shell


4. 结语

PHP反序列化漏洞详细教程及实例(下)的分析到这里,就告一段落了。

如果还有疑问,欢迎来讨论。

同时,非常感谢下面的小伙伴的分析。在编写这篇文章的过程中,起到了参考和启发的作用。

Refer

http://p0sec.net/index.php/archives/114/

https://paper.seebug.org/424/

https://paper.tuisec.win/detail/c1ecf917be22318.jsp     


 


以上是关于审计之PHP反序列化漏洞详解(附实例)的主要内容,如果未能解决你的问题,请参考以下文章

15-PHP代码审计——yii 2.0.37反序列化漏洞

15-PHP代码审计——yii 2.0.37反序列化漏洞

PHP反序列化漏洞代码审计—学习资料

16-PHP代码审计——Typecho1.0.14反序列化漏洞

16-PHP代码审计——Typecho1.0.14反序列化漏洞

详解PHP反序列化漏洞