漏洞原理
<?php
...
$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field);
if (preg_match('/\W/', $bindName)) {
// 处理带非单词字符的字段名
$bindName = md5($bindName);
}
...
} elseif (in_array($exp, ['NOT IN', 'IN'])) {
// IN 查询
if ($value instanceof \Closure) {
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
} else {
$value = is_array($value) ? $value : explode(',', $value);
if (array_key_exists($field, $binds)) {
$bind = [];
$array = [];
foreach ($value as $k => $v) {
if ($this->query->isBind($bindName . '_in_' . $k)) {
$bindKey = $bindName . '_in_' . uniqid() . '_' . $k;
} else {
$bindKey = $bindName . '_in_' . $k;
}
$bind[$bindKey] = [$v, $bindType];
$array[] = ':' . $bindKey;
}
$this->query->bind($bind);
$zone = implode(',', $array);
} else {
$zone = implode(',', $this->parseValue($value, $field));
}
$whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? "''" : $zone) . ')';
}
在 $bindName 在前面进行了一次检测,正常来说是不会出现漏洞的。但如果 $value 是一个数组的情况下,这里会遍历 $value ,并将 $k 拼接进 $bindName 。
进行了预编译后为什么还存在SQL注入呢?因为设置了 PDO::ATTR_EMULATE_PREPARES => false ,那么预编译的过程中PDO不会进行模拟预处理,参数化绑定的整个过程都是和Mysql交互进行的,导致了SQL注入漏洞的出现。预编译虽然是一种防护SQL注入的一种方式,但是如果配置不当或者实现存在缺陷,任可能存在SQL注入漏洞。
详细的漏洞原理地址:ThinkPHP5 SQL注入漏洞 && PDO真/伪预处理分析 | 离别歌
环境搭建
cd vulhub/thinkphp/in-sqlinjection docker-compose up -d
漏洞复现(SQL注入漏洞)
访问 靶机ip:/index.php?ids[]=1&ids[]=2 查看靶场是否正常启动
通过在 ids[] 中添加单引号触发漏洞
构建payload进行sql注入
漏洞复现(敏感信息泄露)
在通过网页报错页面可以发现敏感信息泄露