ThinkPHP5之SQLI审计分析(一)

news2024/11/19 5:36:21

说明

该文章来源于徒弟lu2ker转载至此处,更多文章可参考:https://github.com/lu2ker/

文章目录

  • 说明
      • 0x00 测试代码做了什么?
      • 0x01 调用链分析
      • 0x02 分析最内层调用的处理
      • 0x03 分析上一层调用的处理
      • 0x04 Payload构造

Time:8-31

影响版本:5.0.13<=ThinkPHP<=5.0.15、 5.1.0<=ThinkPHP<=5.1.5

Payload:

/public/index.php/index/index?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1

这是一篇由已知漏洞寻找利用过程的文章,跟着**参考链接**学习分析,以下是收获记录。


0x00 测试代码做了什么?

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $username = request()->get('username/a');
        db('users')->insert(['username' => $username]);
        return 'Update success';
    }
}

index控制器是默认的TP框架程序的入口,该测试代码在index控制器下新建了一个index方法(实际上本来的index方法是tp的欢迎页面,这里是覆盖替换掉原来的)。逐行来看该测试代码:获取get请求的username参数->进行数据库的insert操作->输出Update success

其中,username/a不明白是什么意思,就跟进get方法去看一下它怎么处理的:

thinkphp/library/think/Request.php

在这里插入图片描述

第676行可以看到$name允许是一个数组,但是测试代码中传入的是username/a字符串。674行GET方式接受的数据最后进入input方法,接着看下做了如何处理:

在这里插入图片描述

这里看到将$name参数以/分割为了$name$type,而$type接下来被用到的地方是强制类型转换:

在这里插入图片描述

到这里就知道了a是一种修饰符,查看开发手册得知所有的定义好的修饰符。

在这里插入图片描述

同样的下断点看也可以证明$username最后其实是一个数组:(并不是因为payload是数组形式,而是由/a修饰符决定的)

在这里插入图片描述

然后接下来就进入到执行数据库插入操作的insert方法了。(9行这条代码的意思是向users表的username字段插入$username)

0x01 调用链分析

现在已知$username是一个数组,传入insert方法。跟进insert跳到:

thinkphp/library/think/db/Query.php

在这里插入图片描述

发现其内又调用了$this->builder->insert方法,且注释是生成SQL语句。文件内搜索看看builder是如何定义的:

在这里插入图片描述

为了方便,直接在130行下断点,得到了$class的值:(这里可以随便传个参数比如username=1进去,因为主要是为了知道调用的哪里)

在这里插入图片描述

所以,$this->builder实际上是一个Mysql类的对象,而Mysql类**(thinkphp/library/think/db/builder/Mysql.php)**是继承于Builder类的,且Mysql类里面并没有实现insert方法,所以最终调用的还是Builder类的insert方法:

thinkphp/library/think/db/Builder.php

在这里插入图片描述

经过上述跟进代码分析,最终得知,入口调用的insert方法最终调用的实际上是调用的Builder->insert()方法来生成SQL语句。接下来应该由内向外分析看看有没有什么有效的过滤措施。

0x02 分析最内层调用的处理

