本文介绍CVE-2022 0778漏洞及其复现方法,并精心构造了具有一个非法椭圆曲线参数的证书可以触发该漏洞。
本博客已迁移至CatBro's Blog,那是我自己搭建的个人博客,欢迎关注。本文链接
漏洞描述[1]漏洞出自BN_mod_sqrt()
接口函数,它用于计算模平方根,且期望参数p应该是个质数,但是函数内并没有进行检查,这导致内部可能出现无限循环。这个函数在解析如下格式的证书时会被用到:
- 证书包含压缩格式的椭圆曲线公钥时
- 证书带有显式椭圆曲线参数,其基点是压缩格式编码的
总之,在解析证书时需要对点坐标进行解压缩操作的就会调用到这个函数。所以外部可以通过精心构造一个具有非法的显式曲线参数的证书来触发无限循环,从而造成DoS拒绝服务攻击。
函数分析官方补丁commit[2]
我们先简单过一下这个函数的实现。实现函数签名如下,a是操作数,p是模数
BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
首先对p做了简单的检查,对p是偶数、1这两个显然不是质数的情况直接报错,对p为2的情况进行了特殊处理。
if (!BN_is_odd(p) || BN_abs_is_word(p, 1)) {
if (BN_abs_is_word(p, 2)) {
// ...
}
BNerr(BN_F_BN_MOD_SQRT, BN_R_P_IS_NOT_PRIME);
return NULL;
}
接下来对a是0或1的特殊情况做了特殊处理
if (BN_is_zero(a) || BN_is_one(a)) {
// ...
}
然后计算A := a mod p
,就是计算非负余数
if (!BN_nnmod(A, a, p, ctx))
下面几行的字面意思是从p的第1位开始数有几个连续的0,其实是将|p| - 1表示成如下的格式:|p| - 1 == 2^e * q
,即表示成2的幂次方的奇数倍,其中q是奇数。例如p为49,表示二进制是110001,那么e为4,q为3,49 - 1 == 2^4 * 3
e = 1;
while (!BN_is_bit_set(p, e))
e++;
接下来对e等于1或2的简单情况进行特殊处理,因为不会走到无限循环,我们跳过
if (e == 1) {
}
if (e == 2) {
}
对于e > 2的情况,就需要老老实实用Tonelli/Shanks算法来计算了。首先需要找到一个不是平方数的y,且0 < y < |p|
,因为不是重点我们跳过。
接下来计算q的值,将p右移e位就得到了q
if (!BN_copy(q, p))
// ...
if (!BN_rshift(q, q, e))
y := (y ^ q) mod p
,因为y是个非平方数,所以计算q次方可以得到一个阶为2^e的值。(Don't ask me why ,注释这么写的