Group of Software Security In Progress

Industrial Experience of Finding Cryptographic Vulnerabilities in Large-scale Codebases

作者:Ya Xiao, Yang Zhao, Nicholas Allen, Nathan Keynes, Danfeng (Daphne) Yao, Cristina Cifuentes

单位:Computer Science, Virginia Tech, Blacksburg, VA, Oracle Labs, Brisbane, Australia

出处:arXiv:2007.06122v1 [cs.SE] 12 Jul 2020

原文:https://arxiv.org/abs/2007.06122

摘要

企业环境需要筛选大规模(数百万行代码)的代码库以进行漏洞检测,因此对静态分析工具的精度和可扩展性提出了很高的要求。 在Oracle中,Parfait就是这样一种错误检查器,它提供了包括过程间分析的精确和可扩展的结果。CryptoGuard是一种精确的静态分析器,用于检测基于Soot构建的$Java^{TM}$代码中的密码学漏洞。本文描述了如何通过更改中间表示并依靠Parfait中需求驱动的IFDS框架将CryptoGuard集成到Parfait中,从而提供一种精确且可扩展的密码学漏洞检测工具。作者在几个大型的实际应用程序和一个全面的Java密码学漏洞基准测试CryptoAPI-Bench中对工具进行了评估。初步结果表明,Parfait中新的密码学漏洞检测功能可以检测真实世界中的大规模代码库,误报率极低且运行时较短。

I. 介绍

精确且可扩展的错误检查工具对于公司保证大型项目的安全性至关重要。

Parfait :可扩展的静态代码分析工具,旨在为大型代码库查找以C/C ++、Java、PL/SQL和SQL语言编写的安全性和质量缺陷,专注于CWE前25和OWASP前10列表中的缺陷。

CryptoGuard:精确的静态分析器,用于检测Java密码学API的误用,包含过程间流敏感、上下文敏感和字段敏感的数据流分析,用于筛选加密代码,并通过一组完善的切片算法显着减少虚假警报。

作者的工作旨在基于Parfait的可扩展框架和CryptoGuard的完善见解,实现对Java密码学API误用的精确且可扩展的检测。

许多Java密码学API被认为容易用错:

  • Java密码学体系结构(JCA)和Java密码学扩展(JCE)库中的API对于没有密码学专业知识的开发人员来说过于复杂,无法安全配置。
  • 流行的编码论坛(例如StackOverflow)中令人困惑的官方文档和令人误解的不安全代码示例,使这种做法更加糟糕。

误用这些API的结果很严重,导致了各种漏洞。一项调查显示,CVE数据库的“加密问题”类别中的漏洞已被密码学API误用占据了主导地位(83%)。

密码API误用的检测可以映射到一组程序分析问题上。这些漏洞大多数涉及使用常量或可预测的源来生成秘密和随机数。从密码学API的规定敏感参数(例如密码算法,密钥)开始,作者进行反向数据流分析以捕获其恒定源。但仍然存在一些重大挑战:

(1)来自静态分析的false positive可能非常高。生成加密材料时使用的许多常数(例如资源标识符)与安全性无关,加剧了false positive。

(2)筛选大型项目的可扩展性始终是静态分析面临的挑战。

由于实现方式不同,无法将CryptoGuard直接结合到Parfait中:

(1)Parfait由LLVM支持,而CryptoGuard基于Soot。CryptoGuard根据Soot的Jimple IR定义了五种改进。

(2)Parfait有一个分层的框架来优化分析集合。需要使密码学漏洞检测分析与Parfait框架兼容。

(3)Parfait中的数据流分析遵循过程间有限的满足分配律的子集合(inter-procedural, finite, distributive subset,IFDS)框架。通过将分析转换为图可达性问题,IFDS框架是多项式时间内的精确过程间数据流解决方案。就时间复杂度而言,优于CryptoGuard中基于普通流集的数据流分析。作者需要在一组IFDS算法的上下文中实现密码学漏洞检测。

作者的贡献:

  • 将密码学漏洞检测功能整合到了Oracle工具Parfait中。在Parfait 支持下实施了后向程序间,流敏感、上下文敏感、域敏感度分析。分析包括了在Parfait分层框架下的一组完善的、基于IFDS的分析算法。这些改进的算法具有与CryptoGuard相同的效果,可减少false positive并实现高精度。
  • 将密码漏洞检测应用到了大型应用程序中,发现了许多潜在漏洞,其准确性相对较高(93.44%)。包含2.4K至1321K行代码的代码库的运行时范围为2秒至36分钟,大多数项目可以在10分钟内完成。
  • 在全面的CryptoAPI-Bench上评估了Parfait密码学漏洞检测,总体实现了高精度(86.62%)和召回率(98.40%)。排除路径敏感性测试用例的精度甚至达到100%。作者通过几个测试案例分析了设计选择及其影响。

