php.netextract,挖掘暗藏ThinkPHP中的反序列利用链

前言

在Blackhat2018,来自Secarma的安全研究员Sam Thomas讲述了一种攻击PHP应用的新方式,使用phar伪协议可以在不使用unserialize()函数的情况下触发PHP反序列化漏洞,极大地扩展了PHP反序列化的攻击面并且开源了新工具PHPGGC,PHPGGC可以针对十数个PHP流行框架进行了反序列化利用链输出。

于是本文由此对中国最流行的PHP框架之一Thinkphp进行了反序列化利用链挖掘。

预备知识

1.PHP反序列化原理

PHP反序列化就是在读取一段字符串然后将字符串反序列化成php对象。

2.在PHP反序列化的过程中会自动执行一些魔术方法

方法名

调用条件

__call

调用不可访问或不存在的方法时被调用

__callStatic

调用不可访问或不存在的静态方法时被调用

__clone

进行对象clone时被调用,用来调整对象的克隆行为

__constuct

构建对象的时被调用;

__debuginfo

当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本

__destruct

明确销毁对象或脚本结束时被调用;

__get

读取不可访问或不存在属性时被调用

__invoke

当以函数方式调用对象时被调用

__isset

对不可访问或不存在的属性调用isset()或empty()时被调用

__set

当给不可访问或不存在属性赋值时被调用

__set_state

当调用var_export()导出类时,此静态方法被调用。用__set_state的返回值做为var_export的返回值。

__sleep

当使用serialize时被调用,当你不需要保存大对象的所有数据时很有用

__toString

当一个类被转换成字符串时被调用

__unset

对不可访问或不存在的属性进行unset时被调用

__wakeup

当使用unserialize时被调用,可用于做些对象的初始化操作

3.反序列化的常见起点

__wakeup 一定会调用

__destruct 一定会调用

__toString 当一个对象被反序列化后又被当做字符串使用

4.反序列化的常见中间跳板:

__toString 当一个对象被当做字符串使用

__get 读取不可访问或不存在属性时被调用

__set 当给不可访问或不存在属性赋值时被调用

__isset 对不可访问或不存在的属性调用isset()或empty()时被调用

形如 $this->$func();

5.反序列化的常见终点:

__call 调用不可访问或不存在的方法时被调用

call_user_func 一般php代码执行都会选择这里

call_user_func_array 一般php代码执行都会选择这里

6.Phar反序列化原理以及特征

phar://伪协议会在多个函数中反序列化其metadata部分

受影响的函数包括不限于如下:

(Thinkphp框架中暂未发现,略有遗憾)

漏洞挖掘

1.安装Thinkphp 5.1.37环境

首先去github下载Thinkphp的源码,现在Thinkphp已经分为2个部分,

https://github.com/top-think/framework/tags

https://github.com/top-think/thinkphp/tags

下载5.1.37(最新版)对应的版本号

将framework改名为为thinkphp放到think-5.1.37中

5d632dd29588422db936763aa95bf207.png

2.寻找反序列化的起始点

使用idea打开该文件夹,开启xdebug

直接Ctrl+Shift+F搜索 “__destruct(” 看到此处有其他方法调用,我们继续跟进

343a2c59aa558dc58ae7dac7239addf6.png

发现 Windows->removeFiles(); 中使用了 file_exists 方法,而且 $files 可控

查看 file_exists 的定义可以知道,$filename会被当做字符串处理,那么$filename->__toString()方法就会被调用

2d2d72746646f47d6f861f87361e6725.png

3.寻找反序列化的中间跳板

下面就要求寻找一个实现了__toString()方法的对象来作为跳板

30c91d6f8ca79e8c6870013b973621f3.png

此处thinkphp\library\think\model\concern\Conversion.php存在跳板可能

toArray() 函数中寻找一个满足条件的:

$可控变量->方法(参数可控)

这样可以去触发某个类的__call方法,

找到符合条件的一处,其中 “$relation” 和 “$name” 都是可控变量,$name需要为数组

$relation->visible($name);

c46137cde978f0634f62317e6bcd9caa.png

4.寻找反序列化代码执行点

下面我们需要寻找一个类满足以下2个条件

1.该类中没有”visible”方法

2.实现了__call方法

直接查找 “public function __call”

一般PHP中的__call方法都是用来进行容错或者是动态调用,所以一般会在__call方法中使用

__call_user_func($method, $args)

__call_user_func_array([$obj,$method], $args)

但是 public function __call($method, $args) 我们只能控制 $args,所以很多类都不可以用

经过查找发现 think-5.1.37/thinkphp/library/think/Request.php 中的 __call 使用了一个array取值的

这里的 $hook是我们可控的,所以我们可以设计一个数组 $hook= {“visable”=>”任意method”}

但是这里有个 array_unshift($args, $this); 会把$this放到$arg数组的第一个元素这样我们只能

如下图

cb8be1e62fd6fd49c4a4917deb835911.png

这种情况是很难执行命令的,但是Thinkphp作为一个web框架,

Request类中有一个特殊的功能就是过滤器 filter(ThinkPHP的多个远程代码执行都是出自此处)

所以可以尝试覆盖filter的方法去执行代码

寻找使用了过滤器的所有方法

发现input()函数满足条件

cde51530f10e851993cfb3f14e35935b.png

但是这个方法不能直接使用,$name是一个数组(由于前面判断条件 is_array($name)),(string)$name 会报错终止程序,所以不能直接使用这个函数

继续查找调用input方法的的函数

172ac1bf80cf13e19db1b6cf0dc2f1c7.png

这里发现一个函数 public function param($name = ”, $default = null, $filter = ”),如果能满足$name为字符串,就可以控制变量代码执行了

所以继续向上查找使用了param的方法

8dc7b7aa0969da5ac4f109a1a873d171.png

但是PHP有个特性,一个函数可以接收任意数量参数,超出的部分可以自动忽略

这里就发现isAjax/isPjax方法可以满足param的第一个参数为字符串,因为$this->config也是可控的

5.构造反序列化利用链

攻击链如下图所示

5a8897efb1868ce183227111174e615a.png

6.漏洞利用条件

使用的 ThinkPHP 5.1.X框架的程序中,满足以下任意条件:

1. 未经过滤直接使用反序列化操作

2. 可以文件上传且文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤

fffc0493b91d2b8c6c6124a51450e465.png

POC:

此漏洞仅影响 Thinkphp 5.1.X

参考

https://github.com/ambionics/phpggc

https://www.cnblogs.com/iamstudy/articles/thinkphp_5_x_rce_1.html

https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html

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

https://paper.seebug.org/680/

作者:斗象能力中心 TCC – 小胖虎

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值