HTTP参数传递类型差异产生的攻击面
字数 1740 2025-08-25 22:58:35
HTTP参数传递类型差异产生的攻击面分析
1. 概述
HTTP参数传递类型差异攻击是指利用后端语言对HTTP参数类型处理不一致的特性,通过传递非预期的数据类型(如数组、对象等)绕过安全限制的攻击方式。这种攻击面源于编程语言的松散类型特性以及框架对HTTP参数的处理方式。
2. PHP中的类型差异攻击
2.1 基本示例
<?php
$flag = "flag";
if (isset($_GET['ctf'])) {
if (@ereg("^[1-9]+$", $_GET['ctf']) === FALSE)
echo '必须输入数字才行';
else if (strpos($_GET['ctf'], '#biubiubiu') !== FALSE)
die('Flag: '.$flag);
else
echo '骚年,继续努力吧啊~';
}
?>
攻击方法:
- 使用
?ctf[]=1传递数组参数 ereg()函数处理数组时会返回NULL,从而绕过第一个检查strpos()处理数组时也会返回NULL,不等于FALSE,从而进入第二个条件分支
2.2 PHP中的其他利用点
- 数组传入字符串处理函数通常会返回NULL或特定值
- 常见弱类型比较绕过(如
==与===的区别) - 数组在字符串上下文中会被转换为"Array"字符串
3. Ruby中的类型差异攻击
3.1 示例代码分析
if params[:do] == "#{params[:name][0,7]} is working" then
# ...
ERB.new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
end
预期限制:
- 只取
name参数的前7个字符进行模板渲染 - 限制了模板注入的利用空间
非预期利用:
- 传递数组参数:
name[]=payload&name[]=1&... - Ruby中数组的切片操作
[0,7]会返回前7个数组元素而非前7个字符 - 可以构造包含完整payload的数组,绕过长度限制
3.2 Ruby特性分析
$a = "qwertyu"
$b = ["bbb", "cc", "d"]
puts "$a: #{$a[0,3]}" # 输出前3个字符
puts "$b: #{$b[0,3]}" # 输出前3个数组元素
输出:
$a: qwe
$b: ["bbb", "cc", "d"]
4. JavaScript/Node.js中的类型差异攻击
4.1 基本特性
var a = "abcedfghijtk";
var b = ["qwe", "rty", "uio"];
console.log(a[2]); // 'c'
console.log(b[2]); // 'uio'
4.2 字符串与数组的通用方法
通过比较字符串和数组的共有方法:
function arrayIntersection(a, b) {
// 取两个数组的交集
// ...
}
console.log(arrayIntersection(
Object.getOwnPropertyNames(a.constructor),
Object.getOwnPropertyNames(b.constructor)
));
输出:
["prototype", "slice", "indexOf", "lastIndexOf", "concat", "length", "name"]
4.3 Express框架的特殊特性
Express支持通过URL直接传递对象:
// 输入 ?name[123]=123&name[456]=asd
console.log(req.query.name);
// 输出: { '123': '123', '456': 'asd' }
// 输入 ?name[password]=123456
console.log(req.query.name.password);
// 输出: 123456
其他有趣的行为:
?name[]=123456&name[][a]=123→['123456', { a: '123' }]?name[a]=123456&name=b→{ a: '123456', b: true }?name[a]=123456&name[a]=b→{ a: ['123456', 'b'] }
4.4 实际CTF示例分析
if (req.query.name === undefined) {
return res.sendStatus(500);
} else if (typeof(req.query.name) === "string") {
if (req.query.name.startsWith('{') && req.query.name.endsWith('}')){
req.query.name = JSON.parse(req.query.name);
if (!/^key$/im.test(req.query.name.filename))
return res.sendStatus(500);
}
}
var filename = "";
if (req.query.name.filename.length > 3) {
for (let c of req.query.name.filename) {
if (c !== "/" && c !== ".") {
filename += c;
}
}
}
绕过方法:
- 构造
name为对象,filename为数组:
/admin23333_interface?name[filename]=../&name[filename]=f&name[filename]=l&name[filename]=a&name[filename]=g - 绕过字符串类型检查
- 数组的
.length和迭代器语法与字符串兼容 - 最终拼接成有效路径
../flag
5. 其他语言分析
5.1 Python
框架支持:
- Django和Flask默认只支持字符串参数
- web2py支持列表参数
- Tornado:
self.get_query_argument获取单个参数,self.get_query_arguments获取列表
语言特性:
a = "qwertyuiop"
b = ["aaa", "bbb", "ccc", "ddd"]
print(a[:3]) # "qwe"
print(b[:3]) # ["aaa", "bbb", "ccc"]
共有方法:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__',
'__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'count', 'index']
5.2 Java
- Spring Boot存在HPP漏洞(多个参数用逗号拼接)
- 原生JSP/Servlet严格类型,不易受此类攻击
5.3 Go
- Beego等框架提供严格的类型获取方法(
GetString,GetInt,GetStrings等) - 强类型特性使其安全性较高
5.4 ASP/ASPX
- 主要存在HPP漏洞(多个参数用逗号拼接)
- 不支持自动转换为其他数据类型
6. 防御建议
-
严格类型检查:
- 对所有输入参数进行明确的类型验证
- 使用严格比较运算符(如PHP的
===)
-
参数规范化:
- 将输入强制转换为预期类型
- 对数组/对象参数进行特殊处理
-
框架配置:
- 在Express等框架中禁用或限制复杂参数解析
- 使用中间件对输入进行预处理
-
安全编码实践:
- 避免依赖参数的隐式类型转换
- 对用户输入进行白名单验证
-
防御深度:
- 实施多层防御机制
- 关键操作前进行二次验证
7. 扩展攻击面
除了URL参数外,其他数据传输方式也可能存在类似问题:
- JSON数据:可能传递非预期类型
- XML数据:复杂结构可能被误解析
- Protobuf等二进制协议:类型处理差异
- 多部分表单数据:文件与字段混合处理
安全研究人员应关注各种数据传输方式中的类型处理差异,这往往能发现新的攻击向量。