分析这个生成SQL语句(之前有个注释)的方法具体做了什么,跟进它的parseData方法:(86行

	protected function parseData($data, $options)
    {
        if (empty($data)) {
            return [];
        }

        // 获取绑定信息
        $bind = $this->query->getFieldsBind($options['table']);
        if ('*' == $options['field']) {
            $fields = array_keys($bind);
        } else {
            $fields = $options['field'];
        }

        $result = [];
        foreach ($data as $key => $val) {
            $item = $this->parseKey($key, $options);
            if (is_object($val) && method_exists($val, '__toString')) {
                // 对象数据写入
                $val = $val->__toString();
            }
            if (false === strpos($key, '.') && !in_array($key, $fields, true)) {
                if ($options['strict']) {
                    throw new Exception('fields not exists:[' . $key . ']');
                }
            } elseif (is_null($val)) {
                $result[$item] = 'NULL';
            } elseif (is_array($val) && !empty($val)) {
                switch ($val[0]) {
                    case 'exp':
                        $result[$item] = $val[1];
                        break;
                    case 'inc':
                        $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
                        break;
                    case 'dec':
                        $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
                        break;
                }
            } elseif (is_scalar($val)) {
                // 过滤非标量数据
                if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
                    $result[$item] = $val;
                } else {
                    $key = str_replace('.', '_', $key);
                    $this->query->bind('data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
                    $result[$item] = ':data__' . $key;
                }
            }
        }
        return $result;
    }

这个函数最后return了$result变量,那么我们就看看函数体内是怎样处理$result变量的:

①首先,这个函数的第一个参数是$data,来源于最开始Query.phpinsert方法的2084行:

在这里插入图片描述

进行了一个数组合并操作把合并的结果再赋给$data,那么合并后的$data也是一个数组,且有一个键的键名为username(因为$data本来是['username' =>$username],在入口的第9行)。

②然后,这个函数在100行初始化了$result变量,然后101行用foreach分离$data$key$val$key变成了$item:

在这里插入图片描述

此时的$val是可控的、get方式传入的、等价于$username第113行判断如果$val是个数组,进入switch分支

在这里插入图片描述

$val[0]也就是payload中的username[0]根据不同的值进入不同的三个分支,但是可以看到每个分支的处理都是直接拼接$val[1]$val[2] 赋给 $result[$item],也就是$result['username'],唯一调用的parseKey并没有什么作用:

在这里插入图片描述

③最后,返回$result (也就是赋值给Query.phpinsert方法2084行的$data),至此parseData的主要功能就分析的差不多了。

接着看Builder类的insert方法在调用了parseData之后又干了什么:

在这里插入图片描述

$data中取出键值分别赋值给代表字段名和字段值的变量,简单的替换29-33行定义的SQL语句模型,返回生成好的SQL语句。

在这里插入图片描述

0x03 分析上一层调用的处理

在这里插入图片描述

之后还有两处调用函数,即getBind()getRealSql()但是跟进去看了下都没有任何数据清洗,仅仅是数据处理的一些解析操作。然后就到了execute()去执行SQL语句了。至此,整个处理流程基本上就分析完了,满足SQL注入漏洞的前提条件:①参数用户可控②参数直接拼接到SQL语句中,无任何有效过滤。

0x04 Payload构造

漏洞的利用点还是在最内层调用的parseData方法中,根据刚才的分析已经知道的 v a l 实际上就是传入的 u s e r n a m e ,那么 ‘ val实际上就是传入的username,那么` val实际上就是传入的username,那么val[0] v a l [ 1 ] ‘ 、 ‘ val[1]`、` val[1]val[2]`都是可控的,只要传入满足条件的值即可,因为是insert操作,所以选择用报错函数进行注入:

构造username[0]=inc,进入inc分支。

构造username[1]=updatexml(1,concat(0x7e,user(),0x7e),1),实际的报错语句。

构造username[2]=1,只是为了补齐数组元素个数。

再加上index控制器的index方法的访问路径/public/index.php/index/index/

最后连起来就是

/public/index.php/index/index/?username[0]=inc&username[1]=updatexml(1,concat(0x7e,user(),0x7e),1)&username[2]=1

本意是代码审计,就不考虑再如何利用了。

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

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

相关文章

pyTorch入门(六)——实战Android Minist OpenCV手写数字识别(附源码地址)

学更好的别人&#xff0c; 做更好的自己。 ——《微卡智享》 本文长度为4239字&#xff0c;预计阅读12分钟 前言 前面几篇文章实现了pyTorch训练模型&#xff0c;然后在Windows平台用C OpenCV DNN推理都实现了&#xff0c;这篇就来看看在Android端直接实现一个手写数字识别的功…

The Open Group亚太区总经理Chris Forde元旦贺词:踔厉奋发、笃行不怠,共赴新未来!

Happy New Year everyone, hope you are enjoying the holiday season, and perhaps planning your New Year’s resolutions. 大家新年快乐&#xff01;希望此刻您正在享受假期&#xff0c;或在规划自己的新年决心。 Now is the time for me, with you, to say goodbye to 202…

PDF怎么转换成Word?电脑必备的转换工具

电脑上的办公场景可以说是很多样了&#xff0c;而现在线上办公&#xff0c;线上会议&#xff0c;以及线上网课等的发展越来越全面&#xff0c;关于文件的编辑和传输也渐渐需要更多的软件来辅助我们办公。就像是PDF文件格式和Word文件格式这两种常见的格式&#xff0c;想要直接进…

小米路由器 R4A 刷原生 OpenWrt 后的风景

简 述: 继上篇 小米AX6S刷OpenWrt和开启OpenClash 后&#xff0c;手痒难耐&#xff0c;决定把小米路由器4A千兆版(R4A)路由器 给刷个原生的 OpenWrt。 文章目录背景刷成原生 OpenWrt原生 OpenWrt 基础操作开启 WiFiopkg 换源设置中文OpenClash 插件8M 之殇&#xff0c;终结Refe…

JavaSE学习(二)

1.基本数据类型转换 自动类型转换 1.java程序在进行赋值或运算的时候&#xff0c;会将精度小的类型自动转换为精度大的数据类型再进行计算 2.精度大的类型赋值给精度小的类型会报错&#xff0c;反之则会进行自动类型转换 int a4; floata1.1;这样写是错的&#xff0c;因为1.1是…

Uni-app + Vue3 + TS +Vite 创建项目

一、npx 与 npm 区别 npm 都很熟&#xff0c;可是与 npm 如此相似的 npx 是干嘛的呢&#xff1f;我们为甚要介绍 npx ? 由于 uni-app 官方提供创建命令使用的是 npx&#xff0c;所以我们先来了解下 npx 是干什么的&#xff1f;它与 npm 的区别。 npx 是 npm 的高级版本&…

java 出现unreachable statement异常 原因检查

unreachable statement异常&#xff1a; 今天在写代码的过程中&#xff0c;发现有行代码变红线&#xff0c;显示unreachable statement异常&#xff0c;但是代码本身没什么问题&#xff0c;通过查询资料发现其实就是该行代码不可执行的原因&#xff0c;出现该异常共有以下两种…

第三十八章 贪心算法——区间问题(上)

第三十八章 贪心策略——区间相关问题一、什么贪心策略&#xff1f;二、区间问题合集1、思路&#xff1a;2、问题1&#xff1a; 区间选点&#xff08;1&#xff09;问题&#xff08;2&#xff09;思路和证明a.思路b.证明&#xff08;3&#xff09;代码3、问题2&#xff1a;&…

Linux系统编程——基础篇

文章目录一、快捷键二、文件1.重要文件2.文件类型3.cp4.增加权限5.修改三、查找和检索四、安装五、压缩与解压六、vim的三种工作方式七、gcc编译四步骤八、静态库和动态库一、快捷键 Ctrla&#xff1a;光标移到开头 Ctrle&#xff1a;光标移到结尾 Ctrlu&#xff1a;清除整行 …

SQLSERVER 居然也能调 C# 代码 ?

一&#xff1a;背景 1. 讲故事 前些天看到一个奇怪的 Function 函数&#xff0c;调用的是 C# 链接库中的一个 UserLogin 方法&#xff0c;参考代码如下&#xff1a; CREATE FUNCTION dbo.clr_UserLogin (name AS NVARCHAR(100),password AS NVARCHAR(100) ) RETURNS INT AS…

Kali Linux中shutdown指令的用法3-1

在Kali Linux中&#xff0c;shutdown指令用于停止&#xff08;halt&#xff09;、关闭&#xff08;power off&#xff09;或者重启&#xff08;reboot&#xff09;系统。 1 语法格式 shutdown指令的语法如下所示 shutdown [OPTIONS] [TIME] [WALL] 其中&#xff0c;OPTIONS…

Qt、使用QToolButton和QStackedWidget的侧边栏(SideBar)的实现与实现原理解析

Qt、侧边栏&#xff08;SideBar&#xff09;的原理与实现&#xff08;附Demo&#xff09; 目录Qt、侧边栏&#xff08;SideBar&#xff09;的原理与实现&#xff08;附Demo&#xff09;1、简介2、侧边栏控件组成3、UI布局4、代码实现界面的切换Demo下载&#xff1a;https://git…

2023四川大学图书情报档案专业考研初试介绍(2023.1.02已更新)

文章目录川大图情基本情况2023年招生情况近5年录取数据复试2021-2022年复试线学硕复试线图情专硕复试线2021-2022年复试录取分数2022年学硕部分拟录取人员详细分数(不含调剂)专业课备考专业课资料博主所售资料一览667科目备考参考策略972科目备考方法参考目标分数川大图情基本情…

分享一套开源的springboot制造执行MES系统源码,带本地部署搭建教程+运行文档

全开源的一套超有价值的JAVA制造执行MES系统源码 亲测 带本地部署搭建教程 教你如何在本地运行运行起来。 开发环境&#xff1a;jdk1./1.8 tomcat mysql5.6springmvcmaven 需要源码学习&#xff0c;私信我获取。 一、系统概述&#xff1a; MES制造执行系统&#xff0c;其定位…

十分钟入门HBase特性与安装部署

1.写在前面 目前Hadoop生态的大数据组件都有一个其本身擅长的领域&#xff0c;并且目前看来&#xff0c;这个领域相对较窄&#xff0c;所以各位学生在大数据相关活动中&#xff0c;难免会有技术交集&#xff0c;最近学生在做离线数仓项目的时候&#xff0c;采用kylin技术组件&a…

【MySQL进阶教程】 存储引擎详细介绍

前言 本文为 【MySQL进阶教程】 存储引擎 相关知识介绍&#xff0c;下边具体将对MySQL体系结构&#xff0c;存储引擎介绍&#xff0c;存储引擎特点&#xff08;包含&#xff1a;InnoDB、MyISAM、Memory的特点及对比&#xff09;&#xff0c;存储引擎选择等进行详尽介绍~ &…

学习SpringCloudAlibaba(一)

一、为什么使用SpringCloud Alibaba 有了spring cloud这个微服务的框架&#xff0c;为什么又要使用spring cloud alibaba这个框架了&#xff1f; 最重要的原因在于spring cloud中的几乎所有的组件都使用Netflix公司的产品&#xff0c;然后在其基础上做了一层封装。然而Netfli…

走过 2022

“听过很多道理&#xff0c;依然过不好这一生”。每年写年终总结也是。但是审视自己在过去一年的表现依然是必需的。“吾日三省吾身”&#xff0c;更好的当然是每天都有所反思。世间很多事都离不开反馈&#xff0c;写总结就是一个很好的反馈。经历了过去荒诞的一年&#xff0c;…

开源虚拟机 qemu 安装以及使用方法 (helloos.img)

这篇文章里有 30Day Make OS 光盘的内容&#xff0c;感谢博主 https://blog.csdn.net/monster663/article/details/115919391 链接&#xff1a;https://pan.baidu.com/s/18dz8CuOxN21EAIU3os2KpA 提取码&#xff1a;qwer qemu 牛啤&#xff01; 从 https://www.qemu.org/down…

【阶段一】Python快速入门05篇:高级特性、pip工具、模块的使用、类(class)与异常处理

本篇的思维导图: 高级特性 列表生成式 现在有一个列表,你需要对该列表中的每个值求平方,然后将结果组成一个新列表。 描述 代码