II. 背景

A. Java密码学API误用

作者从开发人员的角度总结了检测到的Java密码学API误用,并在表I中显示了所涉及的容易出错的API及其常见的易受攻击的用法。所涉及的Java类包括:

  • SecureRandom类:加密操作中使用的任何随机数应从SecureRandom生成,而不是从Random生成。此外,通过构造函数或setSeed方法设置静态或可预测的种子很容易受到攻击。
  • MessageDigest类:将被攻破的哈希算法(例如MD5)传递给getInstance API很容易受到攻击。
  • Cipher类: getInstance API易于使用被攻破的密码算法或不安全模式。 易受攻击的用法:1)通过弱密码算法(例如“ DES”); 2)为分组密码指定“ ECB”模式(“ AES / ECB / NoPadding”); 3)没有明确指定分组密码模式(“ AES”),因为默认使用ECB模式。
  • KeyStore和Key Specification 类。 例如SecretKeySpecPBEKeySpec,通过参数接受秘密信息(例如口令、密钥材料)。 任何接受硬编码或可预测的秘密信息的方法调用都是易受攻击的。
  • 算法参数类: IvParameterSpecPBEParameterSpec类管理IV、盐和PBE迭代计数。 静态或可预测的IV和盐会导致漏洞。 迭代次数必须不少于1000。
  • javax.net.ssl类: javax.net.ssl包中的Java类TrustManagerHostnameVerifierSSLSocketFactory的方法提供SSL/TLS服务。 当开发人员覆盖默认方法或跳过必要的步骤以绕过验证时,通常会产生漏洞。

/images/2021-05-17/image-20210503152147997.png

B. CryptoGuard

CryptoGuard应用后向程序切片来发现导致Java密码API误用的常量源和配置。

False Positive减少:CryptoGuard提出了5种改进见解,以消除导致误报的特定于语言的不相关元素。在分析过程中,删除状态指示(例如getBytes(“ UTF-8”))、资源标识符(map的keys)、bookkeeping indices(例如数组的大小参数)、上下文不兼容的常量以及不可行路径中的常量。

运行时改进:过程间分析中开销最大的部分通常是迭代正交探索。CryptoGuard通过将正交探索限制为深度1来改善运行时间。

C. CryptoGuard和Parfait的数据流分析

Parfait实现了许多支持程序分析的功能。IFDS分析框架是Parfiat的一个重要功能,尚未出现在CryptoGuard中。

CryptoGuard的数据流分析:CryptoGuard基于Soot的FlowAnalysis库实现了数据流分析。FlowAnalysis实现了过程内数据流分析,该过程将保持流集并沿数据流轨迹更新它,如图1(b)所示。CryptoGuard对调用图上的被调用者和调用者方法迭代地运行其过程内分析。此设计可能会导致多次重新探索被调用方方法。 为了降低复杂度,其实现将裁剪被调用者方法探索的默认深度设置为1。

Parfait的IFDS:Parfait实现了数据流分析以及IFDS框架。如图1(c)所示,IFDS框架通过在变量之间建立边并汇总超级控制流图上两个程序点之间的边来处理分析。这样可以尽可能避免不必要的重新分析。

/images/2021-05-17/image-20210503151512664.png

Parfait框架:为了提高可伸缩性,Parfait提供了一个分层的框架来优化静态程序分析。根据时间成本,从最快到最慢安排分析时间,这样可以以较低的时间开销找到更多的错误。具体来说,在密码学漏洞检测中,作者根据调用者的深度细分并动态安排分析到不同的层。

III. 检测方法与实现

检测涵盖了表I中所示的所有误用情况。它的两个可扩展性实现因素是Parfait的分层框架以及IFDS中用于处理被调用者方法的摘要机制。

A. 检测方法

检测逻辑与CryptoGuard相似,将密码学API误用映射到数据流分析问题。 具体分为三类:

第1组:过程间后向数据流分析。包括由常量源确定的API误用。具体来说,这些是表I中Java类SecureRandomMessageDigestCipherKeyStoreSecretKeySpecPBEKeySpecPBEParameterSpecIvParameterSpec的API。 需要进行过程间的后向数据流分析,以捕获API参数的常量源。根据漏洞类型对收集的常量源应用不同的检测规则:是否为常数、是否小于1000的数字或是否与某些弱算法匹配。

第2组:过程内模式匹配。表I中与TrustManagerHostnameVerifierSSLSocketFactory相关的漏洞属于此组。这些漏洞通常在负责身份验证操作的方法中产生。作者通过过程内模式匹配找到它们。具体来说,对于HostnameVerifier,检测方法verify的返回值是否始终为“ True”。对于TrustManager,在checkClientTrustedcheckServerTrusted方法中检测三种易受攻击的模式,其中包括1)缺少验证行为; 2)捕获验证异常而不抛出异常; 3)在特定路径下缺少验证。 对于SSLSocketFactory,执行过程内模式匹配,以检查在创建SSLSocketFactory实例之后是否调用了HostNameVerifier.verify方法。

第3组:Sanitizer VS. Verifier。将每个Random都报告为漏洞是不合理的。 因此,对于第1组中的跟踪参数,作者将Random视为Verifier,将SecureRandom视为Sanitizer。因此,仅在这些密码用法中报告Random

B. 密码漏洞检测实现

在Parfait的支持下,作者实现了过程间,流敏感、上下文敏感和域敏感的后向数据流分析,以检测密码漏洞。

调用方法的分层调度。Parfait优化了分析集合以提高可扩展性。图2展示了细分并分配给不同层的向后分析。分析是逐层安排的。 在每一层,后向分析最终会在三种情况下在当前方法的入口点结束。首先,验证一个真实的错误。其次,将潜在的错误清除为没有错误。第三,需要对其调用方方法进行进一步分析。进一步的分析将安排在下一层。 这样,可以首先执行需要较少时间的分析。 它还避免了两个潜在漏洞检测迹线的重复部分。这个分层的框架有效地提高了发现错误的效率。

/images/2021-05-17/image-20210503164155785.png

IFDS中的流函数。用于定义分析的流函数:

  • flow:该函数通过普通的非调用指令指定数据流边。具体来说,它适用于LLVM指令ReturnInstLoadInstStoreInstBitCastInst
  • phiFlow: 该函数通过LLVM phi指令指定数据流边缘。
  • returnVal:该函数指定被调用方法的ReturnInst及其调用站之间的数据流边。此时查询被调用方法的摘要边以处理被调用方法。
  • passArgs:该函数指定被调用方法的参数与在其调用点中传递的参数之间的数据流边。
  • callFlow:该函数将处理数据流边,而与被调用方法无关。大多数改进都发生在这里,以处理无法获取其实现的被调用方法。

密码漏洞检测重新定义了CallFlow中的默认数据流边。

被调用方法汇总。探索一种方法后,将存储该方法的摘要边以供将来使用。 Parfait全面详尽地预先汇总了所有方法,并根据需要查询被调用方法的摘要边。从叶子方法到其调用者,所有方法均根据调用图以自下而上的方式进行汇总。这种设计确保每种方法仅探索一次,因此消除了对被调用方方法的重新探索以避免复杂性爆炸。

C. 改进见解的实现

通过应用反向IFDS分析,可以捕获到达敏感参数的所有常量源。但是很难区分真正不安全的源和伪影响,这会导致极高的假阳性率。 CryptoGuard总结了五种伪影响,包括状态指示符、资源标识符、bookkeeping indices、上下文不兼容的常量以及不可行路径中的常量。细化见解用于消除伪影响。 作者将这些改进见解应用于IFDS算法和LLVM IR指令的上下文中。

CryptoGuard使用Jimple IR描述了完善的见解。为了除去状态指示符,它消除了对被调用方参数的跟踪,只要它们出现在标记有virtualinvoke的Jimple赋值语句中即可。为了除去资源标识符,CryptoGuard消除了对用interfaceinvokestaticinvoke标记的Jimple 赋值语句中出现的参数的跟踪。以IFDS的形式,它们被转换为适用于不同类型LLVM指令的可达边。具体来说,为三种调用指令更改了默认的callFlow函数:1)实例方法的赋值调用项2)静态方法的赋值调用项3)类构造函数的非赋值调用项。

/images/2021-05-17/image-20210503164135367.png

IV. 实测结果和准确性分析

作者在Parfait上的多个大型真实代码库和全面的密码漏洞基准测试中测试了密码学漏洞检测(CryptoAPI-Bench),以评估性能。

