如下controller即可触发SQL注入:

code 区域

public function test()

{

$uname = I('get.uname');

$u = M('user')->where(array(

'uname' => $uname

))->find();

dump($u);

}

为什么?

我们看看代码。我从github下载的最新源码:https://github.com/liu21st/thinkphp

/ThinkPHP/Library/Think/Db/Driver.class.php 531行:

code 区域

// where子单元分析

protected function parseWhereItem($key,$val) {

$whereStr = '';

if(is_array($val)) {

if(is_string($val[0])) {

if(preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i',$val[0])) { // 比较运算

$whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);

}elseif(preg_match('/^(NOTLIKE|LIKE)$/i',$val[0])){// 模糊查找

if(is_array($val[1])) {

$likeLogic = isset($val[2])?strtoupper($val[2]):'OR';

if(in_array($likeLogic,array('AND','OR','XOR'))){

$likeStr = $this->comparison[strtolower($val[0])];

$like = array();

foreach ($val[1] as $item){

$like[] = $key.' '.$likeStr.' '.$this->parseValue($item);

}

$whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';

}

}else{

$whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);

}

}elseif('bind'==strtolower($val[0])){ // 使用表达式

$whereStr .= $key.' = :'.$val[1];

}elseif('exp'==strtolower($val[0])){ // 使用表达式

$whereStr .= $key.' '.$val[1];

}elseif(preg_match('/IN/i',$val[0])){ // IN 运算

if(isset($val[2]) && 'exp'==$val[2]) {

$whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];

}else{

if(is_string($val[1])) {

$val[1] = explode(',',$val[1]);

}

$zone = implode(',',$this->parseValue($val[1]));

$whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')';

}

}elseif(preg_match('/BETWEEN/i',$val[0])){ // BETWEEN运算

$data = is_string($val[1])? explode(',',$val[1]):$val[1];

$whereStr .= $key.' '.strtoupper($val[0]).' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]);

}else{

E(L('_EXPRESS_ERROR_').':'.$val[0]);

}

}else {

$count = count($val);

$rule = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ;

if(in_array($rule,array('AND','OR','XOR'))) {

$count = $count -1;

}else{

$rule = 'AND';

}

for($i=0;$i<$count;$i++) {

$data = is_array($val[$i])?$val[$i][1]:$val[$i];

if('exp'==strtolower($val[$i][0])) {

$whereStr .= $key.' '.$data.' '.$rule.' ';

}else{

$whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' ';

}

}

$whereStr = '( '.substr($whereStr,0,-4).' )';

}

}else {

//对字符串类型字段采用模糊匹配

$likeFields = $this->config['db_like_fields'];

if($likeFields && preg_match('/^('.$likeFields.')$/i',$key)) {

$whereStr .= $key.' LIKE '.$this->parseValue('%'.$val.'%');

}else {

$whereStr .= $key.' = '.$this->parseValue($val);

}

}

return $whereStr;

}

这就是处理where条件的函数,我们看到如下片段:

code 区域

}elseif(preg_match('/BETWEEN/i',$val[0])){ // BETWEEN运算

$data = is_string($val[1])? explode(',',$val[1]):$val[1];

$whereStr .= $key.' '.strtoupper($val[0]).' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]);

}

当匹配/BETWEEN/i和$val[0]时,则将strtoupper($val[0])直接插入了SQL语句。

这个匹配:preg_match('/BETWEEN/i',$val[0]),明显是有问题的。因为这个匹配没加^$也就是首尾限定,所以只要我们的$val[0]中含有between时,这个匹配就可以成立,就产生了一个SQL注入。

为了防止I函数对我们输入的过滤影响,我们看看I函数:

code 区域

