# Manul    Manul 是一个覆盖率引导的并行模糊测试器,适用于 Windows、Linux 和 macOS(测试版)上的开源及黑盒二进制文件,完全用纯 Python 编写。 # 快速开始 ``` pip3 install psutil git clone https://github.com/mxmssh/manul cd manul mkdir in mkdir out echo "AAAAAA" > in/test python3 manul.py -i in -o out -n 4 "linux/test/test_afl @@" ``` ### 安装 Radamsa ``` sudo apt-get install gcc make git wget git clone https://gitlab.com/akihe/radamsa.git && cd radamsa && make && sudo make install ``` 在 Windows 上无需安装 radamsa,Manul 附带了该平台下的 radamsa 原生库。 # 公开 CVE 列表 | CVE ID | 产品 | 发现者 | |----------------------------------------------|----------|----------------| | CVE-2019-9631 CVE-2019-7310 CVE-2019-9959 | Poppler | Maksim Shudrak | | CVE-2018-17019 CVE-2018-16807 CVE-2019-12175 | Bro/Zeek | Maksim Shudrak | 如果你使用 Manul 发现了新漏洞,请联系我,我会将你加入列表。 # 演示  # 依赖 0. Python3(Python2 自 2020 年 1 月 1 日起已弃用,但 Manul 在 Python2 下仍能正常工作) 1. [psutil](https://pypi.org/project/psutil/) 2. Windows 平台上的 pywin32(`pip install pywin32`)(仅 DBI 持久模式需要)。 # 覆盖率引导的模糊测试 目前,Manul 支持两种插桩方式:基于 AFL 的插桩(afl-gcc、afl-clang 和 [afl-clang-fast](https://github.com/mirrorer/afl/tree/master/llvm_mode))和 DBI。 ## 覆盖率引导的模糊测试(AFL 插桩模式) 使用 `afl-gcc` 或 `afl-clang-fast` 以及 `Address Sanitizer`(建议使用以获得更好效果)对目标进行插桩。例如: ``` CC=afl-gcc CXX=afl-g++ CFLAGS=-fsanitize=address CXXFLAGS=-fsanitize=address cmake <目标路径> make -j 8 ``` ``` USE_ASAN=1 CC=afl-clang-fast CXX=afl-clang-fast++ cmake <目标路径> make -j 8 ``` 更多详情请参阅 [这些说明](http://lcamtuf.coredump.cx/afl/QuickStartGuide.txt)。 ## 覆盖率引导的模糊测试(DBI 模式) 在此模式下,你无需对目标进行插桩,但需要下载适用于 Windows 或 Linux 的最新版 DynamoRIO 框架。 Manul 附带了适用于 Linux 和 Windows 的 x86/x64 预编译客户端。你可以在以下文件夹中找到它们: ``` linux/dbi_32|dbi_64/libbinafl.so (DynamoRIO 客户端) win/dbi_32|dbi_64/binafl.dll ``` 遗憾的是,DynamoRIO 在 macOS 上并未得到官方支持。 ### 使用 DynamoRIO 对黑盒二进制文件进行模糊测试 你可以在 [DynamoRIO 下载页面](https://github.com/DynamoRIO/dynamorio/wiki/Downloads) 找到最新的 DynamoRIO 发行包。 你需要在 `manul.config` 文件中取消以下行的注释,并提供 DynamoRIO 启动器和客户端的正确路径。 ``` # 选择 DBI 框架以向 Manul 提供覆盖率("dynamorio" 或 "pin")。例如 dbi = dynamorio dbi = dynamorio # 如果 dbi 参数不为 None,则需要指定 DBI 引擎启动器和 DBI 客户端的路径。 dbi_root = /home/max/DynamoRIO/bin64/drrun dbi_client_root = /home/max/manul/linux/dbi_64/libbinafl.so dbi_client_libs = None ``` 此外,你可以通过使用持久化内存内模糊测试来提高黑盒模糊测试的性能。在此模式下,你应该指示 Manul 对特定函数进行插桩(是的,你需要通过反汇编找到它),并通过取消以下行的注释使其循环运行: ``` dbi_persistence_mode = 1 dbi_target_module = afl_test dbi_target_method = open_file #dbi_target_offset = 0x3198 # 可选,可以提供函数偏移而非名称 dbi_fuzz_iterations = 1000 ``` winAFL 的作者在[这里](https://github.com/googleprojectzero/winafl/blob/master/readme_dr.md#in-app-persistence-mode)提供了关于其工作原理的详细说明。Manul 使用了 winAFL 插桩库的大部分代码来与目标通信并进行插桩。 重要提示:模糊测试 32 位二进制文件时应使用 32 位启动器和 32 位客户端,模糊测试 64 位二进制文件时应使用 64 位启动器和 64 位客户端! #### 编译 DynamoRIO 客户端库 要编译插桩库,你需要使用最新版本的 [DynamoRIO](https://github.com/DynamoRIO/dynamorio/releases/download/)。插桩库的源代码可以在 Manul 主文件夹下的 `dbi_clients_src` 中找到。 ``` 64 位 Linux cd dbi_clients_src wget <DynamoRIO-x86_64-Linux-X.XX.XXXX-X.tar.gz> - 下载最新的 DynamoRIO tar xvf DynamoRIO-x86_64-X.XX.XXXX-X.tar.gz mkdir client_64 cd client_64 cmake ../dr_cov/ -DDynamoRIO_DIR=/home/max/manul/dbi_clients_src/DynamoRIO-x86_64-Linux-X.XX.XXXX-X.tar.gz/cmake make ``` ``` 32 位 Linux cd dbi_clients_src wget <DynamoRIO-i386-Linux-X.XX.XXXX-X.tar.gz> - 下载最新的 DynamoRIO tar xvf DynamoRIO-x86_64-X.XX.XXXX-X.tar.gz mkdir client_64 cd client_64 CFLAGS=-m32 CXXFLAGS=-m32 cmake ../dr_cov/ -DDynamoRIO_DIR=/home/max/manul/dbi_clients_src/DynamoRIO-i386-Linux-X.XX.XXXX-X.tar.gz/cmake make ``` 在 Windows 上,编译库的最简单方法是安装 Visual Studio(已在 2017 和 2013 版本上测试),启动 `VS20XX 交叉工具命令提示符`,并运行以下命令: ``` 64 位 Windows cd dbi_clients_src <下载并解压最新版本的 DynamoRIO> mkdir client_64 cd client_64 cmake -G"Visual Studio 15 Win64" ..\dr_cov\ -DDynamoRIO_DIR=C:\Users\max\manul\dbi_clients_src\DynamoRIO-Windows-XXXX.XX.X.X\cmake cmake --build . --config RelWithDebInfo (如果需要,也可以使用 Debug) ``` ``` 32 位 Windows cd dbi_clients_src <下载并解压最新版本的 DynamoRIO> mkdir client_32 cd client_32 cmake -G"Visual Studio 15" ..\dr_cov\ -DDynamoRIO_DIR=C:\Users\max\manul\dbi_clients_src\DynamoRIO-Windows-XXXX.XX.X.X\cmake cmake --build . --config RelWithDebInfo (如果需要,也可以使用 Debug) ``` ### 在 Linux 上使用 Intel PIN 对黑盒二进制文件进行模糊测试 Manul 最初支持 Intel PIN 覆盖率引导的模糊测试,但由于性能低下和维护开销较高,现已不再支持。 # 命令行参数 最常用的选项可以通过命令行提供。更多选项通过配置文件(`manul.config`)支持。 ``` 示例:python3 manul.py -i corpus -o out_dir -n 40 "target @@" 位置参数: target_binary 要执行的目标二进制文件及选项(不要忘记加上引号,例如 "target e @@")。 可选参数: -h, --help 显示帮助信息并退出 -n NFUZZERS 并行模糊测试器的数量 -s 运行盲模糊测试(无代码插桩) -c CONFIG 配置文件的路径,包含额外选项(参见下方“配置文件选项”部分) -r 恢复之前的会话 必需参数: -i INPUT 初始语料库目录的路径 -o OUTPUT 输出目录的路径 ``` # 配置文件选项 Manul 附带了默认的 `manul.config` 文件,其中用户可以找到所有支持的选项及使用示例。选项应以以下格式指定 `格式:<选项名> = <值>`。符号 `#` 可用于忽略某一行。 #### 字典 ```dict = /home/max/dictionaries/test.dict```。AFL 变异策略允许用户指定一个自定义令牌列表,这些令牌可以随机插入到被模糊测试的文件中。Manul 通过此选项支持该功能(优先使用绝对路径)。 #### 变异器权重 ```mutator_weights=afl:7,radamsa:2,my_mutator:1```。变异器权重允许用户告诉 Manul 在每 10 次执行中,特定模糊测试器应执行多少次变异。在此示例中,AFL 变异器将在 7/10 的变异中执行,Radamsa 在 2/10 中执行,而某个自定义的 `my_mutator` 将获得 1/10。如果你想禁用某个变异器,应将其权重设为 0(例如 `mutator_weights=afl:0,radamsa:1,my_mutator:9`)。 #### 确定性种子(Radamsa 选项) ```deterministic_seed = False|True```。设置为 `True` 时,Radamsa 变异将变为确定性,从而 Manul 的每次运行都会产生相同的输出。 #### 每个线程打印摘要 ```print_per_thread = False|True```。启用此选项后,Manul 将打印每个正在执行的线程的摘要,而不是总计摘要。 #### 禁用易变路径 ```disable_volatile_bytes = False|True``` 启用此选项后,Manul 不会将易变路径列入黑名单。 #### AFL 的 forkserver(仅 Unix) ```forkserver_on = False|True``` 启用或禁用 AFL 的 [forkserver](https://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html)。 #### DBI 选项 ```dbi = dynamorio|pin```。此选项告诉 Manul 将使用哪个 DBI 框架来对目标进行插桩。 ```dbi_root = <路径>```。此选项告诉 Manul 在哪里找到 DBI 框架的主启动器。 ```dbi_client_root = <路径>```。此选项告诉 Manul 在哪里找到 DBI 客户端以执行插桩。 ```dbi_client_libs = 名称_#1,名称_#2|None```。此选项可用于指定需要与主目标一起插桩的库列表(例如,你有一个可执行文件加载了目标库以及其他几个你也想在其中搜索漏洞的库)。 ```dbi_persistence_mode = 0|1|2```。此选项用于在 DBI 持久模式之间进行选择。0 - 无持久化,1 - 标准持久化(函数包装),2 - 尚不支持。 如果 `dbi-persistence_mode = 1|2`,则应指定以下选项: ```dbi_target_module = <模块名>```。Manul 将尝试在此处指定的模块中搜索目标函数。 ```dbi_target_method = <函数名>```。用于持久化包装的函数名称。 ```dbi_target_offset = <函数偏移,例如 0x3198>```。用于持久化包装的函数偏移。如果调试符号不可用,此选项可替代 `dbi_target_method`。 ```dbi_fuzz_iterations = 500```。在内存中运行模糊测试的次数,之后整个程序重新启动。你可以调整此参数以找到最适合你的目标的值。 ```dbi_thread_coverage = False```。仅从执行目标函数的线程中收集覆盖率(尚不支持)。 #### 超时 ```timeout = 10```。在杀死目标并发送下一个测试用例之前等待的时间。 #### init_wait ```init_wait = 1```。此选项可用于设置目标初始化所需的超时时间。 #### Netslave 和 Netmaster 选项 选项 `net_config_master` 和 `net_config_slave` 用于在网络中分发 Manul 实例。你需要执行以下 3 个步骤来运行分布式模糊测试。 1. 创建一个文件,其中包含以 `IP:port` 格式列出的主机列表,这些主机将运行你的从节点。 2. 在远程机器上启动所有 Manul 从节点实例(附带所有必要选项和目标二进制路径),并启用以下选项: ```net_config_slave = 0.0.0.0:1337```。Manul 将启动实例,并在端口 1337 上等待主节点的传入连接。 3. 启动主节点实例,并使用选项 `net_config_master = 文件名` 提供在步骤 1 中创建的包含从节点实例列表的文件。 #### 调试模式 ```debug = False|True``` - 打印调试信息。 ```logging_enable = False|True``` - 将调试信息保存到日志中。 #### Logo ```manul_logo = False|True``` - 开始时打印 Manul 徽标。 #### 禁用统计 ```no_stats = False|True``` - 保存统计信息。 #### 位图同步频率(DBI 模式建议 5000) ```sync_freq = 10000```。允许用户更改覆盖率位图的同步频率。此选项告诉 Manul 应该多久同步一次并行模糊测试实例之间的覆盖率。较低的值会降低性能但增加实例间的协调。 #### 自定义保存输出路径 ```#custom_path = test_path``` - 此选项允许将测试用例保存到自定义文件夹中(如果目标需要从某个预定义位置加载测试用例)。 #### 命令行模糊测试(实验性) ```cmd_fuzzing = True|False```。如果启用此选项,Manul 将通过命令行而不是保存文件的方式向目标提供输入。 #### 忽略信号 ```user_signals = 6,2,1|None```。用户可以告诉 Manul 应忽略目标的哪些信号(不视为崩溃)。 #### 网络模糊测试(实验性) ```target_ip_port = 127.0.0.1:7715|None``` - 用于指定目标 IP 和端口。 ```target_protocol = tcp|tcp``` - 用于指定通过网络向目标发送输入的协议。 ```net_sleep_between_cases = 0.0```。此选项可用于定义发送到目标的测试用例之间的延迟。 目前,网络模糊测试是一个实验性功能(更多细节请参见 issues)。 # 添加自定义变异器 可以通过以下三个步骤添加自定义变异器: 步骤 1:创建一个 Python(.py)文件并为其命名(例如 example_mutator.py) 步骤 2:创建两个函数 `def init(fuzzer_id)` 和 `def mutate(data)`。更多细节请参见 [example_mutator](https://github.com/mxmssh/manul/blob/master/example_mutator.py)。Manul 将在模糊测试初始化期间调用 `init` 函数,并在每个文件提供给目标时调用 `mutate` 函数。 步骤 3:通过在 `manul.config` 中使用 `mutator_weights` 指定变异器名称来启用它。例如 `mutator_weights=afl:2,radamsa:0,example_mutator:8`。 注意:AFL 和 Radamsa 变异器应始终被指定。如果你想禁用 AFL 和/或 Radamsa,只需为它们分配权重 0。 ## 技术细节 待定 ## 状态屏幕 