LibreSSL与OSS-Fuzz集成:漏洞挖掘与修复实战

本文详细记录了将LibreSSL集成到OSS-Fuzz的过程,通过模糊测试发现14+漏洞,包括ASN.1解析器中的内存破坏问题,并展示了具体代码修复和Google漏洞奖励计划的实际案例。

LibreSSL与OSS-Fuzz

2020年4月8日 - 作者:Andrea Brancaleoni

模糊测试集成奖励的故事

在Doyensec的第一个月,我有机会将工作和业余爱好结合起来。我利用Doyensec提供的25%研究时间,将LibreSSL库集成到OSS-Fuzz中。LibreSSL是OpenSSL的API兼容替代品,在心搏漏洞(Heartbleed)攻击后,它被视为OpenBSD、macOS和VoidLinux上OpenSSL的成熟替代方案。

在这项研究中,我们获得了Google的10,000美元奖金,并全部捐赠给癌症研究协会。模糊测试器还发现了14个以上的新漏洞,其中四个直接与内存破坏相关。以下段落将详细介绍将新项目移植到OSS-Fuzz的过程,从遵循社区提供的步骤到实际代码移植,并展示在提交136e6c997f476cc65e614e514ac3bf6ee54fc4b4中修复的一个漏洞。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
commit 136e6c997f476cc65e614e514ac3bf6ee54fc4b4
Author: beck <>
Date:   Sat Mar 23 18:48:15 2019 +0000

    Add range checks to varios ASN1_INTEGER functions to ensure the

    13799 from oss-fuzz
    ok tb@ jsing@

 src/lib/libcrypto/asn1/a_int.c    | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/lib/libcrypto/asn1/tasn_prn.c |  8 ++++++--
 src/lib/libcrypto/bn/bn_lib.c     |  4 +++-
 3 files changed, 62 insertions(+), 6 deletions(-)

FOSS历史学家的模糊之书

作为VoidLinux维护者,我是LibreSSL的长期用户和支持者。LibreSSL是2014年从OpenSSL分叉出来的TLS/加密栈版本,目标是现代化代码库、提高安全性并应用最佳实践开发流程。这种分叉的动机源于心搏漏洞的发现。LibreSSL的努力旨在移除目标平台认为无用的代码,消除代码异味,并以兼容性为代价包含额外的安全默认值。LibreSSL代码库现在大约是OpenSSL的70%(237558行代码 vs 335485行代码),同时在所有主要现代操作系统上实现类似的API。

分叉通常被视为坏事,不仅因为它意味着未来大量浪费精力,而且因为分叉往往伴随着后继团体之间在合法性、继承和设计方向问题上的大量冲突和尖刻争论。存在严重的社会压力反对分叉。因此,主要的分叉(如Gnu-Emacs/XEmacs分裂、386BSD团体分裂为三个子项目,以及短暂的GCC/EGCS分裂)非常罕见,以至于它们在黑客民间传说中被单独记住。——Eric Raymond《 homesteading the Noosphere》

LibreSSL的努力普遍受到好评,现在它取代了OpenBSD、macOS(自10.11起)和许多其他Linux发行版上的OpenSSL。在最初几年,OpenSSL中发现了6个关键漏洞,没有一个影响LibreSSL。历史上,这类分叉往往会产生竞争项目,这些项目后来无法交换代码,将潜在的开发者池分裂。然而,LibreSSL团队在很大程度上展示了能够合并和实施新的OpenSSL代码和错误修复,同时精简原始源代码并减少很少使用或危险的功能。

OSS-Fuzz选择

虽然LibreSSL的开发似乎是一个有 happy ending 的故事,但将模糊测试和安全审计集成到项目中却远非如此。心搏漏洞像是行业的警钟,用于解决构成互联网核心的库的安全性。特别是,Google开放了OSS-Fuzz项目。OSS-Fuzz是一项免费提供Google基础设施以对最流行的开源库执行模糊测试的努力。事实上,执行这些测试的首批项目之一是OpenSSL。

模糊测试是一种众所周知的发现软件中编程错误的技术。许多这些可检测的错误,如缓冲区溢出,可能具有严重的安全影响。OpenSSL在提交c38bb72797916f2a0ab9906aad29162ca8d53546中包含了模糊测试器,并于2016年晚些时候集成到OSS-Fuzz中。

1
2
3
4
5
6
7
commit c38bb72797916f2a0ab9906aad29162ca8d53546
Refs: OpenSSL_1_1_0-pre5-217-gc38bb72797
Author:     Ben Laurie <ben@links.org>
AuthorDate: Sat Mar 26 17:19:14 2016 +0000
Commit:     Ben Laurie <ben@links.org>
CommitDate: Sat May 7 18:13:54 2016 +0100
    Add fuzzing!

