本文目标:精度论文 “CRYLOGGER: Detecting Crypto Misuses Dynamically” 。
针对密码算法的 API 误用,本文算是对上一篇 CryptoGuard 的改进版,实现了一个开源的动态和静态结合的自动检测工具。该论文发布于 2020 年的 S&P ,点击即可免费获取该工具,基准测试工具同 CryptoGuard 使用的论文相同,这里就放 CryptoGuard 实现的基准测试工具链接吧!研究就是层层递进的过程,我们都站在巨人的肩膀上,下一步该向哪儿爬,全在我们一念之间~
念叨
- 一念之间:现在的密码算法怎么样了?
- 二念之间:热门的 “自动检测”
- 三念之间:给通用加密库定规则
- 四念之间:检测详情 —— 长篇代码预警!
- 五念之间:这工具好用不?
- 六念之间:涉及到的逆向工程
- 七念之间:还是有局限性的...
一念之间:现在的密码算法怎么样了?
对于一个密码算法来说,能保证系统安全最重要的一点就是保证密钥的安全。从算法角度上讲,要保障系统机密性的话,就得关注加密算法;要保障系统完整性的话,就得关注加密 Hash 函数。
当然,研究这些基础加密算法的学者有很多,但至今也没能找到一个完全安全的加密算法。即便能够确保一个算法完全正确且安全,我们也无法保证这个算法的实现过程毫无漏洞,比如说误用。
举个具体的例子🌰吧:椭圆曲线加密算法的理论似乎很强悍,像我们的身份证就使用了这种算法去做加密,但早在 2012 年,就有学者利用 OpenSSL 的算法 bug 获取到了椭圆曲线加密算法的完整私钥。连密钥都拿到了,系统估计也就没啥安全性可言了。
抛开算法的理论层面,从上述提到的算法实现上探究,一些伴随着加密的新问题便显现了出来。2013 年就有学者调研出:从 Google Play Store 上下载的 88% 个 Android apps 至少有一种加密误用。具体而言,在保障系统机密性的层面上,一些 apps 使用硬编码密钥进行加密,而不是真正的随机密钥;在保障系统完整性的层面上,一些 Android apps 的哈希函数可能发生冲突,例如 SHA1 。
近年来,研究者们致力于分析各种情况下的密码误用。
① 堆栈溢出获取的代码片段不安全。通过堆栈溢出,有研究者获取了一些 Android apps 98%的代码片段,并分析发现它们大多存在严重的加密问题。
② 应用程序编程接口(API)太过复杂,这是 Java 中加密误用的主要根源。因为 API 复杂,所以开发人员不得不关注一些低级决策——比如选择加密算法的填充类型——而不是专注于高级任务。
③ 一些 Python 的加密库不够完善。研究者们发现 Python 加密库的文档不清不楚、代码示例不足、API 中默认配置也有误。
④ 第三方库不安全。Android 中 90% 的加密误用源自第三方库。
解决问题的前提是,要找到问题,所以检测工具就是这么产生的。由于上一篇的 CryptoGuard 自称是第一个最好的静态分析工具,因此,作者大概是读过 CryptoGuard 之后产生了新的 idea ,立刻反手做了第一个动态检测工具。接下来,作者从密码算法的 API 误用着手,针对一些常量,改进了 CryptoGuard 静态分析工具。
具体来说,CRYLOGGER 就是在 CryptoGuard 的基础上做了动态分析方法和静态分析方法相结合的策略,在线记录log文件,离线检测分析,使得整个工具更加完善。下面我们一点一点来分析 CRYLOGGER ~
二念之间:热门的 “自动检测”
自动化越来越流行,检测工具也不例外,在介绍动态 CRYLOGGER 之前,让我们先谈一谈热门的 “自动检测” 吧!这是设计 CRYLOGGER 工具的关键步骤。
一般来说, “自动检测” 的思想主要是这么两个方面:(1)定义一组加密规则;(2)通过验证加密 API 的传参,检查 apps 是否遵守了这些规则。
💨定规则 (依据)
◾ 论文里的 加密算法或其错误配置 造成的漏洞。
◾ NIST、IETF 等组织和机构定义的加密相关标准。
可以举个例子🌰:① 设置用于加密的最小密钥大小,例如 RSA 是 2048 位;② 设置用于密钥推导的最小迭代次数,例如 PKCS#5 是 1000 次。
💨守规则
◾ 静态方法:主要利用程序切片检查加密 API 的传参,例:CrySL、CryptoLint、 CryptoGuard、MalloDroid、 CogniCrypt、CMA等。
优势:不需执行,代码全分析,可扩展;
劣势:误报。
像上一篇提到的 CryptoGuard 就有很多误报因素,如分支过早被修剪(大概是上一篇提到的控制裁剪深度问题)、遗漏需动态加载的部分代码(大概是上一篇提到的幻想库问题)。
◾ 动态方法:
优势:需在运行时触发加密 API ,少误报;
劣势:难以使用,不支持静态方法的加密规则。
总之,现状就是探索静态分析方法的人多,而探索动态分析方法的人少,这也是作者发现的研究缺口。
来看看 CRYLOGGER 怎么运用自动检测的吧:
还是上面说的两个方面:定规则、守规则。
- logger 定规则:扩展 Java 加密库 ═▶ 跟踪(检测)加密算法的 API 调用 ═▶ log 文件记录(存储)必须用于检查加密规则的相关参数值。
例如:上图中的 logger 保存 apps 代码的 消息摘要(SHA1) 和 对称加密(AES) 的算法名; - checker 守规则:在线执行后 ═▶ 离线分析 log 文件 ═▶ 生成 apps 违规的规则列表。
需要一组检查过程,每个检查过程包含很多加密规则,例如上图中的(红色感叹号), checker 发现 apps 使用了不安全的 Hash 函数 (SHA1) 作为消息摘要算法。
为什么要分离成上述的两个过程?
(1) 加密库的参数稳定,即不太可能添加新参数,就比如说,密钥推导算法的关键参数只是 盐值(salt) 、密码(password) 以及 迭代次数;
(2) 加密规则不确定,新规则针对新漏洞,那么当前规则就需要更新,比如 RSA 的最小 key 的大小会有变数;
(3) 加密规则上下文相关,但一些规则可能与该系统的上下文无关;
(4) 在线检查规则可能会影响性能,而离线不会,这在某些 apps 的关键响应上非常重要。
现在我们已经清楚这个工具要从哪里入手了,接下来就把这两个过程了解得更清楚一点吧!
三念之间:给通用加密库定规则
先上图:
图中指示了经典密码库中的七大安全点:(1)消息摘要;(2)对称加密;(3)非对称加密;(4)密钥派生;(5)随机数生成;(6)密钥存储;(7) SSL/TLS/Certif 。
当然,这里仅包含了现存的静态工具使用的类及其在Java和Android中实现的相应类,它们是误用情况最严重的,但这个库仍不够完整。因此,作者加强了规则的可扩展性,使用者可以任意添加其需要的检测规则。
现在来解释这个图的具体含义:
(1)消息摘要 —— 用于检查数据完整性
【输入】(任意长)data
↓
Hash函数
↓
【输出】(固定长)Hash = 摘要
【重点参数】
alg :用作Hash函数的算法,例如, SHA1 、 SHA256 。不同的库支持不同的算法。
(2)对称加密的分组密码
【输入】(固定长)分组数据 = size + (算法定义长短)key
↓
分组加密
↓
【输出】分组数据 = (加密)密文 or (解密)明文
【重点参数】
alg :用作加密和解密的算法,例如 AES。
key :密钥。
#block :固定大小的分组数据。
mode :一些操作模式,用于处理多个分组数据,例如 电子密码本模式 (ECB) 使得分组数据独立加解密;密码分组链接模式 (CBC) 使得每个明文分组都与前面的密文分组异或;密码反馈模式 (CFB) ;输出反馈模式 (OFB) ;Galois/counter (GCM) 。
iv :初始化向量,定义第一个需要异或的分组数据。
pad :用于补足分组大小的填充算法,例如 最后一个分组填充 0 的 ZEROPADDING 、PKCS#5 、 PKCS#7 。
(3)非对称加密的公钥加密算法
【发送方】 【发送方】 | 【发送方】 【发送方】
↓ ↑ | ↓ ↑
用接收方的公钥加密 用接收方的私钥解密 | 用发送方的私钥签名 用发送方的公钥验签
↓ ↑ | ↓ ↑
【接收方】 【接收方】 | 【接收方】 【接收方】
【重点参数】
alg :用于加密的算法,例如 RSA、椭圆曲线(EC)、数字签名算法(DSA) 。
key :用到的公私钥对。
pad :算法,例如 NOPADDING、PKCS1-v1.5、PSS。
(4)密钥派生
【输入】password or passphrase
↓
加 salt
↓
Hash函数
↓
经固定迭代次数
↓
【输出】密钥
【重点参数】
pass :输入的密码或密码短语。
salt :盐值,即随机值。
iter :用于生成密钥的固定迭代次数,迭代次数越多,越不易被暴力攻击。
(5)随机数生成
本文只假设两类算法:Secure 和 NotSecure
【重点参数】
alg :如果生成适合加密的随机数,则使用 Secure 算法,否则使用 NotSecure 算法。
out :生成随机数的字节。
seed :生成的种子。
(6)密钥存储 —— 存储加密密钥、证书等敏感内容
【重点参数】
pass :输入的密码或密码短语。
(7) SSL/TLS/Certif 。
【重点参数】
urlprot :HTTP 或 HTTPS 连接。
allhost :验证是否接受所有的主机名。
allcert :验证证书是否可信。
sethost :SSL/TLS 连接的主机名验证。
以上便是针对加密算法所需要关注的脆弱点。
❗等等,图中的圆圈还没介绍❗
别急,我们上一”念“中提到过,定规则有两大依据:论文里的 加密算法或其错误配置 造成的漏洞;NIST、IETF 等组织和机构定义的加密相关标准。
作者从论文和 NIST 和 IETF的 官方发布文档中搜集 CRYLOGGER 应支持的规则,并制成下表。
具体地:
💨 R-01 不允许 apps 使用不安全的 哈希函数 ,也就是那些可以产生冲突的函数,比如 SHA1 ;
💨 R-02 禁止使用不安全的对称加密 算法 ,比如 Blowfish 、 DES 等;
💨 R-03和R-04 不允许 apps 使用 ECB 、CBC 操作模式 。因为 ECB 将相同的明文加密形式不变,这打破了语义安全的属性,容易受到攻击;而 CBC 在 C/S 中易受 padding oracle 攻击;
💨 R-05和R-06 限制 密钥的生成 。对称加密的密钥是由 apps 随机生成的,而非硬编码常量;密钥的随机生成器应安全;
💨 R-07和R-08 与上一条类似。对称加密中应使用的 IV ,而非密钥。作者认为当 IV 与某些操作模式 (如GCM) 匹配时, IV 始终随机且非常数。作者认为使用 IV 可以加强数据的保密性;
💨 R-09 不允许 重用 相同的 (密钥/ IV)对 加密不同的消息。因为重用可预测;
💨 R-10和R-05 是一样的,这里针对密钥生成中使用的 盐值 ;
💨 R-11 要求 盐值足够大 (≥64位);
💨 R-12 禁止 重用 相同盐值,因为它违背了向相应密码中添加随机性的目的;
💨 R-13 密钥生成算法的 迭代足够多 ,防暴力攻击;
💨 R-14和R-15 不允许使用列入 黑名单的密码 ,增大密码破解难度;
💨 R-16 禁止使用 固定密码 ;
💨 R-17 要求 伪随机数生成器(PRNG) 的 seed 使用随机值,而非常量值。因为常量 seed 生成的数字序列可预测;
💨 R-18 不允许 apps 使用未经批准的 PRNG ,比如 java.util.Random ;
💨 R-19、R-20和R-21 禁止 RSA算法 的某些配置。密钥应该 ≥2048位 ,加/解密时必须使用不同于 NOPADDING 和 PKCS1-v1.5 的填充算法;
💨 R-22 禁止使用 HTTP ,要求使用 更安全的 HTTPS ;
💨 R-23 禁止使用 静态密码 存储密钥;
💨 R-24和R-25 要求 正确验证 主机名和证书,比如不允许接受所有主机名或所有证书;
💨 R-26 禁止修改 标准主机名验证器 ,因为修改可能导致不安全的 SSL/TLS 通信。
以上,规则全部阐述完毕。下面开始检测!