【论文阅读】HouseFuzz_ Service-aware grey-box fuzzing for vulnerability detection in linux-based firmware
摘要
当前模糊测试方法针对linux固件漏洞的限制:
1、仿真上的限制(在仿真和模糊测试过程中,固件服务的多进程特性被过度简化,限制了固件测试的范围)
2、模糊测试反馈引导的限制(简单说就是因为linux固件存在多进程运行的特性,需要有这样一个框架服务于多进程。监控所有目标服务有关的执行反馈,从而指导模糊测试流程。但是现有工具通常把这个多进行运行简化为单进程模式,在触发需要多进程协作的漏洞的时候就会存在限制)
3、测试用例生成的局限(固件服务定制化高,会在标准协议的基础上引入很多新的约束,而当前模糊测试工具不具备这样定制化服务协议的感知能力。当前种子由标准协议构建精心设计,然后随机变异,导致种子质量会比较差)
贡献:专为服务感知的灰盒模糊测试工具HOUSEFUZZ(项目地址:GitHub - HouseFuzz/HouseFuzz: HouseFuzz: Service-Aware Grey-Box Fuzzing for Vulnerability Detection in Linux-Based Firmware · GitHub)
工作原理:固件仿真阶段,会先仔细遍历整个系统的初始化流程,识别现有方法遗漏的网络进程和守护进程;模糊测试阶段,会采用多进程模糊测试框架,实现多进程启动固件服务的全面检测;同时会结合离线与在线固件服务分析,捕捉定制化服务协议的令牌级语义约束,用来生产高质量的测试用例。
效果:与当前最优的灰盒固件模糊测试方法比,同一固件数据集上识别的网络服务数量提高76%,覆盖率提高24.8%,发现0-day增加175%。
一、研究现状:
论文梳理了现有灰盒固件模糊测试的工作,主要分为两类:系统级仿真(如 FirmAE)和进程级仿真(如 FirmAFL、EQUAFL、GREENHOUSE)。虽然这些方法取得了一定效果,但都未能充分应对固件服务的多进程协作和定制协议特性。
在仿真方面:
| 方法类型 | 代表工作 | 主要问题 |
|---|---|---|
| 系统级仿真 | Firmadyne, FirmAE | 仿真开销大,且仍可能遗漏进程;不关注多进程协作 |
| 进程级仿真 | FirmAFL, EQUAFL, GREENHOUSE | 仅仿真单个进程,忽略守护进程;仅单进程覆盖反馈;无法处理定制协议语义约束 |
在模糊测试方面:
| 环节 | 现有方法 | 存在问题 |
|---|---|---|
| 反馈引导 | 仅监控单进程的代码覆盖 | 无法检测多进程协作触发的漏洞;反馈信号不完整,限制代码探索空间 |
| 测试用例生成 | 标准协议模板 + 随机变异 | 忽略定制协议的语义约束,生成的输入在浅层校验即被拒绝,难以触发深层路径 |
简单总结下来其实再仿真上面没什么特别大的贡献,项目是基于greenhouse实现的仿真,在模糊测试上尤其针对多线程的模糊测试有自己的idea
二、存在的问题和挑战:
论文明确指出三个关键障碍:
- 服务仿真不完整:多进程服务的识别和仿真被简化,导致大量网络服务被遗漏。
- 反馈引导局限:仅监控单一进程的代码覆盖,忽略多进程协作,限制漏洞触发。
- 测试用例生成缺乏语义:忽略定制服务协议的复杂约束,生成的输入常被浅层验证拒绝。
(一)、启发来源:
idea的启发来源,用一个例子简单解释清楚:

这个例子主要用来说明多进程和定制化服务的重要性,这个例子主要展示了基于linux的固件中存在的缓冲区溢出漏洞,这个漏洞依次通过四个步骤触发:
1、服务初始化(系统启动时自动启动的ncc进程会启动mini_http进程。然后这个进程会绑定到一个网络通道并等待传入请求(line24-line26))
2、请求发送(攻击者发送恶意数据包用来激活mini_http进程(L27,L28))
3、请求处理(mini_http的handle_request()函数会解析HTTP请求。在L34确认请求以“.cpp”结尾,表示对ncc的特殊请求,并在L35\36通过套接字将其分派给ncc进程)
4、漏洞触发(当网络控制器ncc在收到IPC 请求后(L3),会调用ipc_handler函数来处理请求(L6)这个函数会检查多项约束条件,最终到达容易受到攻击的19行发生缓冲区溢出——因为“pc ip”读取的没有长度限制的长字符传被用于格式化buf(L8\L13-17))
(二)、三个挑战
服务识别挑战:
基于上面这个例子,想要触发漏洞,首先对于mini_httpd和守护进程ncc都需要被识别为全功能服务,简单说就是被放着起来了。然后要实现这样的仿真是很难的,因为基于白名单的方法中,只有在面向网络和守护进程的名称都出现在白名单中时,才会进行识别,但实际上ncc这种进程可能会因为没有在白名单里面被忽略。而基于系统的仿真会仿真所有进程,并在进程简历网络,但是在建立网络通道或者进程间通信IPC被建立前,这个仿真就可能因为异常或者系统中断而停止。现有的模糊测试对这些异常的处理侧重在单个,而不是全维度上。
见解:我们发现,通过观察异常流程事件(如崩溃)可以识别出异常流程,而通过深入分析流程执行跟踪可定位异常代码。在进行服务仿真时,我们倾向于采用系统仿真方式,同时仔细识别并修复所有服务相关流程中的异常代码。这一设计选择有助于仿真程序遍历更多系统初始化流程,从而识别出更多网络服务。
多进程模糊测试:
现有的灰盒固件模糊测试技术低估了涉及多进程的linux服务的复杂性,仅将面向网络进程的mini_httpd为唯一测试目标。具体而言,这些技术仅以mini_httpd的代码覆盖率为指导,而忽略了其他有价值的覆盖率。这样的方法就容易忽略比如上述图中的例子。
见解:本文的目标是设计一个用户空间多进程模糊测试框架,该框架不仅能够监控多进程执行反馈,相比系统级模糊测试还能提供更优的开销[11][6]。具体而言,所提出的框架可监控并合并所有服务相关进程的代码覆盖率,为服务模糊测试提供全面指导。此外,该框架具备多进程漏洞判定功能,可提示不同进程中漏洞的触发情况。
定制服务的挑战:
比如像上述这样的例子,我们想要成功攻击,攻击输入不仅要符合标准http协议语法,还要满足定制协议的复杂约束。就比如,在ncc进程中通过L14的验证,CCPact和set必须结合起来形成语义上有效的键值对。但是现有方法完全忽略了这一点
见解:如图1所示,定制服务协议为生成高质量测试用例提供了三个有价值的提示。首先,“set”和“CCPact”是要插入到测试用例中的有趣值。其次,“set”和“CCPact”分别对应一个键和值,这表明它们应该作为赋值的左右操作数插入。第三,当“CCPact”用作字段名称时,使用“set”作为对应值是有意义的。我们的想法是根据这些提示形式化定制服务协议,然后通过分析固件服务来推断定制服务协议,最后利用推断出的协议来指导测试用例生成。
三、核心思路
简单来说就是针对上面的三个问题分别提出了自己的见解,这里主要对应论文的5-8章节,5-7为设计,8为实验。
顺便讲一下有关第四章介绍这个项目的部分:

图2主要介绍了这个项目的整体工作流程:
输入:镜像固件、标准协议
首先进入固件仿真部分(对应第五章的内容),核心在于遍历初始化流程实现网路服务的识别;
随后利用多进程模糊测试框架(对应第六章)和服务协议引导型模糊测试(第七章),用于改进现有单进程且不感知协议的模糊测试。
两大特点:多进程代码覆盖率为引导,并通过多进程漏洞预言机来检测漏洞。这种服务协议引导型模糊测试技术可以推导标准和定制化的协议,并利用推到得到的协议生成语法和语义产生一些高质量的测试用例。
四、技术路线
(一)、服务仿真模块($5)
服务仿真包含两个步骤:1、识别固件中的网络服务(5.1),具体说是面向网络的进程和守护进程 2、使用现有进程模拟技术进行模拟(这里侧重用的是greenhouse https://github.com/sefcom/greenhouse)
1、整体服务识别
基本思想是分析系统初始化过程,因为在初始化的过程中主要负责为服务简历网络通道和进程间通信通道。这些通道随后会作为识别面向网络的进程和守护进程标识。所以这个初始化很重要,这个项目通过仔细识别和解决中断或挂起系统初始化的仿真异常来改进遍历。
(1)、初始化模拟
受greenhouse启发(处理补丁服务进程以规避单进程服务模拟的障碍),该项目会修复系统初始化过程中发现的一场,从而识别更多的网络服务。与greenhouse不同的是,这个项目的目标是包含多个进程的整个系统的初始化流程。所以实现上就会先定位引发异常的进程,然后再定位异常代码。但是这样会存在一个难点:正常进程和异常进程的行为及其相似,所以单纯采用greenhouse那种基于启发式规则的异常识别方法就难免会产生误报,对此的应对策略是使用IPC进程间通信信号来判断真实性。
:::color4
【简单地说其实也就四步:尝试运行 -> 崩溃/卡死 -> 自动打补丁 -> 再次运行】
:::

系统首先会通过匹配名称找到init或者preinit字样的固件中的init程序(L1),随后启动仿真修补循环来修复识别到的异常。这个修补循环首先会启动init接受进程并收集所有的进程执行轨迹(L4),检测到异常(L6),就会对异常代码进行修补并重新仿真运行(L13).
这个循环会持续进行直到满足三个条件之一:1、没有异常了(L7)2、先前的补丁破坏了仿真(L5/L10-12)3、到了最大循环测数(L14-15)
接着我会展开说说这个过程中涉及到的几个比较核心的过程:
(1)异常识别:
L6,算法使用了IdentifyExpection()识别哪些进程会引发异常以及异常代码是什么。具体而言,会使用表中的四个异常指标,当init进程因验证失败或内存错误导致异常时,系统初始化就会被中断。这个时候,housefuzz就会在init进程执行轨迹的末尾检测exit()函数或者识别一些知名信号。类似地,非INIT进程的异常中断可能会中断系统初始化,其中HOUSEFUZZ仅在观察到致命异常信号(例如,段错误信号)时识别此类异常,因为非INIT进程通常退出。
此外,INIT 和非 INIT 进程的挂起都可能阻止初始化代码的执行。例如,由于 NVRAM 变量配置错误,INIT 进程可能会生成交互式调试 shell。再例如,非 INIT 脚本可能会在启动面向网络的进程之前一直等待网络适配器,这是无休止的,因为网络适配器在模拟过程中永远不存在。由于缺乏信号等强有力的指标,悬挂比中断更加隐蔽。对于非INIT进程,如果它消耗了大量的总CPU时间(等于或大于1 3 ),HOUSEFUZZ认为它陷入忙等待。对于 INIT 进程,如果没有检测到其他异常,HOUSEFUZZ 会积极地认为它挂起。

(2)异常处理
识别到异常后,系统会分析该进程后续的执行轨迹用来定位异常代码。原理是:当进程中止或挂起时,进程会停止执行或在循环内继续执行相同的代码,因此可以在尾部执行跟踪中找到直接的异常原因。随后,housefuzz会选择满足两个条件的函数后续使用(被调函数大小不超过阈值,最大程度减小补丁的影响;位于程序内可执行片段而不是外部调用),并将该指令重写为nop
(3)鲁棒性增强
由于异常识别涉及启发式和攻击机制,因此可能会产生误报。为了解决这个问题,系统有回退机制(L10-12)具体来说,当仿真过程中发现较少的网络通道和IPC通道(即C)时,这意味着初始化代码逻辑被补丁破坏了。在这种情况下,HOUSEFUZZ认为之前的补丁容易出错,并采用之前的仿真结果来避免错误并增强鲁棒性。
(2)、网络进程识别
两个目标:首先,需要识别网络渠道。模糊测试必须与网络通道进行通信才能执行测试用例;其次,它需要推断正确的命令行来启动进程,包括程序路径和参数。
HOUSEFUZZ 首先将监听网络通道的进程识别为面向网络的进程。具体来说,HOUSEFUZZ 在仿真过程中收集系统调用跟踪,包括 PID、系统调用名称和参数,并检查网络绑定调用(即 bind())的具体参数以提取网络通道。如果通道暴露于网络(即不是“localhost”),则它被视为面向网络的进程。有时,面向网络的进程(例如 HTTP 服务器)通过加密隧道(例如 HTTPS)间接与网络交互,因此它可能不会直接侦听网络通道。我们将它们表示为面向网络的代理进程。 HOUSEFUZZ 基于已知的代理端口(例如 HTTP 的 80)来识别这些进程,因为它们可以比通过加密隧道进行模糊测试更有效。在识别出面向网络的进程后,HOUSEFUZZ 从相应的 execve() 系统调用的参数中提取其命令行。
1. 动态追踪与识别 (Dynamic Identification)
HouseFuzz 并不是静态地看哪些程序叫
httpd,而是通过全系统仿真时的动态行为来判断:
- 监控系统调用:在仿真运行期间,它会实时记录系统调用轨迹(System Call Traces)。
- 捕获关键信息:它会记录每个进程的 PID、系统调用名称以及具体参数。
- 分析
bind()调用:重点检查bind()函数的参数。如果一个进程尝试将自己绑定到一个端口,并且绑定的地址不是本地回环地址(即不是localhost/127.0.0.1),HouseFuzz 就会将其标记为面向网络的进程。2. 识别“代理型”网络进程 (Proxied Network-facing Processes)
这是 HouseFuzz 的一个聪明之处,它考虑到了加密隧道(如 HTTPS/SSL)对测试效率的影响:
- 规避加密瓶颈:某些进程(如 Web 服务器)可能隐藏在加密隧道后面,并不直接监听外部端口。
- 基于已知端口识别:HouseFuzz 会识别这些被代理的进程。例如,如果它发现一个进程在处理 80 端口(HTTP)的逻辑,即便它最终通过加密隧道传输,HouseFuzz 也会定位到这个原始进程。
- 目的:直接对这些原始进程进行模糊测试,比穿透加密隧道(Encrypted Tunnel)去测试要高效得多。
3. 环境提取与准备 (Context Extraction)
一旦确认了目标进程,HouseFuzz 就会执行“斩首行动”:
- 提取命令行 (Command Line):从系统中提取该进程启动时的完整命令行参数。
- 意义:这不仅是为了知道如何启动这个进程,更是为了后续能将其剥离出来,在更轻量级的用户态仿真环境(Docker + QEMU)中独立运行。
(3)、守护进程识别
同样两个目标:第一个是推断和推理进程之间的 IPC 依赖性,以查找给定网络服务的守护进程。其次,它还需要识别守护进程的命令行。
1. 寻找“联络暗号”(锁定 IPC 密钥)
HouseFuzz 认为每个通信通道都有一个唯一的“钥匙”(Key),比如 Unix Domain Socket 的文件路径。
- 动态抓取:它不看代码(静态分析),而是在程序运行时,盯着系统调用。
- 关键动作:当进程调用
open()(打开文件通道)或bind()(绑定地址)时,HouseFuzz 瞬间记录下这些通道的路径和文件描述符(FD)。这比静态看代码要准得多。2. 识别“幕后大佬”(确定守护进程)
一个网络服务(比如 Web 界面)往往需要向后台的“大佬”(守护进程)请示数据。
- 监控读写:HouseFuzz 盯着
send()(发送数据)和recv()(接收数据)这些系统调用。- 顺藤摸瓜(溯源):如果它发现 A 进程发出的数据被 B 进程读走了,它会立即通过文件描述符(FD)回溯系统轨迹,查出它们是通过哪个通道联络的。
- 判定逻辑:如果一个进程建立的通道被其他网络服务进程频繁读写,那么这个进程就被判定为依赖项(守护进程)。
2、进程仿真
HOUSEFUZZ采用GREENHOUSE提出的技术进行流程仿真。为了模拟服务,HOUSEFUZZ 首先启动其守护进程,然后启动面向网络的进程。在此序列中,面向网络的进程和守护进程都将根据需要自动启动实用程序进程。在灰盒模糊测试期间,所有服务进程都应由模糊器管理。 HOUSEFUZZ 使用 execve() 系统调用来跟踪由 QEMU [28] 测试的服务启动的所有进程,以进行覆盖率收集和漏洞检测。
(二)、多进程模糊测试框架($6)
两个核心:多进程代码覆盖率为指导;漏洞判定器能检测被测试服务中的所有进程
1、多进程覆盖率引导
进程下的多进程覆盖引导比起单进程又存在自身的难点。一般来说,代码覆盖率会包含三个步骤:(测试完成事件)TCE检测、覆盖率记录、覆盖率指导
后续篇幅也会围绕这三个角度展开阐述和单线程相比不同的地方。
(1)、测试完成事件(TCE)检测
首先先简单说一下什么是这个TCE(test completion event): 在模糊测试过程中,灰盒模糊测试器在测试用例执行过程中记录代码覆盖率,然后会根据特定事件(比如进程终止)来检测测试用例执行是否完成。这个过程就是TCE检测。
那么在多进程代码覆盖率引导的过程中,这个TCE检测难点在哪里呢?在于确定“何时收集代码覆盖率”。
在多进程模糊测试中,测试用例的执行会引入比单进程更复杂的进程状态转换,具体而言,单进程只需要识别某个进程是否终止或者网络资源是否释放即可,但是多进程模糊测试需要检测所有的服务进程是否都已经完成测试用例处理,否则收集到的收集率就是不完整的。而这种不完整就有可能会导致有价值的测试用例的遗漏。
因此housefuzz会在观测到所有进程的测试用例都完成后,才将测试视作执行完毕。接下来会用这个图来简单说说这个过程具体是怎么实现的:

实用进程(“Utility processes” ):这类进程的生命周期很短,因为他们往往是为了处理单个请求而启动的。所以housefuzz也就像传统模糊测试器一样检测使用程序进程的TCE终止。
面向网络和守护进程(network-facing/daemon process****):这类进程一般都是长时间运行的进程,通常在测试用例处理过后会继续运行。我们发现面向网络的进程通常在处理请后后释放网络资源,所以系统在检测到网络资源被释放(即模糊测试下的网络套接字被释放),便认为该进程的TCF得到满足。 与处理网络请求面向网络的进程不同,守护进车各主要处理进程间通信(IPC)。处理完进程间通信后,守护进程可能会复用IPC通道而不是关闭。这种情况下,守护进程会通过调用特定的I/O监听(比如select、poll)反复等待进程间通信,并在从这些系统调用返回时开始处理请求。基于这一观察,HOUSEFUZZ 检测 I/O 侦听系统调用以检测 IPC 请求处理的完成情况。当 HOUSEFUZZ 发现守护进程重新调用 I/O 监听系统调用时,HOUSEFUZZ 假定该进程已完成 IPC 请求处理。但是IPC 请求处理的完成并不总是与测试的完成相同,因为面向网络和实用程序进程可能在一次测试期间发出多个 IPC 请求。为了避免此问题,HOUSEFUZZ 在检测到所有面向网络和实用程序进程的 TCE 后,再检测守护进程的 TCE。当观察到所有进程的 TCE 时,HOUSEFUZZ 认为测试用例已完成执行。
(2)、覆盖率记录
为了记录每个进程而无需数据竞争,housefuzz在进程启动时为其分配独立的共享内存作为覆盖率位图;在被覆盖率指导使用之后,所有的位图都会被清空并放入共享内存池中,供下一轮测试使用。为了节约内存空间,加载同一程序的进程在位图检测到该进程的测试用例执行TCE后,会合并到位图中。为了进一步降低开销,会避免收集测试用例过程中违背触发的守护进程的覆盖率,这会通过维护一个激活标志来实现,当守护进程退出accept(),这个标志就会被启用。
(3)、覆盖率范围指导
housefuzz通过将收集到的覆盖率位图与历史代码覆盖率(AFL++原始映射的那个)进行对比来分析这些位图。如果在任意进程的位图中发现了新的比特位,就意味着当前测试用例触发了服务中的新路径。因此,系统就会把这个一用例视为有价值的,并把它加入种子队列中,用于后续探索。
但是这里存在一个问题,不同测试启动的进程可能不同,所以需要保证一个一致性问题——所比较的覆盖数据对应于同一程序而言。为了解决这个问题,HOUSEFUZZ 集成了加载相同可执行 ELF 对象的所有进程的覆盖位图(通过总结命中计数器),并使用输出覆盖位图进行比较。
2、多进程漏洞检测
前面也说了,和单进程不同的是,housefuzz恶意检测服务进程中的所有漏洞(包括网络进程、守护进程、以及实用进程)。这里会简单说说怎么实现的:
重点关注对象:内存损坏(memory corruption)和命令注入
- 内存损坏:检测服务进程中的崩溃信号(比如段错误),并检测内存损坏影响是否可以通过用户输入控制来排除实用程序进程中不可利用的崩溃。
- 命令注入:对所有服务进程的execve()进行插桩。
附录B有关于这部分的详细技术细节。
简单看了下附录b部分,关于内存损坏这里没什么好解释的,主要还是有关于后面命令注入这里。
命令注入通常依靠特殊字符串实现的,这里对这部分的实现集中在两个点:
1、没有使用api,因为厂商会对api做特殊化处理,而是直接对execve进行插桩
2、对于字符这里,首先使用普通字母来绕过滤观察效果,然后再替换为字符串,从而大幅度提高命令注入的成功率
(三)、服务协议引导的模糊测试($7)
这部分开始会说点关于固件中的定制化协议是如何实现的,本文是怎么去解决的
1、服务协议的形式化
固件协议包含两个部分:标准服务协议和自定义服务协议部分。为了提高测试用例的质量,所以需要特别针对这两类展开。
方法:上下文无关语法(“context-free grammar (CFG)”),通过标记依赖图(“token dependency graph (TDG)”)来实现定制化服务协议的语义约束进行形式化建模
(这部分开始我有点没看懂,但是尽力解释清楚,所以先贴一段原文有关的解释,然后会给出一个我自己的理解)
定义:TDG 是有向图 G(N, E)(如图 5 所示)。每个图节点 n ∈ N 是一个二元素元组 n(v, t)。n.v 表示 token 值,是一个具体的字符串,n.t 是 token 类型,描述 token 语义。我们定义了三种基本的 token 类型,分别表示 Path、Key 和 Value 等多种 token 的语义,其中 Path 表示特定的功能路由,Key 表示赋值的左操作数,Value 表示赋值的值。例如,图 1 中的 token“ccp act”和“set”分别对应于 Key 和 Value 类型。对于每个图边 e(ni, nj) ∈ E,它表示从源标记 ni 到目标标记 nj 的依赖关系(即语义约束);具体来说,这意味着当nj已经被测试用例使用时,需要插入ni来满足这个约束。如图 4 所示,有两种依赖关系:控制流依赖关系和数据流依赖关系。对于控制流依赖性,ni 依赖于 nj,因为 nj 控制是否将访问/需要 ni(如场景 1 和 2 所示)。对于数据流依赖性 nj 决定协议如何理解 ni (如场景 3 所示)。
一、TDG是什么?
一句话,一个有向图,描述测试输入中不同令牌(token)之间的依赖关系。这里的“令牌”指的是输入中的最小语义单元,比如 HTTP 请求中的路径、参数键(Key)、参数值(Value)、Header 名称等。TDG 中的一条边(edge)表示“某个令牌的值依赖于另一个令牌的值或属性”。
简单说,TDG 就是一张“关系网”,告诉我们输入中哪些部分是“绑在一起的”——改变了其中一个,另一个也必须跟着变,否则输入可能会被服务的解析逻辑拒绝。
二、一个HTTP的简单例子
假设有一个智能家具固件,它的 HTTP 管理接口接受如下请求:
POST /api/setup
Content-Type: application/json
{
"deviceType": "light",
"operation": "toggle"
}
这个接口的语义约束可能是:
deviceType 的值必须是一个预定义的设备类型(如 "light", "switch", "sensor");
** operation 的值必须与 deviceType 匹配**(例如 "light" 对应的操作可以是 "toggle"、"dim";"switch" 对应的操作只能是 "toggle")。
传统方法(仅用 CFG + 随机变异):
CFG 可以生成格式正确的 JSON,但可能随机变异出 "deviceType": "abc" 或 "operation": "xyz",这些一眼就会被服务器拒绝,因为值不合法。
TDG 如何建模:
HOUSEFUZZ 会从固件二进制中推断出这样的依赖关系:
令牌节点 A:deviceType(参数键)
令牌节点 B:light、switch、sensor 等(参数值候选)
令牌节点 C:operation(参数键)
令牌节点 D:toggle、dim(参数值候选)
依赖边:
A → B:值 light 依赖于键 deviceType
C → D:值 toggle 依赖于键 operation
跨字段依赖:B(deviceType = "light") → D(operation 的可选值中需要包含 "toggle" 或 "dim")
三、TDG如何生成用例
HOUSEFUZZ 在生成测试用例时,会先基于 CFG 生成语法正确的骨架(例如一个合法的 JSON 请求),然后根据 TDG 中的依赖关系,插入符合依赖的令牌值。例如:
第一步:CFG 生成 {"deviceType": "
", "operation": " "} 第二步:从 TDG 中查询,发现 deviceType 的值必须是 light、switch 或 sensor,于是随机选择一个,比如 "light"。
第三步:再从 TDG 中查询,当 deviceType 为 "light" 时,operation 的值只能是 "toggle" 或 "dim",于是随机选择一个,比如 "toggle"。
最终生成:
这样生成的测试用例同时符合语法和语义约束,有很大概率能通过浅层验证,进入深层处理逻辑
四、TDG来源的判断
TDG 不是人工编写的,而是 HOUSEFUZZ 自动推断的:
在线组件:通过动态插桩,在运行实际测试时记录哪些键值对被解析、哪些依赖关系被触发。
离线组件:通过静态代码分析(控制流分析、数据流分析)从固件二进制中提取路径、参数键和依赖关系。
最后再给出一个对比图:
| 概念 | 类比 | 作用 |
|---|---|---|
| CFG | 作文的语法模板(主语+谓语+宾语) | 保证句子在格式上正确 |
| TDG | 作文的逻辑关系图(如果主语是“老师”,谓语必须用“讲课”而不是“吃饭”) | 保证内容在语义上合理 |
在了解了这些信息之后开始我们后面的内容会更加轻松一点。

论文中给出了图4这个例子,旨在说明** 定制协议里不仅有 token,还存在 token dependency(token 依赖)**。这些依赖决定了某个 token 能不能单独出现、应该跟谁一起出现、以及它应该以什么方式被解释。
所以在图4中我们实现了两种依赖的区分:
- 控制流依赖(control-flow dependency)
- 数据流依赖(data-flow dependency)
论文说图 4 中:
场景 1 和场景 2 展示的是 控制流依赖
场景 3 展示的是 数据流依赖
接下来会详细说一下这个图里面的三个场景:
1. 场景 1:某个 token 决定另一个 token 是否需要出现
:::color4
这是最典型的“开关型依赖”。
比如一个请求里有:
action=set
key=volume
value=10
这里 value=10 之所以有意义,是因为前面已经有 action=set。
如果 action=get,那 value 可能根本不会被访问,甚至不允许出现。
通俗地说就是:
- 没有前面的条件 token,后面的 token 根本不会被处理。
所以如果 fuzzing 只随机改 value,却没有同时满足前面的 action=set,请求很可能在浅层检查就失败,根本走不到深层代码。
:::
2. 场景 2:一个 token 决定走哪条分支,从而决定后续需要哪些 token
:::color4
这个和场景 1 类似,但更强调分支选择。
比如:
type=network 时,需要 ip、mask
type=system 时,需要 timezone、hostname
这里 type 决定进入哪条处理逻辑分支。
如果你发的是 type=network,却只带了 timezone,那后面的 token 组合就不匹配,服务会直接拒绝。
所以这里的依赖是:
- 前面的 token 决定了后面哪些 token 才是“合法且会被访问”的。
这依然属于控制流依赖,因为它影响的是“代码会不会走到某个分支”。
:::
3. 场景 3:一个 token 决定另一个 token 应该如何被解释
:::color4
这是数据流依赖,重点不是“会不会访问”,而是“怎么理解”。
比如:
format=hex
data=FF
这里 data=FF 到底是什么意思,要看 format=hex。
如果前面是 format=string,那同样的 FF 就会被当成普通字符串;
如果前面是 format=number,那又会有另一种解释。
通俗地说:
- 一个 token 不是独立有意义的,它的含义依赖另一个 token。
所以 fuzzing 如果只改 data,不管 format,生成的输入就可能语义错误。
:::
所以通过这些例子我们可以更加直观感受到定制协议的难点:不仅仅是字段多,同时字段之间存在依赖,也不是说把这些字段拼起来就行。
2、定制化服务协议推理
所以这个部分就会说说论文是如何解决定制化协议的难点的,这里housefuzz会通过分析目标服务来推理令牌的依赖关系,并利用这些依赖关系构建令牌依赖图(TDG)。为了推理这些依赖关系,我们提出两个正交组件+在线和离线令牌依赖推理图。每个组件采用不同的方法来推理令牌的值、类型和依赖关系。在线组件在模糊测试中会利用动态插桩来收集令牌值,并分析模糊测试用例以推理令牌类型和依赖关系。离线组件则通过静态分析从固件代码中提取令牌值,并推理其类型与依赖关系。这两个组件互为补充:离线组件轻量高效,可快速推理出初始令牌依赖图,用于早期的代码探索;而随着探索的深入,在线组件可借助模糊测试反馈不断完善令牌依赖图。

(1)、在线TDG推理
先总结地说,这个部分会包含三个步骤:
:::success
步骤1(收集与推断 token):通过插桩字符串比较函数(如 strcmp),在模糊测试运行时收集当前测试用例中与输入 token 进行比较的期望 token。
步骤2(推断 token 类型):利用标准协议解析器解析测试用例,获取输入 token 的字段类型(如参数值、参数键),并将其映射为 token 类型(如 Value、Key)。被推断的期望 token 获得与输入 token 相同的类型。
步骤3(推断依赖关系):根据 token 类型推断依赖关系(例如 Value 类型通常依赖 Key 类型),并结合标准协议信息精确定位依赖的具体目标 token。
:::
这三步依次完成:收集 token → 赋予类型 → 建立依赖关系,最终将新的 token 及其依赖插入 TDG,为后续语义正确的测试用例生成提供依据。
然后我们结合图5的例子说说这三个步骤到底是个什么意思:
输入测试用例是一个 HTTP POST 请求:
POST /get_set.cpp HTTP/1.1
...
ccp_act=AAA&...
对应第一步就会先解析输入,获得字段类型:
:::warning
HOUSEFUZZ 先用标准 HTTP 解析器解析这个测试用例,发现:
"AAA" 是参数 ccp_act 的值(Value),因此 "AAA" 的 token 类型为 Value。
"ccp_act" 是参数键(Key),类型为 Key。
"/get_set.cpp" 是路径(Path),类型为 Path。
对应图 5 左上角的 Input tokens (value, type) 方框:
Token 1 ("AAA", Value) Token 2 ("ccp_act", Key) Token 3 ("/get_set.cpp", Path)
在模糊测试执行过程中,HOUSEFUZZ 插桩了 strcmp() 等字符串比较函数。它发现代码中有一个比较操作:strcmp("AAA", "set")
即输入 token "AAA" 正在与一个期望的固定字符串 "set" 进行比较。
:::
第二步:推断新的token
:::warning
HOUSEFUZZ 的逻辑是:既然代码拿 "AAA" 与 "set" 比较,那么 "set" 就是服务器期望的一个 token(称为被推断的 token)。并且,因为 "AAA" 的类型是 Value,所以被推断的 token "set" 的类型也是 Value。
:::
第三步:推断依赖关系并用于后续测试
:::warning
得到新的 token "set"(类型 Value)后,HOUSEFUZZ 接着推断它依赖哪个 token。论文中提到一个经验规则:
即一个 Value 类型的 token 通常依赖于某个 Key 类型的 token。在当前的测试用例中,哪个 Key 与这个 Value 同属一个参数?标准 HTTP 解析器告诉我们:"ccp_act"(Key)与 "AAA"(Value)组成一个键值对。因此,被推断的 token "set" 应该依赖于同一个 Key —— "ccp_act"。
对应图 5 下部的依赖关系线:"set" → "ccp_act"(箭头指向依赖的目标,即 "set" 依赖 "ccp_act")。
现在 TDG 中多了一条边:Value token "set" 依赖于 Key token "ccp_act"。当 HOUSEFUZZ 后续生成新的测试用例时,如果它仍然包含 Key "ccp_act",就应该同时尝试插入 Value "set"(而不是随机乱填),以确保语义正确性。
:::
(2)、离线TDG推理
这里的离线推理主要是通过静态代码分析完成,不需要初始测试用例。
步骤一:在无测试用例的情况下,利用已有的静态分析工具提取 token
离线组件首先调用已有的静态分析工具(如文献 [20]、[34]),从固件二进制中识别出 HTTP 服务协议的 URL 路径和参数键(即 Path 和 Key 类型的 token)。
步骤二:通过静态代码分析,识别三种代码模式来推断 token 依赖
离线组件使用图 4 所示的三种代码模式(直接控制流依赖、间接控制流依赖、数据流依赖),基于固件二进制中的控制流和数据流分析,推断 token 之间的依赖关系。这部分不依赖动态执行,而是直接分析代码逻辑。
步骤三:在推断依赖的同时提取 Value token
由于离线组件没有测试用例,Value token(参数值)通常是在依赖推断过程中发现的——例如,当静态分析发现一个 Key 与某个字符串常量进行比对时,该字符串常量就可以被识别为一个 Value token。
步骤四:将推断结果组合成初始 TDG,用于指导后续模糊测试
最终,所有提取的 token 及其依赖关系被整合成一个初始的 TDG。这个初始 TDG 可以弥补在线组件在模糊测试初期缺乏种子的不足,同时能发现在线组件因只能插桩标准字符串比较函数而遗漏的自定义字符串比较逻辑。
3、服务协议引导的测试用例生成
核心目标:既保证测试输入“格式正确”,又尽量保证它“语义合理”,从而让输入更容易通过服务的解析和校验,进入更深层代码。‘
思路:
把两类信息结合起来:标准服务协议(如 HTTP)的语法结构、定制协议的 token 依赖关系(由 TDG 描述)
步骤:
1. 先用标准协议生成“语法正确”的输入骨架
论文先用 CFG(上下文无关文法) 来描述标准服务协议,比如 HTTP。也就是说,HOUSEFUZZ 先不是乱拼字符串,而是按照 HTTP 这种协议的“语法规则”去生成请求。这样生成出来的输入,至少在格式上像一个真正的 HTTP 请求。论文还解释了 CFG 的作用方式:
“CFG consists of sets of production rules, where each indicates ‘a grammar symbol can be replaced by a list of grammar symbols and constant strings’. By continuously applying production rules, a test case generator can finally create a derivation tree whose leaf nodes composite a syntactic valid test case.”
这句话的意思很简单:CFG 里有一组“替换规则”,不断展开后,最终就能生成一个语法合法的请求。
2. 论文给出的 HTTP CFG 例子

这个例子说明:一个 HTTP 请求可以被拆成:
HTTP 请求
请求行
URI
查询字符串
Header
Body
所以,CFG 负责告诉 HOUSEFUZZ:HTTP 请求应该长什么样。
3. 再用 TDG 保证“语义更合理”
但只有 CFG 还不够。因为 CFG 只能保证“长得像 HTTP”,却不能保证里面的参数搭配符合固件服务自己的业务逻辑。因此,HOUSEFUZZ 再利用 TDG 来处理定制协议语义约束。
4. 具体怎么做:先生成骨架,再插入推断 token
也就是两步:
第一步: 用 CFG 生成一个标准协议的合法请求骨架
第二步: 把 TDG 推断出的 token 小心地插进去,并尽量满足这些 token 的依赖关系
五、实验验证
这部分开始会从四个角度来进行实验,主要是为了作证四个角度:
- 横向对比(RQ1):与 SoTA 工具 GREENHOUSE 相比,HOUSEFUZZ 多发现 76% 的网络服务,代码覆盖提升 24.8%,0-day 漏洞多 175%。
- 服务识别(RQ2):HOUSEFUZZ 识别 311 个网络面进程(GREENHOUSE 44 个,FirmAE 128 个),召回率 80.4%。
- 多进程消融(RQ3):多进程框架虽引入开销,但额外发现 14 个单进程无法检测的漏洞,且 38% 的 CVE/CNVD 源自多进程漏洞。
- 协议引导消融(RQ4):相比仅用 CFG,加入 TDG 后漏洞检测提升 94%,离线推断进一步增加 55 个漏洞。
- 稳定性实验:在 8,746 个种子中,排除非确定性逻辑后 TCE 检测达到 100% 一致覆盖。
接下来会分别说一下文中涉及的这四个大板块。
(一)、实验设置
原型实现:HOUSEFUZZ借助Binwalk实现了从linux的固件中提取固件。在系统初始化环节,主要依靠QEMU用户模式运行每个固件镜像,同时追踪系统调用和基本块的执行轨迹。随后利用Radare2对轨迹进行分析,定位并修补异常代码,以提升系统初始化仿真的鲁棒性。在识别完网络服务之后,会将对应服务的二进制文件输入基于IDA Pro的静态分析引擎,提取离线TDG,这个提取的数据将用于服务协议引导的模糊测试。在模糊测试阶段,系统原型会使用greenhouse的技术对目标服务进行仿真,并对AFL++的QEMU模式进行修改,以增加对多进程的反馈机制、判定,还有对在线TDG推理的支持。
数据集:这里其实没说原始的数据集来源,主要是根据前沿研究去找的镜像固件,包含了三个厂商的70个固件。因为原始数据集没有标注镜像版本,或者过时版本无法下载,送一其中35个固件替换为同产品的最新固件。然后使用了binwalk进行提取,有10个提取失败。这60个提取的可用固件用于后续大部分实验。

实验环境:“Ubuntu 22.04”“Intel Xeon(R) Gold 5218 CPU (2.30 GHz × 64 cores) and 245 GB memory”
漏洞披露:们已负责任地将实验中发现的全部156个零日漏洞上报给厂商,随后又提交至漏洞数据库,其中包括58个缓冲区溢出漏洞、15个操作系统命令注入漏洞以及83个拒绝服务漏洞。
(二)、通用模糊测试性能(Q1)
本章节开展了两项实验,对比试验和漏洞挖掘实验,以验证HOUSEFUZZ在代码探索和漏洞检测方面的优越性能。
1、对比实验
在表2数据集的基础上,与相同的模糊测试设置下,对比了HOUZEFUZZ与当前最优灰盒固件模糊测试技术的整体模糊测试性能。
基线:GREENHOUSE
模糊测试目标:针对表2的60个镜像固件。首先运行GREENHOUSE 手机其中所有可模拟且可模糊测试的服务(共包含41个web服务),把这些作为固件服务数据集
模糊测试设置:相同仿真环境(运行相同的)对固件服务数据集进行模糊测试。每个工具每个服务3轮模糊测试,每轮一个CPU核心,持续24h。
种子:来自GREENHOUSE的初始目标种子
测试用例:GREENHOUSE使用从目标服务中提取的模糊测试字典,而HOUSEFUZZ仅使用自动提取的标记依赖图。同时在评估HOUSEFUZZ的时候,标记依赖图推理所消耗的时间是计入了模糊测试时间内的。
对比指标:漏洞检测和代码覆盖率。
- 为了评估漏洞检测,我们首先对崩溃站点检测到的所有崩溃进行分组,并进一步利用基于 QEMU 的清理程序和手动根本原因分析来找出独特的漏洞。手动根本原因分析以发现独特的漏洞。特别是,为了公平起见,HOUSEFUZZ 检测到的命令注入被排除在比较之外,因为 GREENHOUSE 无法识别此类漏洞。
- 为了评估代码覆盖率,我们只考虑面向网络的程序的边缘覆盖率,因为GREENHOUSE不具备像HOUSEFUZZ那样收集多进程覆盖率的能力。按照代表性的模糊评估实践[37],我们计算了 p 值和 A^12 分数来衡量显着性和效果大小,并将 p < 0.05 视为两种工具之间存在显着差异的指标,而 A^12 ≥ 0.71 则视为 HOUSEFUZZ 可能优于 GREENHOUSE 的指标。此外,我们还根据总测试用例执行时间分析了 HOUSEFUZZ 引入的执行开销。

代码覆盖率结果:HOUSEFUZZ的边缘覆盖率比GREENHOUSE高出24.8%(p < 0.01 和 Aˆ12 ≥ 0.71)
漏洞检测结果:这边检测了由 GREENHOUSE 和 HOUSEFUZZ 检测到的 705 起和 2,519 起崩溃,并将结果(即独特的漏洞)总结在表 3 中。综上所述,GREENHOUSE仅在18个服务上检测到46个漏洞,其中40个0day,而HOUSEFUZZ在25个服务上检测到128个漏洞,其中110个0day。这意味着 HOUSEFUZZ 检测到的 0day 比 GREENHOUSE 多 175%。在这里,HOUSEFUZZ 在 2 个服务上发现的漏洞比 GREENHOUSE 略少(即少 1 个漏洞),这主要是由于多进程代码覆盖率收集的开销较高。
性能开销结果:由于多进程反馈收集和 TDG 推断,HOUSEFUZZ 在 41 个服务上平均比 GREENHOUSE 多引入了 6.8 倍的时间开销。对于内存开销,HOUSEFUZZ 为每个活动进程分配了一个(128 KB)覆盖位图,内存开销与活动进程数量成线性关系。在这里,我们认为考虑到 HOUSEFUZZ 在漏洞检测和代码覆盖率方面明显优于 GREENHOUSE,开销是完全可以接受的。
2、漏洞挖掘实验
鉴于之前对比实验使用的固件镜像数据集仅涉及2019年之前发布的旧版固件,本实验旨在证明HOUSEFUZZ也可以检测主动维护的产品的最新固件镜像中的漏洞。因此,我们在一个新的固件镜像数据集上进行了这个实验,其中固件镜像是在2020年之后发布的。
模糊测试目标(固件服务数据集):我们按照以下步骤构建了一个新的固件服务数据集。首先,我们收集了 45 个积极维护的产品的最新固件镜像,这些产品是从 9 个供应商最近的安全咨询网页中采样的,并且有 2020 年之后发布的固件镜像。然后,我们对这些图像进行了提取,发现有12张可以成功提取。最后,我们遵循第 8.2.1 节中描述的相同工作流程,从新固件图像数据集中收集可模拟和可模糊的服务作为模糊目标。来自新固件图像数据集作为模糊测试目标。结果,我们从 12 个提取的固件映像中识别出 12 个 Web 服务,如表 4 所示。

漏洞搜寻结果:我们使用 HOUSEFUZZ 对每个已识别的 Web 服务进行 24 小时模糊测试,成功检测到 5 个服务中的 21 个 0day 漏洞,并分配了 21 个 CVE/CNVD。这一结果表明,当应用于新固件镜像数据集时,HOUSEFUZZ 在漏洞检测方面也很有效。
(三)、服务识别实验(Q2)
实验概述:验证服务识别方法的有效性,实际上还能让HOUSEFUZZ识别出那些被忽略且存在隐含漏洞的服务
基线:依旧是GREENHOUSR和FirmAE。GREENHOUSE 使用名称白名单来识别面向网络的程序和守护程序;然后,它使用预定义参数或从 FirmAE 仿真结果中提取的参数启动已识别的程序,以获得面向网络的守护进程。FirmAE 模拟整个固件系统,无需服务识别。为了与 FirmAE 进行比较,我们运行 netstat [39] 工具来识别仿真过程中面向网络的进程,这类似于在不修补仿真异常代码的情况下运行 HOUSEFUZZ。
面向网络的进程识别与网络服务识别的结果:对于面向网络的进程识别,我们在第 8.1 节中介绍的相同固件图像数据集上运行 HOUSEFUZZ、GREENHOUSE 和 FirmAE。当面向网络的进程打开非本地网络通道(例如公共 IP 地址上的 TCP/UDP 端口)时,该进程被视为已识别。最后,结果表明 HOUSEFUZZ、FirmAE 和 GREENHOUSE 从 60 个提取的固件映像中分别成功识别了 311、128 和 44 个面向网络的进程。我们手动确认这些过程是实际面向网络的,因此识别精度为100%。这表明 HOUSEFUZZ 发现的面向网络的流程比现有工作至少多 143%。以总共 387 个独特的面向网络的流程为基准,HOUSEFUZZ 的召回率达到 80.4%,显着高于 FirmAE 和 GREENHOUSE 的 33.1% 和 18.1%。表 5 说明了三种工具识别的网络服务的分布情况。由于名称白名单的限制,GREENHOUSE识别出的网络服务最少,限制了未列出的面向网络的进程的发现。此外,我们发现 GREENHOUSE 尝试使用错误的参数模拟 3 个 Web 服务器,导致模拟进程在建立网络通道之前中止。通过比较 HOUSEFUZZ 和 FirmAE,我们发现 FirmAE 仅在 25 个固件映像中成功识别了网络服务,而 HOUSEFUZZ 在 59 个固件映像中成功识别了网络服务。这是因为HOUSEFUZZ自动识别并处理异常,以分析更多的系统初始化过程。
为了了解第 5.1.1 节中描述的异常识别和处理的有效性,我们分析了每个识别的仿真异常。我们观察到 HOUSEFUZZ 识别出 30 个固件映像的 85 个模拟异常,其中 8 个使用恢复机制自动识别为误报。因此,该机制避免了忽略 8 个固件映像中的 77 个服务。在处理已识别的异常后,HOUSEFUZZ 在 23 个固件映像上成功识别了另外 119 个 (37%) 网络服务。这些结果表明HOUSEFUZZ的异常识别和处理技术对于网络服务识别是有效的。

守护进程识别:通过人工分析,我们确认GREENHOUSE和HOUSEFUZZ在守护进程识别任务中达到了100%的精确率。然而,由于分析进程间通信(IPC)依赖关系需要大量逆向工程工作,因此难以构建守护进程的真实值来评估召回率。为此,我们提供一个示例来说明基于白名单方法的缺陷。我们发现 GREENHOUSE 无法识别名为“zebra”的重要守护进程。根据文档[40],运行 zebra 是运行 ripd 服务所必需的,因此 zebra 对于测试 ripd 服务至关重要。HOUSEFUZZ 在不使用名称白名单的情况下成功找到了所有这些守护进程。
已识别的被忽视服务中的漏洞。如果不识别面向网络的进程,灰盒模糊测试就无法正确测试相应的网络服务,也无法发现这些服务中的漏洞。如上所述,HOUSEFUZZ 成功识别出比基线更多被忽视的网络服务。在这里,我们进一步选择了 3 个 GREENHOUSE 和 FirmAE 均未识别的 Web 服务,使用 HOUSEFUZZ 对每个服务进行了 72 小时的模糊测试,成功检测到 12 个 0day 漏洞。
(四)、多进程模糊测试框架实验(Q3)

实验目的:这里主要是为了说明多进程模糊测试框架的有效性。首先是消融实验,用来评估这个多进程模糊测试框架整体的有效性。其次,开展了人工漏洞分析,用来探索这个多进程模糊测试框架是否确实对多进程漏洞检测起到了作用。最后评估了多进程模糊测试所采用的TCE检测技术是否文件,以实现稳定的代码覆盖率收集。
1、 消融实验:验证多进程框架有效性
实验将仅支持单进程模糊测试的 GREENHOUSE 作为基线,与集成多进程框架的 HOUSEMP 进行对比。
核心结果:
- HOUSEMP 在 16 个目标服务上的代码覆盖率明显优于 GREENHOUSE;
- 尽管由于多进程反馈收集带来额外开销,HOUSEMP 总体少发现 7 个漏洞;
- 但 HOUSEMP 额外检测出了 14 个 GREENHOUSE 未发现的漏洞;
- 53.7% 的目标服务存在 IPC(进程间通信)行为,且所有服务均包含多个协同进程。
结论:
- 仅关注网络主进程会遗漏大量真实执行路径;
- 引入多进程反馈后,能够更有效探索守护进程、工具进程等隐藏逻辑;
- 多进程模糊测试虽然存在性能开销,但能显著提升覆盖率与漏洞挖掘能力。
2、多进程漏洞分析:验证真实漏洞检测能力
研究人员进一步对 HOUSEFUZZ 检测出的漏洞进行了人工分析,重点识别“多进程漏洞”。
核心发现:
- 多进程漏洞需要同时触发:
- 一个进程中的 IPC 行为;
- 另一个进程中的漏洞代码;
- 单进程模糊测试由于缺少跨进程反馈,几乎无法发现此类漏洞;
- 在实验中共识别出 24 个多进程漏洞;
- 其中 17 个获得了 CVE/CNVD 编号,占全部编号漏洞的 38%。
结论:
- 多进程模糊测试框架不仅提升覆盖率;
- 更能够发现传统单进程模糊测试难以检测的真实跨进程漏洞。
3、TCE 稳定性实验:验证覆盖率收集可靠性
实验进一步评估 TCE 检测技术在多进程场景下的稳定性。
实验方法:
- 对 13453 个种子重复执行 20 次;
- 若同一种子始终产生一致覆盖率,则认为 TCE 检测稳定。
核心结果:
- 8746 个种子在单进程模式下本身稳定;
- 其中仅 358 个种子在多进程模式下出现覆盖率不一致;
- 不一致原因主要来自 select() 引发的非确定性控制流(NCL),而非 TCE 本身;
- 排除 NCL 影响后,剩余 8388 个种子实现了 100% 一致覆盖率。
结论:
- TCE 检测技术能够稳定地确定覆盖率收集时机;
- 在多进程模糊测试中具备较高鲁棒性与可靠性;
- 可支持稳定的多进程代码覆盖率反馈机制。
(五)、服务协议引导的模糊测试实验(Q4)
本节主要验证服务协议引导机制与 TDG(Token Dependency Graph,令牌依赖图)推理技术的有效性,重点分析其对漏洞检测能力和测试用例质量的提升作用。

1. 消融实验:验证协议引导模糊测试有效性
实验对比了三种方案:
- HOUSECFG:仅使用 CFG 与基础令牌生成测试用例;
- HOUSETDGOL:引入定制化服务协议,但不包含离线 TDG 推理;
- HOUSEFUZZ:完整集成协议建模与离线 TDG 推理。
核心结果:
- HOUSECFG 仅检测到 36 个漏洞;
- HOUSETDGOL 相比:
- GREENHOUSE 漏洞数提升 49%;
- HOUSECFG 提升 94%;
- HOUSEFUZZ 在 HOUSETDGOL 基础上进一步多发现 55 个漏洞。
性能提升原因:
- 基于 TDG 的协议建模能够生成更高质量的测试用例;
- 离线 TDG 推理补充了在线分析能力;
- 在线组件只能识别标准字符串比较函数;
- 离线组件还能识别定制化字符串比较逻辑,从而发现更多令牌与依赖关系。
结论:
- 服务协议引导机制能够显著提升 Linux 固件服务的漏洞检测能力;
- TDG 建模能够有效增强模糊测试输入的结构感知能力。
2. TDG 推理有效性分析
实验进一步分析了 TDG 的规模与质量。
核心统计结果:
- 每个 TDG 平均包含:
- 734 个节点(令牌);
- 2610 条依赖边;
- 所有 TDG 都存在具有实际意义的令牌依赖关系;
- 这些依赖关系能够真实描述服务输入结构。
结论:
- Linux 固件服务中普遍存在定制化协议结构;
- 使用 TDG 对协议进行建模具有实际价值。
3. TDG 精确率评估
由于缺乏真实标准数据集,实验采用人工分析方式评估 TDG 推理质量。
(1)令牌推理精确率
实验随机抽取 100 个令牌进行人工验证。
结果:
- HOUSEFUZZ 令牌推理精确率达到 62%;
- GREENHOUSE 仅为 15%。
原因:
- HOUSEFUZZ 使用了细粒度静态分析与动态分析;
- GREENHOUSE 仅依赖粗粒度字符串扫描。
(2)依赖关系推理精确率
实验随机分析 100 条 TDG 边。
结果:
- 排除错误令牌后;
- TDG 依赖关系推理精确率达到 44%。
分析:
- 尽管精确率并非完全准确;
- 但模糊测试能够通过大量迭代自动筛选有效依赖关系;
- 因此当前 TDG 推理质量已足够支持高效测试。
总体结论
- 服务协议引导机制显著提升了漏洞挖掘能力;
- TDG 能有效建模 Linux 固件中的定制化协议结构;
- 离线+在线结合的 TDG 推理方案能够生成更高质量测试用例;
- 即使 TDG 推理并非完全准确,也依然能够有效辅助模糊测试。
六、局限
本节主要分析 HOUSEFUZZ 在实际应用中的局限,问题主要集中在仿真环境、认证处理、协议建模、TDG 推理精度以及适用范围等方面。
1. 仿真保真度仍受限制
- HOUSEFUZZ 本质上仍基于 process emulation,而非真实设备运行;
- 初始化异常与服务启动失败问题尚未完全解决;
- 会影响代码覆盖率与漏洞发现能力。
结论:
- 当前进程仿真技术的可靠性仍有提升空间;
- 仿真环境仍是固件 fuzzing 的核心瓶颈之一。
2. 难以覆盖认证后的深层逻辑
- 部分固件服务需要登录、鉴权或 session 建立后才能进入核心功能;
- HOUSEFUZZ 当前缺少 authenticated session 维护机制;
- 因此难以发现 post-authentication vulnerabilities。
结论:
- 对需要认证的目标,覆盖率与漏洞检测能力会明显受限;
- 深层高权限逻辑仍难以有效测试。
3. 对未知协议支持有限
- HOUSEFUZZ 的协议引导依赖 CFG 与 TDG 等协议模型;
- 约 32.4% 的服务使用未知或非标准协议;
- 缺乏标准协议文法时,协议引导能力会下降。
结论:
- 对非标准协议、私有协议的适配能力有限;
- 仍需结合协议逆向与 structure-aware fuzzing 进一步改进。
4. TDG 推理存在误差
- 当前 TDG 推理并非完全准确;
- Token 与依赖关系推断中仍存在噪声;
- 部分生成输入无法满足真实语义约束。
结论:
- 协议建模效果并非完全稳定;
- 但 fuzzing 的试错机制可以一定程度缓解该问题。
5. 适用范围较集中
- HOUSEFUZZ 主要面向 Linux 固件中的网络服务与 daemon 进程;
- 对非 Linux 固件、非网络服务的适用性尚未充分验证。
结论:
- HOUSEFUZZ 属于针对 Linux 固件场景的专项优化方案;
- 并非适用于所有嵌入式系统的通用 fuzzing 框架。
七、关于项目的尝试
因为项目也给出来了docker镜像,所以在本机也简单跑了一下。
这里先说说感受,就是首先对固件仿真这块的限制是比较大的。我本地是win11开的docker然后里面再跑qemu进行仿真其实效果有点有限,仿真感受也很慢(毕竟虚拟机套虚拟机了)。
其次是关于模糊测试这个部分,在成功之后模糊测试这里需要自己在IDA上给出一个种子集,这里我有点没看懂,我给了一个空种子集合或者复用了一下自带的种子集最后依旧没有成功,遗憾退场:(


浙公网安备 33010602011771号