首页
社区
课程
招聘
OpenSSL国密爆出8.1分高危漏洞CVE-2021-3711
发表于: 2021-9-3 18:56 4593

OpenSSL国密爆出8.1分高危漏洞CVE-2021-3711

2021-9-3 18:56
4593

背景

OpenSSL 是一个知名的开源安全套接字层密码库。全球成千上万的 web 服务器的网站加密技术使用 OpenSSL。

网银、在线支付、电商网站、门户网站、电子邮件等互联网应用广泛使用 OpenSSL 实现数据的安全传输和安全存储。

历史上,OpenSSL 多次出现安全漏洞。


2014 年,OpenSSL 爆出 Heartbleed(心脏滴血)漏洞,网络出现了“致命内伤”。

心脏滴血称为互联网安全历史上最严重的漏洞之一,当时全球三分之二的网站可被该漏洞攻击。

心脏滴血漏洞的 CVE 编号是 CVE-2014-0160,CVSS3.1 打分 7.5,属于严重漏洞。


业界使用 CVE ID 作为漏洞编号。CVE 是通用漏洞披露(Common Vulnerabilities and Exposures)的英文缩写。

业界采用 CVSS 量化漏洞影响。CVSS 是通用漏洞评分系统(Common Vulnerability Scoring System)的英文缩写。

CVSS 得分最大为 10,最小为 0。得分 7~10 的漏洞通常认为严重,得分在 4~6.9 之间是中级漏洞,0~3.9 是低级漏洞。


2021 年 8 月 24 日,OpenSSL 发布了 OpenSSL 1.1.1l,该版本修复了一个高危漏洞:CVE-2021-3711。

根据

https://access.redhat.com/security/cve/cve-2021-3711

该漏洞的 CVSS3.1 打分 8.1,属于严重漏洞。

该漏洞影响 OpenSSL 1.1.1l 之前的所有包含 SM2 商密算法版本。业界一些基于 OpenSSL 改造过的商用国密算法版本也可能受该漏洞影响。


本文结合 OpenSSL 公告、修复前后的 OpenSSL 代码和触发漏洞的 sm2 密文数据,分析 CVE-2021-3711 漏洞原理,并评估对腾讯自研国密算法库的影响。

漏洞分析

根据官网披露的信息细节https://www.openssl.org/news/secadv/20210824.txt

得出如下分析:


漏洞原因:SM2 解密时分配了一块内存,解密后的结果可能大于该分配内存的容量,造成内存越界写。


以下是具体分析,使用 CVE-2021-3711 漏洞修复之前的 OpenSSL 1.1.1 代码。


1、OpenSSL EVP 解密操作

OpenSSL EVP 将常用的密码算法进行了封装,提供统一的密码学各种函数。


看示例图找规律,OpenSSL 对密文的解密是什么样的操作?

示例 1:crypto/evp/p_open.c

示例 2:crypto/crmf/crmf_lib.c

示例 3:crypto/cms/cms_env.c

示例 4:crypto/pkcs7/pk7_doit.c

实际应用中密文的解密一般需要调用两次 EVP_PKEY_decrypt。

第一次调用 EVP_PKEY_decrypt,指针 out 为 NULL,返回长度 keylen。

通过 OPENSSL_malloc 分配一块 keylen 大小的堆内存。

第二次调用 EVP_PKEY_decrypt,指针 out 为第一次调用所分配的内存,运算结束后存放解密结果。


2、EVP_PKEY_decrypt 实现

在初始化 EVP_PKEY_CTX 结构后,通过 EVP_PKEY_decrypt 可以调用到具体的密码算法执行解密运算。

int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx,                     unsigned char *out, size_t *outlen,                     const unsigned char *in, size_t inlen){    int ret;
...

if (ctx->op.ciph.algctx == NULL)    goto legacy;

ret = ctx->op.ciph.cipher->decrypt(ctx->op.ciph.algctx, out,                                          outlen, (out == NULL ? 0 : *outlen), in, inlen);return ret;
legacy:
   ...
}


3、pkey_sm2_decrypt 实现


