fuzz 普通程序和数据库有哪些不同点
1. 输入和协议的复杂性
- 普通程序:通常处理简单格式的输入,如文件、命令行参数或简单的网络数据包。这些输入的格式相对单一,fuzzer 很容易理解和变异。例如,对一个图片解析器进行fuzzing,输入就是图片文件
- 数据库:数据库的输入是复杂的、有状态的网络协议和查询语言。fuzzer 需要理解和生成符合特定数据库协议(如 MySQL 的二进制协议、PostgreSQL 的文本协议)的数据包。更重要的是,数据库的查询语言(SQL)本身就极为复杂,包含多种数据类型、函数、联结操作和语法结构。fuzzer 不仅要生成畸形的协议数据,还要生成语法正确但语义畸形的 SQL 查询语句,比如超长的字符串、负数、特殊字符等
2. 状态管理和序列依赖
- 普通程序:大多数普通程序是无状态的。每次运行fuzzer,程序都从一个干净的状态开始,处理一个单独的输入。这使得fuzzer很容易追踪和重现漏洞
- 数据库:数据库是有状态的。一个查询的执行结果可能依赖于之前的查询。例如,你必须先创建一张表,然后才能向其中插入数据,最后才能查询。这意味着fuzzer需要生成一系列有逻辑关系的查询序列,而不是单个独立的输入。如果一个漏洞需要多次操作才能触发,fuzzer 必须能够管理和生成这个操作序列
3. 性能和效率
- 普通程序:普通程序的fuzzing通常很快。一个文件解析器在毫秒级就能处理一个输入。这使得覆盖率引导的fuzzer(如 AFL)能够每秒测试数千甚至数万次,从而快速找到漏洞
- 数据库:数据库的fuzzing通常很慢。每次建立连接、发送查询、接收响应都需要时间。此外,一个查询可能需要几毫秒甚至几秒来执行。这大大降低了fuzzer的测试速度,因此需要更智能的fuzzing策略
4. 漏洞类型和检测方法
- 普通程序:fuzzing通常用于寻找内存安全漏洞,如缓冲区溢出、空指针解引用等。当程序崩溃时,fuzzer会立即捕获到异常
- 数据库:数据库的漏洞类型更为多样,不仅包括内存安全问题,还包括:
- 逻辑漏洞:错误的查询结果、数据损坏等。这些漏洞不会导致程序崩溃,fuzzer需要有专门的逻辑来检测
- 权限绕过:在低权限下执行高权限操作
- SQL 注入:这是一种特殊的逻辑漏洞,fuzzing可以用于寻找新的注入点
由于很多数据库漏洞不会导致程序崩溃,fuzzer需要额外的断言(assertions)或 Oracle 来检测。例如,fuzzer可以执行一个查询,然后用一个已知正确的查询来验证结果是否一致。如果不一致,就可能存在逻辑漏洞
5. 架构和环境的复杂性
- 普通程序:通常在一个单一进程中运行,fuzzer可以直接附加或作为子进程运行
- 数据库:数据库是复杂的系统,通常是多进程或多线程的。这使得传统的fuzzer很难精确地追踪代码覆盖率。例如,一个主进程可能接受连接,然后派生出多个子进程来处理查询。fuzzer需要能够跟踪和监控这些子进程
特性 | Fuzzing 普通程序 | Fuzzing 数据库 |
---|---|---|
输入 | 单一,通常是文件或简单网络数据 | 复杂,包含协议和查询语言 |
状态 | 无状态,每个输入独立 | 有状态,需要管理查询序列 |
速度 | 非常快,每秒数千次以上 | 较慢,受网络和查询执行速度影响 |
漏洞类型 | 主要是内存安全漏洞(崩溃) | 内存安全、逻辑漏洞、权限问题等 |
检测方法 | 捕获崩溃 | 捕获崩溃,并需要断言或Oracle来检测非崩溃漏洞 |
挑战 | 探索代码路径和绕过输入验证 | 管理状态、生成复杂输入、处理低速和多进程环境 |