由于LibreSSL和OpenSSL共享大部分代码库,LibreSSL主要实现OpenSSL的安全子集,我们认为将OpenSSL模糊测试器移植到LibreSSL将是一个有趣且有用的项目。此外,这导致发现了几个与内存相关的破坏错误。需要注意的是,以下细节不会取代官方OSS-Fuzz指南,而是帮助选择OSS-Fuzz集成的好目标项目。一般来说,申请新的OSS-Fuzz集成包括四个逻辑步骤:

  1. 选择:选择一个尚未移植的新项目。检查OSS-Fuzz项目目录中的现有项目。例如,检查是否有人已经在拉取请求中尝试执行相同的集成。
  2. 可行性:检查该项目在互联网上的可行性和安全影响。作为一般准则,项目对网络日常使用的影响越大,奖金就越大。在撰写本文时,OSS-Fuzz奖金通过Google补丁奖励计划高达20,000美元。另一方面,任何集成都需要开发良好的覆盖范围。因此,集成已经采用模糊测试器的项目更容易。
  3. 技术集成:遵循超级详细的入门指南执行初始集成。
  4. 盈利:申请Google补丁奖励计划。盈利?!

我们获得了奖金,并帮助保护互联网更多一点。你也应该这样做!

心碎

在发现崩溃后,OSS-Fuzz基础设施提供了一个最小化的测试用例,供分析师检查。问题发现在ASN.1解析器中。ASN.1是一种形式符号,用于描述电信协议传输的数据,无论语言实现和这些数据的物理表示如何,无论复杂还是非常简单。巧合的是,它用于x.509证书,这代表了构建公钥基础设施的技术基础。

通过dumpasn1传递我们的测试用例0202 ff25,可以看到它如何出错,说长度为2(字节)的整数用负值编码。这在ASN.1中是不允许的,甚至在LibreSSL中也不应该允许。然而,正如OSS-Fuzz所发现的,这个测试使Libressl解析器崩溃。

1
2
3
4
5
6
7
8
$ xxd ./test
xxd ../test
00000000: 0202 ff25                                ...%
$ dumpasn1 ./test
  0   2: INTEGER 65317
       :   Error: Integer is encoded as a negative value.

0 warnings, 1 error.

由于LibreSSL实现没有防范负整数,尝试将精心制作的负ASN.1整数转换为BIGNUM的内部表示,导致不受控制的过度读取。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
AddressSanitizer:DEADLYSIGNAL
    =================================================================
    ==1==ERROR: AddressSanitizer: SEGV on unknown address 0x00009fff8000 (pc 0x00000058a308 bp 0x7ffd3e8b7bb0 sp 0x7ffd3e8b7b40 T0)
    ==1==The signal is caused by a READ memory access.
    SCARINESS: 20 (wild-addr-read)
        #0 0x58a307 in BN_bin2bn libressl/crypto/bn/bn_lib.c:601:19
        #1 0x6cd5ac in ASN1_INTEGER_to_BN libressl/crypto/asn1/a_int.c:456:13
        #2 0x6a39dd in i2s_ASN1_INTEGER libressl/crypto/x509v3/v3_utl.c:175:16
        #3 0x571827 in asn1_print_integer_ctx libressl/crypto/asn1/tasn_prn.c:457:6
        #4 0x571827 in asn1_primitive_print libressl/crypto/asn1/tasn_prn.c:556
        #5 0x571827 in asn1_item_print_ctx libressl/crypto/asn1/tasn_prn.c:239
        #6 0x57069a in ASN1_item_print libressl/crypto/asn1/tasn_prn.c:195:9
        #7 0x4f4db0 in FuzzerTestOneInput libressl.fuzzers/asn1.c:282:13
        #8 0x7fd3f5 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/libfuzzer/FuzzerLoop.cpp:529:15
        #9 0x7bd746 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/libfuzzer/FuzzerDriver.cpp:286:6
        #10 0x7c9273 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/libfuzzer/FuzzerDriver.cpp:715:9
        #11 0x7bcdbc in main /src/libfuzzer/FuzzerMain.cpp:19:10
        #12 0x7fa873b8282f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/libc-start.c:291
        #13 0x41db18 in _start

这种“野生”地址读取可能被恶意行为者用于在安全敏感上下文中执行泄漏。Libressl维护团队不仅迅速解决了漏洞,还在提交46e7ab1b335b012d6a1ce84e4d3a9eaa3a3355d9中包含了一项额外保护,以防止缺少ASN1_PRIMITIVE_FUNCS。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
commit 46e7ab1b335b012d6a1ce84e4d3a9eaa3a3355d9
Author: jsing <>
Date:   Mon Apr 1 15:48:04 2019 +0000

    Require all ASN1_PRIMITIVE_FUNCS functions to be provided.

    If an ASN.1 item provides its own ASN1_PRIMITIVE_FUNCS functions, require
    all functions to be provided (currently excluding prim_clear). This avoids
    situations such as having a custom allocator that returns a specific struct
    but then is then printed using the default primative print functions, which
    interpret the memory as a different struct.

对陌生人关闭大门

模糊测试,尽管被视为发现安全漏洞的最简单方法之一,但仍然非常有效。即使OSS-Fuzz特别针对开源项目,它也可以适应闭源项目。事实上,以实现LLVMFuzzerOneInput接口为代价,它集成了所有最新和最伟大的clang/llvm模糊测试技术。

随着Dockerfile语言在开发运维方面的巨大改进,我们强烈认为OSS-Fuzz模糊测试接口定义语言也应该用于每个非平凡的闭源项目。如果你需要帮助,请联系我们进行安全自动化项目!

一如既往,这项研究得益于Doyensec提供的25%研究时间。请继续关注新剧集!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计