对于 SM2 解密,EVP_PKEY_decrypt 中的 ctx->op.ciph.cipher->decrypt 对应的是 pkey_sm2_decrypt。


pkey_sm2_decrypt 函数位于 crypto/sm2/sm2_pmeth.c。

static int pkey_sm2_decrypt(EVP_PKEY_CTX *ctx,                            unsigned char *out, size_t *outlen,                    const unsigned char *in, size_t inlen)
                   
{    
   EC_KEY *ec = ctx->pkey->pkey.ec;
   
   SM2_PKEY_CTX *dctx = ctx->data;
   
   const EVP_MD *md = (dctx->md == NULL) ? EVP_sm3() : dctx->md;

     if (out == NULL) {    
       if (!sm2_plaintext_size(ec, md, inlen, outlen))
           
            return -1;
   
       else
       
       return 1;
       
}

return sm2_decrypt(ec, md, in, inlen, out, outlen);
}

根据第一节 OpenSSL EVP 解密操作可知,第一次调用 EVP_PKEY_decrypt 函数时,指针 out 为 NULL,返回长度作为接下来分配堆内存的大小。


这里 sm2_plaintext_size 函数返回 outlen,作为接下来分配堆内存的大小。


4、sm2_plaintext_size 实现


sm2_plaintext_size 函数位于 crypto/sm2/sm2_crypt.c


int sm2_plaintext_size(const EC_KEY *key, const EVP_MD *digest, size_t msg_len,                       size_t *pt_size)
                     
{    
   const size_t field_size = ec_field_size(EC_KEY_get0_group(key));
   
   const int md_size = EVP_MD_size(digest);
   
   size_t overhead;
   
   
     if (md_size < 0) {    
   SM2err(SM2_F_SM2_PLAINTEXT_SIZE, SM2_R_INVALID_DIGEST);
       return 0;
 
}

     if (field_size == 0) {    
   SM2err(SM2_F_SM2_PLAINTEXT_SIZE, SM2_R_INVALID_FIELD);
       return 0;
       
  }

     overhead = 10 + 2 * field_size + (size_t)md_size;
     if (msg_len <= overhead) {    
   SM2err(SM2_F_SM2_PLAINTEXT_SIZE, SM2_R_INVALID_ENCODING);
    return 0;
   
}

*pt_size = msg_len - overhead;
return 1;

}

注意:返回的长度等于 msg_len - overhead,而 overhead = 10 + 2 * field_size+(size_t)md_size。


5、overhead 存在的问题


sm2 国密算法知识

关于 overhead 的设置,涉及 SM2 算法和 SM2 密文格式的知识,在此进行补充。


SM2(SM 是“商密”拼音的缩写)是我国商用密码的公钥密码标准,标准号为:GM/T 0003-2012。

SM2 标准中规定采用 256 比特的椭圆曲线域参数。

SM2 算法采用 SM3 算法作为算法步骤中的哈希算法,SM3 算法的输出是 256 比特的哈希值。

根据 GM/T 0009-2012,SM2 密文格式如下:

这里,XCoordinate 和 YCoordinate 是加密过程基于随机数计算出的椭圆曲线点的 X 坐标和 Y 坐标。


overhead 取值分析


查看 sm2_plaintext_size 函数:


field_size = ec_field_size(EC_KEY_get0_group(key)),对于 SM2 算法,field_size 等于 32。md_size = EVP_MD_size(digest),SM2 算法采用 SM3 算法,因此 md_size 等于 32。


从上述 2 点可知,sm2_plaintext_size 函数中的 overhead 取值等于 106(10+2*32+32)。


这里的 magic number 10 背后有什么含义呢?


对于 SM2 密文,ASN.1 包括 5 个 Tag 和 5 个 Length,ASN.1 编码引入的长度不小于 10 个字节。分析如下:

每个 Tag 占 1 个字节,5 个 Tag 占 5 个字节。


XCoordinate、YCoordinate 和 HASH 由于值的长度范围相对固定,这 3 个 Length 占 3 个字节。


