零日漏洞研究曾经是国家级行为体和独立研究人员的专属领域,如今已发展成为一个庞大的市场。随着发现和利用的零日漏洞数量持续增加,漏洞研究——即分析系统以发现新漏洞的过程——在网络安全中扮演了关键角色。
对于攻防安全领域的新手来说,漏洞研究似乎带有某种神秘色彩,远超一般的黑盒渗透测试或网页攻击,深入到内存破坏、汇编代码和动态插桩的深层次领域。这种印象部分源于大多数零日漏洞报告只描述漏洞本身,而不说明发现过程。
此外,漏洞研究范围涵盖硬件和软件,寻找特定漏洞的方法差异巨大。正如你将学到的,开展有效的漏洞研究需先制定整体策略,再选择具体技术。
本章将带你进入漏洞研究世界。你将了解漏洞报告基础、什么是漏洞(以及什么不是)、以及三大核心技术领域:代码审计、逆向工程和模糊测试。我还会给出简单的准则,帮助你找到适合自己研究的目标。但发现漏洞之前,你需要先知道该寻找什么。
什么是漏洞?
美国国家标准与技术研究院(NIST)给出漏洞定义:
信息系统、安全流程、内部控制或实现中的弱点,可能被威胁源利用或触发。
首先,漏洞必须是系统设计或实现中的缺陷,一旦被利用,系统将表现出开发者未预期的不安全行为。
行业标准的**通用漏洞评分系统(CVSS)**通过机密性、完整性和可用性三要素评估漏洞影响:
- 机密性:攻击者可访问未授权的数据;
- 完整性:攻击者可修改未授权的数据;
- 可用性:攻击者可破坏系统的可用性。
这些要素描述成功利用漏洞对系统的影响,帮助对漏洞进行特征描述。例如,允许攻击者随意写文件的漏洞影响完整性,但未必影响机密性。虽然在找漏洞时可能不常考虑这些,但在撰写漏洞披露报告时非常有用。
其次,漏洞必须是可被威胁源利用的弱点。如果系统存在缺陷但无利用途径,那它只是“缺陷”,不是漏洞。缺陷指导致非预期功能的错误。所有漏洞都是缺陷,但反之不成立。例如某路由器固件报告错误星期几的代码缺陷,无法被外部触发,也不突破安全边界,属于缺陷而非漏洞。
通用漏洞与披露(CVE)记录
通用漏洞披露编号(CVE)如 CVE-2020-19909 或 CVE-2020-21469 是分配给公开披露漏洞的唯一标识。由 MITRE 机构管理的系统逐渐成为全球引用漏洞的标准。然而,CVE 并非所有漏洞的“官方”认证,且并非所有漏洞都有对应 CVE,也并非所有 CVE 都是真正漏洞。
CVE 系统为一个由多个 CVE 编号机构(CNA)组成的联邦式管理体系,授权相关厂商或组织分配适用于其产品的 CVE 编号,从而控制发布流程。虽然有统一的分配规则及中央申请表单,但编号仍较为分散,偶有误报。
缺陷与漏洞的区别
混淆缺陷和漏洞常导致错误报告。例如 curl 和 Postgres 项目曾拒绝一些可视为缺陷但非漏洞的披露。
curl 项目的 CVE-2020-19909 涉及命令行参数 --retry-delay 的整数溢出。虽然代码中存在溢出,且理论上可能被利用让系统更早重试失败请求,但未越过安全边界,也难说明存在安全风险。官方说明指出该溢出可能导致拒绝服务,但前提是用户输入异常巨大且不现实的值。
类似地,Postgres 的 CVE-2020-21469 涉及通过反复发送 SIGHUP 信号导致拒绝服务,利用该漏洞需要已获得高权限账户。该功能可用标准操作完成同样结果,故项目方认为这不是安全漏洞。
记住这些区别,避免浪费时间在非安全问题上。
后续章节(第10章)将详细介绍负责任披露流程及与 CNA 合作的细节。现在你对漏洞的定义更清晰了,我们接下来讨论漏洞研究的相关内容。
什么是零日漏洞研究?
零日漏洞研究是对软件和硬件目标进行系统性分析,以发现安全漏洞的过程。其涵盖范围广泛,从桌面应用程序到物联网设备,再到操作系统内核,几乎涵盖所有技术领域。
鉴于范围之广,漏洞研究通常聚焦于单一组件,例如操作系统中的某个驱动程序,或物联网设备中的某个网络服务。虽然覆盖所有潜在漏洞和目标超出本书范围,但通过聚焦特定组件,我们可以总结出适用于多个组件的通用技术,如逆向工程共享库或对网络协议进行模糊测试。尽管各目标的具体漏洞和背景有所不同,发现漏洞的流程遵循共同的工作流程。
为了更好理解漏洞研究,我们先将其与渗透测试区分开来。
漏洞研究与渗透测试的区别
漏洞研究和渗透测试共享一些技术和工具,但目标不同。
渗透测试旨在发现并利用特定系统(如网络应用或网络)的漏洞。这些漏洞不一定是新的,比如渗透测试人员可能扫描网络以寻找使用已公开利用脚本的过时Active Directory服务器。
而漏洞研究致力于发现软件或硬件目标中的漏洞。虽然这些目标可能是一个系统,比如路由器,但研究对象不限于某个特定实例(如某企业的企业网络或某域名下的Web服务器)。如果你发现路由器的新漏洞,那么理论上所有使用该路由器的网络都面临风险。
漏洞研究的目标与渗透测试的目标在于其公开可得性差异。例如,渗透测试可能发现某组织使用的定制插件脚本可被攻击者利用取得服务器访问权限,但该脚本仅存在于该组织的服务器上,非开源或商业产品,因此不属于典型漏洞研究的范畴。
此外,虽然漏洞必须可被利用,但漏洞研究不一定涉及实际利用。例如,你发现某程序存在可覆盖栈上返回地址指针的缓冲区溢出,只需一个简单的触发该漏洞的概念验证(PoC)即足够完成漏洞研究。真正开发可执行任意shellcode的完整利用代码属于利用开发范畴,是渗透测试必经的步骤。在大型漏洞研究机构中,发现漏洞和开发利用通常由不同团队负责,前者将成果交给后者精炼成可靠利用。这种分工在操作系统内核中的复杂漏洞(如堆破坏)尤为常见,因为需要精确的payload以适配不同版本和内存状态。不过,漏洞研究和利用开发常被混为一谈,本书聚焦于更狭义的漏洞发现定义。
漏洞研究的另一大特点是利用静态和动态分析(包括逆向工程)对目标进行深入分析。渗透测试通常评估目标的实际安全态势,可能仅限于黑盒测试(不了解内部实现),针对外部攻击面执行测试,比如对Web应用的请求测试。相对而言,漏洞研究侧重白盒(了解内部实现如源码)和灰盒(部分了解内部)分析,通过代码审计和逆向工程技术,从“内向外”的视角发现系统弱点。因此,漏洞研究在发现深层漏洞方面更具优势。
学科与技术
如前所述,漏洞研究主要包括三大核心学科:代码审计、逆向工程和模糊测试。正如接下来章节所展示的,这三者均涵盖手动与自动化技术。例如,代码审计始于基础的手工源到汇追踪,随后深入自动化代码分析工具;模糊测试则通过自动生成和测试异常输入实现漏洞发现。下面是对各学科的简要概述。
代码审计
代码审计是分析系统源代码以发现漏洞的过程。本书以代码审计作为起点,而非逆向工程或模糊测试,旨在为根因分析和污点分析等高级技能打下基础。理解目标功能是漏洞研究的关键组成部分,因此先从代码入手,有助于在进行逆向工程或模糊测试时更好地构建目标“后端”的概念。
代码审计看似比逆向工程或模糊测试更简单,但发现漏洞的难度并不与漏洞的严重程度成正比。有些关键漏洞竟然极易发现。例如,CVE-2021-44228 是 Apache Log4j 中一个毁灭性的远程代码执行漏洞,影响了大量系统并给防御者带来巨大压力。漏洞根源是 2016 年 Alvaro Muñoz 和 Oleksandr Mirosh 发现的 JNDI 注入漏洞。该漏洞自 2013 年起就存在于 Log4j 代码库,却未被发现,说明代码审计人员或自动扫描工具没有识别出该问题,或即使发现也不知道该查什么。
这也说明代码审计的重要性:绝大多数软件都以某种形式使用开源代码,从共享库到复制粘贴的代码片段。数十年前存在的漏洞代码依然潜藏在最新软件中等待被发现。针对分支代码,后续项目可能修补了漏洞,却未同步到上游。例如,我曾发现 Apache OpenOffice 中的远程代码执行漏洞,该漏洞已在 LibreOffice 中被修复(详见 spaceraccoon.dev/all-your-d-…)。由于软件依赖链不断扩大,一个开源项目的漏洞可能影响成千上万个其他项目,进而影响数百万用户。
一个典型例子是 liblzma 库中发现的后门。liblzma 是一个提供数据压缩功能的软件库,因广泛应用于多个 Linux 发行版,攻击者在特定条件下可利用该后门访问全球任何开放了 SSH 服务的服务器。
鉴于开源依赖的普遍存在,研究者无需尝试攻破专有软件中加固和混淆的代码,而是可以有创意地攻击开源依赖。例如,安全研究者“Angelboy”通过发现并利用开源的 Netatalk(Apple 文件协议服务器)中的漏洞,在多款网络附加存储(NAS)设备上实现了远程代码执行,而非针对厂商编写的软件。
逆向工程
逆向工程指对软件(如由源码编译而成的二进制可执行文件)进行拆解,揭示并分析其内部机制。从这个角度看,逆向工程是代码审计的延续。虽然它看起来比分析人类可读代码更具挑战,但也更令人兴奋,因为许多目标依赖“安全通过模糊”来隐藏明显的弱点。这意味着它们可能避开了只做代码审计的安全研究者的关注。随着时间推移,缺乏透明度导致漏洞不断累积而未被发现。第一个正确逆向该软件的研究者,极有可能发现隐藏在表面之下的大量漏洞。
代码审计就像阅读复杂地图找到从 A 点到 B 点的路径,而逆向工程则像探索黑暗隧道,可能在下一个转弯处发现意想不到的宝藏。当然,这并不意味着你会摸黑前行。我们将讨论如何通过静态和动态分析系统性地一步步绘制目标的“地图”,最终走出一条与代码审计类似的路径。
逆向工程不仅限于汇编代码,也可以将二进制编译为中间语言(如 Java 字节码),甚至包括嵌入脚本语言(如 JavaScript)的解释器。通过提取或反编译的部分源码进行分析,建立在代码审计能力之上,这也是为什么你会在学习代码审计后再学习逆向工程。
模糊测试
模糊测试是一种高度自动化的漏洞发现手段,通过向目标发送各种无效或意外输入进行“轰炸”。早期的漏洞研究中,模糊测试大多是“开着模糊器等漏洞崩溃”的方式。现代覆盖引导模糊测试(coverage-guided fuzzing)采用更先进有效的技术枚举目标。随着模糊测试工具的日益易用,许多研究者将其纳入工作流,成熟的产品团队也利用模糊测试在开发生命周期早期发现低垂漏洞。
你将学会通过编写模糊测试挂钩(fuzzing harness)优化模糊流程,重点测试目标中有趣或被忽略的部分。编写优化的挂钩需要借助大量代码审计和逆向工程的概念。
选择漏洞研究目标
练习目标选择能够大幅提升你发现漏洞的几率。正如你将看到的,挑选合适的研究目标充满挑战,因为目标未必一定存在漏洞。我建议初学者优先选择白盒目标,以便练习漏洞研究的三大核心学科。网络上有大量开源项目和免费软件的源码可供选择。
我们可以借鉴类似 CIA 三原则的“三要素”法则来选定目标:熟悉度(Familiarity)、可用性(Availability)和影响力(Impact)。
熟悉度
熟悉度指你对目标的了解程度。你应挑选使用你熟悉的编程语言或框架开发的目标。虽然许多漏洞在不同语言和框架中原理类似,但某些漏洞利用依赖特定环境。比如反序列化漏洞在不同上下文中存在细微差异。不过,只要你能理解代码,没必要成为语言特定漏洞利用专家。
有时目标已被研究或有良好文档,会议演讲、白皮书和漏洞报告都能帮助你快速熟悉。虽然你可能想避开已被充分研究的“硬目标”,但热门项目功能不断变化,仍有探索空间。尝试之前不要轻言放弃!
还要考虑目标平台类型,这影响可利用漏洞类别和你发现漏洞的能力。它是 Web 应用还是原生共享库?调用 Windows API 吗?运行于客户端还是服务器?目标是否使用了知名协议和标准?良好文档有助于你识别常见函数和例程,节省识别时间。
可用性
不同于 CIA 三原则中的“可用性”,漏洞研究中可用性指目标的可访问性和易分析程度。评估项目可用性时,有几个重要因素:
- 源码获取难易:代码托管于 SourceForge(许多老项目存在)还是 GitHub?能否追踪版本变更和开发分支?项目是单仓库还是多子项目分散?你不希望浪费时间追踪私有依赖或晦涩共享库。
- 测试环境搭建难度:深入漏洞研究后,你需要在可运行的项目实例上测试,构建 PoC(最小利用代码)。项目可能提供编译好的二进制,但可能缺少调试标志或配置,增加利用开发难度。源代码看似存在漏洞,但可能有运行时校验或验证机制减缓风险。正如 Manul Laphroaig 所言:“PoC || GTFO”,即必须证明漏洞可被利用。
- 容器化或良好文档:理想项目提供容器化构建选项或详尽的搭建说明。源码编译复杂时,寻找带调试符号和配置的开发构建版本。边看代码边测试潜在漏洞以验证假设,确保研究贴近目标实际工作机制。
- 项目所有者可接触性:发现漏洞后,应有责任方愿意确认并修补。查看 README 或官网是否有安全联系邮箱。例如 Apache 软件基金会(ASF)提供统一安全邮箱 security@apache.org 和项目级安全联系人。但部分项目所有者可能不欢迎或无法响应漏洞报告。
影响力
影响力指目标的重要程度。对一个已死气沉沉、几十年前的项目挖漏洞虽具教育意义,但若没人使用则无实际影响。但有时“死项目”重要性超乎想象,比如某死项目是维护老旧文件格式兼容性的唯一解析库。
npm 注册表中就有典型案例。js-yaml 包(www.npmjs.com/package/js-…)体积小,更新少,但依赖者超过 2 万,周下载量高达一亿次!此类目标漏洞发现后,波及范围巨大,如 CVE-2021-44228 爆发后全球争相修补 Log4j 一样。
评估项目影响力指标多样,如 GitHub 星标数、分支数、下载量和其他项目使用情况。优先选择目标取决于你的研究目标,但多用户目标通常更具吸引力。
项目探索途径
考虑以上因素后,从数百万代码库中选目标确实不易。建议访问 GitHub 话题页面 github.com/topics ,按编程语言筛选,并按星标、分支或最近更新时间排序。这样若你专注某类项目(如模拟器、框架),可快速定位潜力目标。同时,可关注 GitHub Trending 页面,发现尚未广泛被漏洞研究者关注的新兴项目。
另一个选项是浏览 Apache 软件基金会项目目录 projects.apache.org ,可按名称、委员会、类别、语言及提交者数量排序。ASF 项目有成熟漏洞披露流程及安全联系人,但避免“老旧项目”漏洞挖掘,因为这类项目很难获得漏洞响应。
综合来看,合理的目标选择策略不仅提高效率,也为你后续的漏洞研究打下坚实基础。
总结
本章通过定义漏洞研究、将其与渗透测试区分开来,介绍了这一快速发展和演进的领域,详细讲解了漏洞研究的三大学科(代码审计、逆向工程和模糊测试),并讨论了如何选择熟悉、易获取且具影响力的研究目标。现在,是时候深入我们的第一个学科:代码审计了。