5个追求更好披露流程的理由 - Trail of Bits博客
案例1: borsh-rs Rust库中的未定义行为
第一个案例涉及加密序列化库borsh-rs中的一个漏洞,该漏洞两年未修复,这成为实施全面安全策略的理由。
在审计期间,我发现不安全的Rust代码在与未实现Copy特性的零大小类型一起使用时可能导致未定义行为。尽管之前有人报告过这个错误,但由于开发人员不清楚如何避免代码中的未定义行为并保持相同属性(例如,抵抗DoS攻击),因此未修复。在此期间,库用户未被告知该错误。
使用GitHub的私有报告功能可以简化整个过程。如果项目开发人员在私下报告时无法解决漏洞,他们仍然可以一键通知Dependabot用户。在GitHub上私下报告漏洞时,发布实际修复是可选的。
我联系了borsh-rs开发人员,讨论在没有可用修复时通知用户的问题。开发人员认为最好通知用户,因为只有库的某些用途会导致未定义行为。我们提交了通知RUSTSEC-2023-0033,创建了GitHub公告。几个月后,开发人员修复了该错误,并发布了主要版本1.0.0。然后我更新了RustSec公告以反映已修复。
以下代码包含导致未定义行为的错误:
|
|
图1:使用不安全的Rust代码(borsh-rs/borsh-rs/borsh/src/de/mod.rs#123–150)
图1中的代码将字节反序列化为某种泛型数据类型T的向量。如果类型T是零大小类型,则执行不安全的Rust代码。代码首先将向量的请求长度读取为u32。之后,代码分配一个空的Vec类型。然后将T的单个实例推入其中。随后,通过调用forget函数临时泄漏刚分配的Vec的内存,并通过将Vec的长度和容量设置为请求的长度来重建它。因此,不安全的Rust代码假定T是可复制的。
不安全的Rust代码防止DoS攻击,其中反序列化的内存表示明显大于序列化的磁盘表示。该攻击通过将向量长度设置为大数并使用零大小类型来工作。我们博客文章"Billion times emptiness"中描述了此错误的一个实例。
案例2: Rust库中解析以太坊ABI的DoS向量
7月,我披露了四个以太坊API解析库中的多个DoS漏洞,这些漏洞难以报告,因为我必须联系多个方面。
该错误影响了四个GitHub托管项目。只有Python项目eth_abi启用了GitHub私有报告。对于其他三个项目(ethabi、alloy-rs和ethereumjs-abi),我必须研究谁在维护它们,这容易出错。例如,我不得不通过向GitHub提交URL附加后缀.patch来获取维护者的电子邮件地址。以下链接显示了我用于提交的非工作电子邮件地址:
https://github.com/trailofbits/publications/commit/a2ab5a1cab59b52c4fa71b40dae1f597bc063bdf.patch
有关此错误的技术细节,请阅读博客文章"Billion times emptiness"。
案例3: Expo中认证标签长度限制缺失
2022年底,Trail of Bits的安全工程师Joop van de Pol在expo-secure-store中发现了一个加密漏洞。在这种情况下,供应商Expo未能跟进我们关于他们是否确认或修复了错误,这让我们一无所知。更糟糕的是,尝试跟进供应商消耗了大量时间,这些时间本可以用于在开源软件中查找更多错误。
当我们最初通过其GitHub上列出的电子邮件地址secure@expo.io向Expo发送有关该漏洞的电子邮件时,一名Expo员工在一天内回复并确认他们将把报告转发给他们的技术团队。然而,在那次回复之后,尽管在一年内进行了两次温和的提醒,我们再也没有收到Expo的回音。
不幸的是,Expo不允许通过GitHub进行私有报告,因此电子邮件是我们唯一的联系地址。
现在来看错误的细节:在API级别23以上的Android上,SecureStore使用KeyStore中的AES-GCM密钥来加密存储的值。在加密期间,标签长度和初始化向量(IV)由底层的Java加密库作为Cipher类的一部分生成,并与密文一起存储:
|
|
图2:在存储中加密项目的代码,其中标签长度存储在密文旁边(SecureStoreModule.java)
对于解密,读取密文、标签长度和IV,然后使用KeyStore中的AES-GCM密钥进行解密。
有权访问存储的攻击者可以将现有的AES-GCM密文更改为具有更短的认证标签。根据底层Java加密服务提供程序的实现,最佳情况下的最小标签长度为32位(这是NIST规范允许的最小值),但在最坏情况下可能更低(例如,8位甚至1位)。因此,在最佳情况下,攻击者修改密文后相同标签被接受的概率很小但不可忽略,但在最坏情况下,这个概率可能很大。无论哪种情况,成功概率都取决于密文块的数量。此外,重复的解密失败和成功最终都会泄露认证密钥。有关如何执行此攻击的详细信息,请参阅NIST的GCM中的认证弱点。
从加密的角度来看,这是一个问题。然而,由于需要存储访问,在实践中可能难以利用此问题。根据我们的发现,我们建议将标签长度固定为128位,而不是写入存储并从那里读取。
故事本应在这里结束,因为我们在初始交流后没有收到Expo的任何回复。但在我们的第二次电子邮件提醒中,我们提到我们将公开披露此问题。一周后,通过将最小标签长度限制为96位,该错误被静默修复。实际上,96位提供了足够的安全性。但是,也没有理由不选择更高的128位。
修复是在我们最后一次提醒后整整一周创建的。我们怀疑我们之前的电子邮件提醒导致了修复,但我们不确定。不幸的是,我们从未得到适当的认可。
案例4: num-bigint Rust库中的DoS向量
2023年7月,Trail of Bits的安全工程师Sam Moelius在著名的num-bigint Rust库中遇到了一个DoS向量。尽管通过电子邮件披露非常顺利,但用户从未通过GitHub公告或CVE被告知此错误。
num-bigint项目托管在GitHub上,但未设置GitHub私有报告,因此库作者或我们无法快速创建公告。Sam通过发送电子邮件向num-bigint的开发人员报告了此错误。但查找开发人员的电子邮件容易出错且耗时。不是直接发送错误报告,您必须首先通过电子邮件确认您已联系到正确的人,然后才发送错误详细信息。使用GitHub私有报告或存储库中的安全策略,发送漏洞的渠道将很明确。
但现在让我们讨论漏洞本身。该库实现了非常大的整数,这些整数不再适合原始数据类型如i128。除此之外,库还可以序列化和反序列化这些数据类型。Sam发现的漏洞隐藏在该序列化功能中。具体来说,由于内存消耗过大或请求的内存分配过大且失败,库可能会崩溃。
num-bigint类型实现了Serde的特征。这意味着crate中的任何类型都可以使用任意文件格式(如JSON或bincode crate使用的二进制格式)进行序列化和反序列化。以下示例程序显示了如何使用此反序列化功能:
|
|
图3:示例反序列化格式
事实证明,某些输入会导致上述程序崩溃。这是因为实现Visitor特征使用不受信任的用户输入来分配特定的向量容量。下图显示了可能导致程序崩溃并显示消息"memory allocation of 2893606913523067072 bytes failed"的行。
|
|
图4:基于用户输入分配内存的代码(num-bigint/src/biguint/serde.rs#61–108)
我们最初于2023年7月20日联系了作者,该错误于2023年8月22日在提交44c87c1中修复。修复版本于第二天发布为0.4.4。
案例5: 使用react-native-mmkv将MMKV数据库加密密钥插入Android系统日志
最后一个案例涉及react-native-mmkv库中明文加密密钥的披露,该问题于2023年9月修复。在为客户进行安全代码审查期间,我发现了一个提交,修复了关键依赖项中未跟踪的漏洞。由于没有安全公告或CVE ID,我和客户都未被告知该漏洞。漏洞管理的缺乏导致了一种情况,即攻击者知道漏洞,但用户却一无所知。
在客户参与期间,我想验证加密密钥的使用和处理方式。提交修复:“Don’t leak encryption key in logs"在react-native-mmkv库中引起了我的注意。以下代码显示了有问题的日志语句:
|
|
图5:初始化MMKV并记录加密密钥的代码
在该修复之前,我正在调查的加密密钥以明文形式打印到Android系统日志中。这破坏了威胁模型,因为即使启用了Android调试功能,也不应从设备提取此加密密钥。
经客户同意,我通知了react-native-mmkv的作者,作者和我得出结论,应通知库用户有关该漏洞。因此,作者启用了私有报告,我们一起发布了GitHub公告。错误被分配了ID CVE-2024-21668。该公告现在在运行npm audit或npm install时如果使用易受攻击版本的react-native-mmkv,会提醒开发人员。
此案例强调,在npm包方面,基本上无法绕过GitHub公告。填充npm audit命令输出的唯一方法是创建GitHub公告。使用私有报告简化了该过程。
要点
GitHub的私有报告功能有助于保护软件生态系统。如果使用正确,该功能可以为漏洞报告者和软件维护者节省时间。私有报告的最大影响是它与GitHub公告数据库相关联——例如,在使用GitLab的机密问题时缺少这种链接。借助GitHub的私有报告功能,现在有一个流程供安全研究人员发布到该数据库(经存储库维护者批准)。
在GitHub上使用私有报告也使披露过程更加清晰。使用电子邮件时,不清楚是否应加密电子邮件以及应发送给谁。如果您曾经加密过电子邮件,您知道有无尽的陷阱。
但是,您可能仍想向开发人员或安全联系人发送电子邮件通知,因为维护者可能会错过GitHub通知。带有已创建公告链接的基本电子邮件通常足以提高意识。
步骤1: 添加安全策略
发布安全策略是拥有漏洞报告流程的第一步。为避免混淆,良好的策略明确定义了发现漏洞时应采取的措施。
GitHub有两种发布安全策略的方式。您可以在存储库根目录中创建SECURITY.md文件,或者通过创建.github存储库并在其根目录中放置SECURITY.md文件来创建用户或组织范围的策略。
我们建议从使用disclose.io的Policymaker生成的策略开始(参见此示例),但将"Official Channels"部分替换为以下内容:
我们有多个接收报告的渠道:
- 如果您发现特定GitHub项目的任何安全问题,请单击相关GitHub项目中"Security"选项卡上的"Report a vulnerability"按钮:https://github.com/%5BYOUR_ORG%5D/%5BYOUR_PROJECT%5D。
- 发送电子邮件至security@example.com
始终确保至少包含两个联系点。如果一个失败,报告者在回退到直接向开发人员发送消息之前仍有另一个选项。
步骤2: 启用私有报告
现在安全策略已设置,请查看引用的GitHub私有报告功能,该工具允许 discreetly 向维护者沟通漏洞,以便他们可以在公开披露之前修复问题。它还会通知更广泛的社区,例如npm、Crates.io或Go用户,有关其依赖项中的潜在安全问题。
启用和使用该功能很容易,几乎不需要维护。唯一的关键是确保正确设置GitHub通知。仅当您配置电子邮件通知时,报告才会通过电子邮件发送。默认情况下未启用此功能的原因是此功能需要主动监控GitHub通知,否则报告可能得不到所需的关注。
配置通知后,转到存储库的"Security"选项卡并单击"Enable vulnerability reporting”:
关于报告漏洞的电子邮件主题为"(org/repo) Summary (GHSA-0000-0000-0000)。“如果您使用网站通知,您将收到类似这样的通知:
如果您想为整个组织启用私有报告,请查看此文档。
使用私有报告的一个好处是漏洞发布在GitHub公告数据库中(有关更多信息,请参阅GitHub文档)。如果依赖存储库启用了Dependabot,则对您项目的依赖会自动更新。
除此之外,GitHub还可以自动发布CVE ID,可用于在GitHub之外引用错误。
此私有报告功能在GitHub上仍处于官方测试阶段。我们遇到了小问题,如缺少消息模板和报告者无法添加协作者。我们将后者作为错误报告给GitHub,但他们声称这是设计如此。
步骤3: 通过webhooks获取通知
如果您希望在自选的消息平台(如Slack)中接收通知,可以在GitHub上创建存储库或组织范围的webhook。只需启用以下事件类型:
创建webhook后,repository_advisory事件将发送到设置的webhook URL。该事件包括报告漏洞的摘要和描述。
如何让安全研究人员满意
如果您想增加从安全研究人员那里获得高质量漏洞报告的机会,并且已经在使用GitHub,那么请设置安全策略并启用私有报告。简化报告安全错误的过程对于软件的安全性很重要。它还有助于避免研究人员变得烦恼并决定不报告错误,或者更糟糕的是,决定将漏洞转化为漏洞利用或作为0-day发布。
如果您使用GitHub,这是您采取行动的时候,通过设置基本安全策略和启用私有报告,优先考虑安全,保护公共软件生态系统的安全,并为每个人营造更安全的开发环境。
如果您不是GitHub用户,其他问题跟踪系统上也存在类似功能,例如GitLab中的机密问题。但是,并非所有系统都有此选项;例如,Gitea缺少此类功能。我们在本文中关注GitHub的原因是该平台由于其公告数据库而处于独特地位,该数据库馈送到例如npm包存储库。但无论您使用哪个平台,请确保您有可见的安全策略和可靠的渠道设置。