取决于 CipherText 值,CipherText 和第一个 tag 后面的 Length 长度不定,这 2 个 Length 可能超过 2 个字节。这里 overhead 选择 10,是选择 SM2 密文 ASN.1 编码引入的长度的最小值。


返回的长度等于 msg_len - overhead,若 overhead 取值小,则返回长度大,分配内存大于实际需要,不会溢出。


这里的 field_size 没有考虑 XCoordinate 和 YCoordinate 的具体取值,有没有风险?


1)XCoordinate 和 YCoordinate 是加密过程基于随机数计算出的椭圆曲线点的 X 坐标和 Y 坐标,满足以下方程:YCoordinate * YCoordinate ≡ XCoordinate * XCoordinate * XCoordinate - 3 * XCoordinate + b(mod p)这里,≡表示方程的左右两边模 p 的结果相等,p 和 b 是 SM2 国密标准中规定的常数。


2)满足上述方程的 XCoordinate 和 YCoordinate 通常都是占 32 字节的大数。


3)如果密文中携带的 XCoordinate 占 31 字节,YCoordinate 占 32 字节,则真实的 overhead 可能小于 106。


此时使用 msg_len - 106 的结果去会分配空间,导致分配的空间小于解密后的结果,内存越界写。


4)存在满足上述方程的占 31 字节甚至更少的 XCoordinate 或 YCoordinate 吗?


OpenSSL 给出的 SM2 密文数据示例给出了肯定的回答。

触发漏洞的数据示例


1、SM2 密文数据

OpenSSL 给出的密文数据示例如下:

3072022070DAD60CDA7C30D64CF4F278A849003581223F5324BFEC9BB329229BFFAD21A6021F18AFAB2B35459D2643243B242BE4EA80C6FA5071D2D847340CC57EB9309E5D04200B772E4DB664B2601E3B85E39C4AA8C2C1910308BE13B331E009C5A9258C29FD040B6D588BE9260A94DA18E0E6


2、解析 SM2 密文

这组密文的长度是 116 字节。按照 ASN.1 格式解析这组密文:

3072 //30 表示 SEQUENCE 类型,72 表示后续的数据总长度是 114 字节

0220 //02 表示 INTEGER 类型,20 表示该整数的长度是 32 字节

70DAD60CDA7C30D64CF4F278A849003581223F5324BFEC9BB329229BFFAD21A6 //32 字节的 XCoordinate

021F //02 表示 INTEGER 类型,1F 表示该整数的长度是 31 字节

18AFAB2B35459D2643243B242BE4EA80C6FA5071D2D847340CC57EB9309E5D //31 字节的 YCoordinate

0420 //04 表示 OCTETSTRING 类型,20 表示该字符串的长度是 32 字节

0B772E4DB664B2601E3B85E39C4AA8C2C1910308BE13B331E009C5A9258C29FD //32 字节的 HASH

040B //04 表示 OCTETSTRING 类型,0B 表示该字符串的长度是 11 字节

6D588BE9260A94DA18E0E6 //11 字节的密文


经过验证,上述的 XCoordinate 和 YCoordinate 满足 SM2 椭圆曲线方程。

3、触发堆溢出


第一次调用 pkey_sm2_decrypt,指针 out 为 NULL,msg_len 等于 116。sm2_plaintext_size 函数返回 10(msg_len - overhead = 116 - 106)。通过 OPENSSL_malloc 分配 10 字节的内存,out 指向该内存。第二次调用 pkey_sm2_decrypt,由于密文有 11 字节,因此解密结果也是 11 字节。

out 指向的内存是 10 字节,而解密结果是 11 字节,导致越界写 1 字节。


腾讯自研国密库不受该漏洞影响

近年来,国家积极推进国产密码基础设施的建设,推广与应用。


为贯彻落实国家密码战略,推进公司产品信息安全,腾讯自研了 TencentSM 国密算法库,摆脱对国外开源密码算法库的依赖。


TencentSM 符合国密 SM2、SM3 以及 SM4 算法标准,已在腾讯多个业务中平稳运行。


TencentSM 自研了 SM2 解密实现,未使用和参考 OpenSSL 该部分所对应的代码,不受该漏洞影响。



[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//