前言
话还要从一次渗透测试任务说起,根据目标指纹在github上找到了某套基于thinkphp3的代码,登录口的代码大概长这样子:
<?php
namespace Home\Controller;
class IndexController {
public function login() {
$param = I('post.');
$param['password'] = md5($param['password']);
$data = M('Users')->where($param)->find();
}
}
可以看到这里直接将POST数组传入了where(
函数,这种操作是不是很眼熟呀Laraval有过这种问题,貌似CI还是YII也有过(不记得了)
分析代码
忽略可能导致的业务逻辑漏洞,这里极有可能存在sql注入问题,直接使用tp3.2.5的代码做为演示,跟入代码到ThinkPHP/Library/Think/Model.class.php
这里将传入的$_POST
数组最终存放到了$this->options['where']
就结束了,返回Model类
之后会进入到增删该查的sql语句构建代码中,示例代码用的是find(
,跟入代码:
上图红框的位置判断了find(
数组参数的键,也就是对之前sqli注入的修复
蓝色箭头是对$this->options['where']
(这里也就是$_POST
数组),根据数据表中的的字段类型进行相应的强制转化
这里值得注意的是,只针对数据表里有的字段进行类型转化,如果传入xxx=123
之类的并不会被删除过滤,但后面会怎么处理呢?
回到蓝色箭头后面红色箭头处的函数select(
,一直跟入src/ThinkPHP/Library/Think/Db/Driver.class.php
Think\Db\Driver->select
Think\Db\Driver->buildSelectSql
Think\Db\Driver->parseSql
Think\Db\Driver->parseWhere
最终来到Think\Db\Driver->parseWhere(
传入的部分$_POST
数组现在是$where
,可以看到箭头处中对以_
为前缀的键有特殊处理,跟进parseThinkWhere(
方法
这里声明了三种键值的不同处理方式,最终成为sql的where语句,这三种方式均可被利用,这里展示利用_string
和_complex
进行sql注入
剩下_query
有兴趣的师傅可自行尝试利用
参考
https://www.leavesongs.com/PENETRATION/cachet-from-laravel-sqli-to-bug-bounty.html