function I($name,$default='',$filter=null,$datas=null) {

if(strpos($name,'/')){ // 指定修饰符

list($name,$type)     =    explode('/',$name,2);

}

if(strpos($name,'.')) { // 指定参数来源

list($method,$name) = explode('.',$name,2);

}else{ // 默认为自动判断

$method = 'param';

}

switch(strtolower($method)) {

case 'get' : $input =& $_GET;break;

case 'post' : $input =& $_POST;break;

case 'put' : parse_str(file_get_contents('php://input'), $input);break;

case 'param' :

switch($_SERVER['REQUEST_METHOD']) {

case 'POST':

$input = $_POST;

break;

case 'PUT':

parse_str(file_get_contents('php://input'), $input);

break;

default:

$input = $_GET;

}

break;

case 'path' :

$input = array();

if(!empty($_SERVER['PATH_INFO'])){

$depr = C('URL_PATHINFO_DEPR');

$input = explode($depr,trim($_SERVER['PATH_INFO'],$depr));

}

break;

case 'request' : $input =& $_REQUEST; break;

case 'session' : $input =& $_SESSION; break;

case 'cookie' : $input =& $_COOKIE; break;

case 'server' : $input =& $_SERVER; break;

case 'globals' : $input =& $GLOBALS; break;

case 'data' : $input =& $datas; break;

default:

return NULL;

}

if(''==$name) { // 获取全部变量

$data = $input;

$filters = isset($filter)?$filter:C('DEFAULT_FILTER');

if($filters) {

if(is_string($filters)){

$filters = explode(',',$filters);

}

foreach($filters as $filter){

$data = array_map_recursive($filter,$data); // 参数过滤

}

}

}elseif(isset($input[$name])) { // 取值操作

$data = $input[$name];

$filters = isset($filter)?$filter:C('DEFAULT_FILTER');

if($filters) {

if(is_string($filters)){

$filters = explode(',',$filters);

}elseif(is_int($filters)){

$filters = array($filters);

}

foreach($filters as $filter){

if(function_exists($filter)) {

$data = is_array($data) ? array_map_recursive($filter,$data) : $filter($data); // 参数过滤

}elseif(0===strpos($filter,'/')){

// 支持正则验证

if(1 !== preg_match($filter,(string)$data)){

return isset($default) ? $default : NULL;

}

}else{

$data = filter_var($data,is_int($filter) ? $filter : filter_id($filter));

if(false === $data) {

return isset($default) ? $default : NULL;

}

}

}

}

if(!empty($type)){

switch(strtolower($type)){

case 's': // 字符串

$data     =    (string)$data;

break;

case 'a':    // 数组

$data     =    (array)$data;

break;

case 'd':    // 数字

$data     =    (int)$data;

break;

case 'f':    // 浮点

$data     =    (float)$data;

break;

case 'b':    // 布尔

$data     =    (boolean)$data;

break;

}

}

}else{ // 变量默认值

$data = isset($default)?$default:NULL;

}

is_array($data) && array_walk_recursive($data,'think_filter');

return $data;

}

较前些版本有些改进:

1.加了类型强制转换$type,但在默认情况下$type是空的,强制类型转换是不存在的。

2.将is_array($data) && array_walk_recursive($data,'think_filter');放在最后一行。我们看看think_filter这个过滤函数:

code 区域

function think_filter(&$value){

// TODO 其他安全过滤

// 过滤查询特殊字符

if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|LIKE|NOTLIKE|BETWEEN|IN)$/i',$value)){

$value .= ' ';

}

}

这个实际上就是对我之前那个漏洞的一个解决方案,将一些关键词后面加空格。但我们看到,这个正则是存在"^$"首尾限定符的。所以只有传入参数完全"等于"BETWEEN的时候才会被加上空格,而且这里加上空格也不会影响漏洞的产生,因为漏洞位置的正则没有加^$首尾限定符。

还有一个说明:之前thinkphp出了个"错误"的补丁,这个补丁已经被官方去掉了,所以不用考虑那个补丁造成的一些干扰。

漏洞证明:

那我们回到最初那段代码:

code 区域

public function test()

{

$uname = I('get.uname');

$u = M('user')->where(array(

'uname' => $uname

))->find();

dump($u);

}

这个代码,我们来测试一下:

果然是有漏洞的,我们看看具体执行的SQL语句:

就在BETWEEN处。除了BETWEEN外还有IN,我就一块说明了。

Onethink演示:

细我就不说了,和之前一样。

修复方案:

正则一定要写明确:

/^BETWEEN$/i

版权声明:转载请注明来源 phith0n@乌云

