协议层SQL注入走私攻击
来源: DEF CON 32 - SQL Injection Isn’t Dead: Smuggling Queries at the Protocol Level
作者: Paul Gerste (SonarSource)
日期: 2024年8月10日
核心思想
将 HTTP 请求走私(Request Smuggling)的概念应用到二进制数据库协议层面,通过整数溢出实现 SQL 注入。
先前研究
James Kettle 的 HTTP Desync Attacks 启发了本研究的思路:
- 通过制造 HTTP 请求边界解析的不一致实现攻击
- 文本解析差异:
chunkedvs[\t]chunked - 逻辑差异:
Content-LengthvsTransfer-Encoding
问题: 其他协议是否也存在类似问题?
二进制协议的边界问题
分隔符(Delimiters)
- 如空字符结尾的字符串
- 攻击方式:在值中插入分隔符
长度字段(Length Fields)
- 如 Type-Length-Value (TLV) 协议
- 攻击方式:整数溢出、字节序问题
为什么选择数据库协议?
| 优势 | 说明 |
|---|---|
| 适用性 | 几乎所有 Web 应用都有数据库 |
| 严重性 | 数据库包含敏感数据(PII、认证信息) |
| 可利用性 | 大多数查询包含用户输入 |
数据库协议对比
| 数据库 | 消息格式 |
|---|---|
| PostgreSQL | Type-Length-Value |
| MySQL | Length-Sequence-Value |
| Redis | Type-Length-Delimiter-Value-Delimiter |
| MongoDB | messageLength-requestID-responseTo-opCode-value |
PostgreSQL 案例
协议格式
1 | Type (1字节) | Length (4字节整数) | Value |
Length 字段最大值为 2³²-1
漏洞代码 (pgx)
1 | func (src *Bind) Encode(dst []byte) []byte { |
问题:len(dst[sp:]) 返回 int 类型,强制转换为 int32 时发生截断,当消息大小超过 2³² 时长度字段溢出。
消息大小溢出
正常消息:
1 | Size: 8 = 0x00000008 |
溢出消息:
1 | Size: 2³²+4 = 0x100000004 |
攻击影响
- 可注入完整 SQL 语句(不受 UNION、子查询限制)
- 类似堆叠查询(Stacked Queries)效果
- 可读取/写入/删除数据库中所有数据
- 直接数据外传较困难(应用只处理第一个响应)
Payload 构造:NOP Sled
使用大量最小消息提高命中率:
1 | Type | Length |
成功率:约 20%(最多 5 次尝试)
Payload 构造:Trampolines
利用长度字节作为有效消息类型:
1 | Type | Length |
限制:第一个长度字节不能 > 0x3F
解决方案:交替模式
1 | Type | Length |
成功率:50%(最多 2 次尝试)
受影响的 PostgreSQL 库
| 语言 | 库 | 可利用 | 修复版本 |
|---|---|---|---|
| Go | pgx | ✅ | 4.18.2, 5.5.4 |
| Go | pg | ✅ | 未修复 |
| Go | pgdriver | ✅ | 未修复 |
| Go | pq | ✅ | 未修复 |
| C# | Npgsql | ✅ | 4.0.14, 5.0.18, 6.0.11, 7.0.7, 8.0.3 |
| Java | pgjdbc | ❌ | - |
| JS | pg | ❌ | - |
真实案例:Harbor
- Harbor 容器镜像仓库(CNCF 毕业项目)
- 默认配置存在漏洞
- 无需认证即可利用
- 2.11.0 版本通过更新 pgx 修复
MongoDB 案例
协议格式
1 | messageLength | requestID | responseTo | opCode | value |
漏洞代码 (mongodb Rust)
1 | async fn write_to<T: AsyncWrite + Send + Unpin>(&self, mut writer: T) -> Result<()> { |
Payload 构造
问题:Payload 必须是有效的 UTF-8
解决方案:利用 BSON 文档元数据构造恶意字节
1 | Query: { title: "A" * (0x7dd - 1), genre: "SciFi", ... } |
受影响的 MongoDB 库
| 语言 | 库 | 可利用 | 修复版本 |
|---|---|---|---|
| Rust | mongodb | ✅ | 2.8.2 |
| Python | pymongo | ❌ | - |
| Go | mongo | ❌ | - |
| Java | mongo-java-driver | ❌ | - |
| JS | mongodb | ❌ | - |
绕过大 Payload 限制
常见防护
- 默认请求体大小限制
- JSON/表单解码大小限制
- 反向代理大小限制
绕过方法
未保护端点
- 部分端点无默认限制(如 Harbor)
压缩绕过
- 部分服务器在解压前检查大小(Nginx、Fastify)
WebSocket
- 许多过滤器不适用,支持大消息
替代 Body 类型
- 如 multipart 表单,部分过滤器不适用
服务端构造
- 通过 SSRF、模板引擎、i18n 等在服务端创建大字符串
语言对大 Payload 的支持
| 语言 | 最大字符串 | 最大缓冲区 | 静默溢出 |
|---|---|---|---|
| Go | >2³² | >2³² | ✅ |
| Java | 2³¹-1 | 2³¹-1 | ✅ |
| C# | 2³¹-1 | >2³² | ✅ |
| JS | 2²⁹-24 | >2³² | 取决于实现 |
| Python | >2³² | >2³² | ❌ |
| Rust | >2³² | >2³² | Release 模式 |
未来研究
检测方法
- 白盒测试:搭建自己的测试环境
- 黑盒测试:需要更多研究和工具
- 如何安全地检测漏洞库?
扩展研究
更多协议:
- 其他数据库
- 缓存系统
- 消息队列
更多攻击技术:
- 分隔符相关的攻击
- 2 字节长度字段(仅需 65KB vs 4GB)
Takeaways
- 整数溢出在内存安全语言中仍然相关
- 发送大量数据是可行的
- SQL 注入没有消亡——如果常规方法不行,就深入一层!
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 !