A. 实测发现

作者在12个大型代码库上运行了Parfait。 其中10个是Oracle内部产品,2个是开源项目。 报告有61个漏洞,其中57个已被手动验证为真阳性。 精度为93.44%。 作者已将检测到的漏洞报告给相应的开发人员。 在开源项目方面,作者进一步发现该漏洞不是处于非生产(开发)模式,就是处于最新版本中。 下面显示了几个在现实世界中检测到的案例。

/images/2021-05-17/image-20210503164844697.png

清单1显示了使用恒盐和迭代计数不足作为PBE参数的漏洞。这种情况代表了在初始化过程中硬编码的敏感密码材料(例如口令、盐、IV等)最常见的易受攻击模式。

/images/2021-05-17/image-20210503165207135.png

清单2引入了使用熵不足的盐的漏洞。当用相同的变量迭代分配随机盐时,其值空间显着减小,因此使穷举攻击成为可能。分析报告称,在第3行中涉及盐的构建的常数为16。 但是,为了准确地捕获不足的熵问题,需要执行符号。

/images/2021-05-17/image-20210503165745756.png

清单3显示了在开源项目Spring Security中检测到的漏洞,发布在CVE数据库中。SecureRandom实例依赖第4行上不可靠的InputStream作为种子。受此现实漏洞的启发,作者对SecureRandom.setSeed应用了更严格的规则,只有通过SecureRandom.generateSeed()方法进行的自播和手动播种才被视为安全。

/images/2021-05-17/image-20210503170142314.png

清单4显示了绕过证书验证的情况。尽管这是一个false positive的情况,但不建议代码中这样实现。这种情况通过简单地抛出所有证书的UnsupportedOperationException完全禁用了证书验证。 报告它的原因是它与易受攻击的模式匹配,即缺少验证(例如默认的checkclientTrust)。

运行时分析。 图5中列出了几个项目的运行时。检测的运行环境为Intel®Xeon®CPU E5-2690 v4 @ 2.60GHz,128G内存,Oracle Linux Server 6.9。大多数项目(甚至数百万行代码)都可以在10分钟内完成分析。由于这些内部项目的访问限制,无法在这些项目上运行CryptoGuard进行比较。但是,与CryptoGuard相比,作者的密码学漏洞检测具有更多的设计来实现更好的可伸缩性。

/images/2021-05-17/image-20210503170435660.png

B.Crypto-Bench的精确度分析

作者在158个测试用例上测试了13种CryptoAPI-Bench密码漏洞类型的Parfait。CryptoAPI-Bench包括从基本的测试单元到更高级的案例的各种测试单元。

总体精度和召回率分别为86.62%和98.40%。所有false positive案例都来自路径敏感案例,这验证了工具对于排除路径敏感的案例已实现了较高的精度。

/images/2021-05-17/image-20210503204142910.png

应用角度 VS. 库角度在某些情况下,Parfait与CryptoGuard的漏洞定义有所不同。 如果检测到问题出在应用程序中,则Parfait的设计更好地避免过高估计漏洞。 如果它们在库中,则CryptoGuard的设计能更好地发现潜在的错误方法,尽管它们可能尚未被调用。

潜在的改善 有两个潜在的改进可修复false-negative情况。首先,可能由于缺少clinit方法的概要而导致 false negative(附录清单8),解决方法是更新调用图构造以覆盖每个类的clinit。其次,false-negative情况由捕获的源(即String)和敏感参数(即int)之间的不兼容类型引起的(附录清单6),改善方法是通过使用Java语言进行类型转换检查类型兼容性。

局限性 在处理路径敏感的情况和指针问题方面仍然存在局限性。作者的分析针对的是大型项目,因此必须权衡以得出更好的整体性能。

V. 结论

作者基于可扩展的错误检查程序Parfait和精确的密码漏洞检测工具CryptoGuard,实现了精确且可扩展的密码学漏洞检测。利用CryptoGuard的完善洞察力,作者的检测结果再现了CryptoGuard所获得的高精度结果(很少或没有误报)。实验表明,对12个现实世界中的大型项目而言,不包括路径敏感的情况,其精度为93.44%,而对于CryptoAPI-Bench,则为100%。 得益于IFDS和Parfait的分层框架,密码漏洞检测还为大型代码库实现了良好的运行时性能。11个大型代码库的运行时间范围从2秒到36分钟。 大多数代码库可以在10分钟之内进行筛选。