ThinkPHP最新版本SQL注入漏洞的更多相关文章

  1. ref:ThinkPHP Builder.php SQL注入漏洞(<= 3.2.3)

    ThinkPHP Builder.php SQL注入漏洞(<= 3.2.3) ref:https://www.jianshu.com/p/18d06277161e TimeSHU 2018.04 ...

  2. DedeCMS V5.7sp2最新版本parse_str函数SQL注入漏洞

    织梦dedecms,在整个互联网中许多企业网站,个人网站,优化网站都在使用dede作为整个网站的开发架构,dedecms采用php+mysql数据库的架构来承载整个网站的运行与用户的访问,首页以及栏目 ...

  3. Drupal 7.31版本爆严重SQL注入漏洞

    今早有国外安全研究人员在Twitter上曝出了Drupal 7.31版本的最新SQL注入漏洞,并给出了利用测试的EXP代码. 在本地搭建Drupal7.31的环境,经过测试,发现该利用代码可成功执行并 ...

  4. 浅析PHP框架Laravel最新SQL注入漏洞

    PHP知名开发框架Laravel,之前在官方博客通报了一个高危SQL注入漏洞,这里简单分析下. 首先,这个漏洞属于网站coding写法不规范,官方给了提示: 但官方还是做了修补,升级最新版本V5.8. ...

  5. 【漏洞分析】Discuz! X系列全版本后台SQL注入漏洞

    0x01漏洞描述 Discuz!X全版本存在SQL注入漏洞.漏洞产生的原因是source\admincp\admincp_setting.php在处理$settingnew['uc']['appid' ...

  6. DEDECMS 5.7之前版本远程SQL注入漏洞

    2012/4/29 凌晨 知道创宇安全研究团队截获到最新DEDECMS SQL注入 0day,官网目前提供下载的最新版5.7也受影响,截止本告警发出时官方尚未给出补丁或解决方案,此漏洞利用简单且ded ...

  7. DedeCMS全版本通杀SQL注入漏洞利用代码及工具

    dedecms即织梦(PHP开源网站内容管理系统).织梦内容管理系统(DedeCms) 以简单.实用.开源而闻名,是国内最知名的PHP开源网站管理系统,也是使用用户最多的PHP类CMS系统,近日,网友 ...

  8. sqlmap查找SQL注入漏洞入门

    1.安装sqlmap sqlmap是一款非常强大的开源sql自动化注入工具,可以用来检测和利用sql注入漏洞.注意:sqlmap只是用来检测和利用sql注入点的,使用前请先使用扫描工具扫出sql注入点 ...

  9. WordPress WP-Realty插件‘listing_id’参数SQL注入漏洞

    漏洞名称: WordPress WP-Realty插件‘listing_id’参数SQL注入漏洞 CNNVD编号: CNNVD-201310-499 发布时间: 2013-10-23 更新时间: 20 ...

随机推荐

  1. sz的博客感想

    https://www.cnblogs.com/freeflying/p/4796369.html 在很多人眼里,当把中学和大学对比起来的时候,大学只能用一个字形容:“渣”.我的大学前两年过得比较随性 ...

  2. Golang教程:switch 语句

    switch 是一个条件语句,用于将一个表达式的求值结果与可能的值的列表进行匹配,并根据匹配结果执行相应的代码.可以认为 switch 语句是编写多个 if-else 子句的替代方式. 举例是说明问题 ...

  3. 利用WebBrowser控件实现百度自动搜索

    (1)新建一个MFC对话框项目 (2)对话框中添加WebBrower控件,添加方法:点击菜单栏工具->选择工具箱项->在弹出的选择工具箱项对话框选择COM组件->Microsoft  ...

  4. JS大写转小写小写转大写,JS字符串大小写互换

    Array.prototype.map.call(str,a=>a.toUpperCase(a)==a?a.toLowerCase():a.toUpperCase()).join(''); 效果 ...

  5. Java 线程--继承java.lang.Thread类实现线程

    现实生活中的很多事情是同时进行的,Java中为了模拟这种状态,引入了线程机制.先来看线程的基本概念. 线程是指进程中的一个执行场景,也就是执行流程,进程和线程的区别: 1.每个进程是一个应用程序,都有 ...

  6. 把一个项目a生成后放在另一个项目b使用(b项目是例子中的ScreenWebPage_Tool)

    a项目属性---生成事件---后期生成事件命令行   xcopy /r /y  $(TargetDir)*.* $(SolutionDir)ScreenWebPage_Tool\bin\Debug\* ...

  7. 关于css伪类,伪元素详解总结

    伪类 伪类就是一种虚构的状态或者说是一个具有特殊属性的元素可以使用CSS进行样式修饰.常见的几种伪类是: :link , :visited , :hover , :active , :first-ch ...

  8. javascript获取文件后缀名

    javascript获取文件后缀名:在需要验证文件格式的时候,首先就要获得文件的格式,下面是一个通过正则表达式获取文件后缀名的一个简单实例. function validate(){ var impo ...

  9. 纯css面板插件,自适应,多样式

    最近在做公司的系统后台,用的bootstrap,在设计布局的时候不喜欢他的面板,所以自己写了这个面板插件,分享给大家 先上预览图: 默认样式: 绿色: 黄色: 蓝: 红: 使用方法: 引用MoUi.c ...

  10. git 命令收藏

    git init # 初始化本地git仓库(创建新仓库)   git config --global user.name "xxx" # 配置用户名   git config -- ...