-
-
[原创]OpenSSL漏洞分析(CVE-2021-3450)
-
2021-4-26 10:21 7851
-
一、漏洞介绍
前段时间OpenSSL项目组发布了一个CA证书绕过的漏洞(CVE-2021-3450),主要就是X509_V_FLAG_X509_STRICT标志可对证书链中存在的证书进行其它安全检查,默认情况下未设置。从OpenSSL版本1.1.1h开始,添加了一项检查以禁止在链中显式编码椭圆曲线参数的证书,这是附加的严格检查。执行此检查时出现一个错误,这意味着先前检查的结果会被覆盖,该检查用于确认链中的证书是有效的CA证书。
二、漏洞细节
漏洞位置在 check_chain_extensions 这个函数里面,大概就是在进行X509_V_FLAG_X509_STRICT 标志验证的时候,没有判断check_ca的结果,就进行X509_V_FLAG_X509_STRICT 标志的验证,这样会把之前check_ca的验证结果给覆盖掉,从而绕过ca检查。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | ret = X509_check_ca(x); / / 这里返回ca检查的结果 switch (must_be_ca) { case - 1 : if ((ctx - >param - >flags & X509_V_FLAG_X509_STRICT) && (ret ! = 1 ) && (ret ! = 0 )) { / / 当ret不等于 0 和 1 的时候,会把ret置为 0 ,及证书有问题 ret = 0 ; ctx - >error = X509_V_ERR_INVALID_CA; } else ret = 1 ; break ; case 0 : if (ret ! = 0 ) { ret = 0 ; ctx - >error = X509_V_ERR_INVALID_NON_CA; } else ret = 1 ; break ; default: / * X509_V_FLAG_X509_STRICT is implicit for intermediate CAs * / if ((ret = = 0 ) || ((i + 1 < num || ctx - >param - >flags & X509_V_FLAG_X509_STRICT) && (ret ! = 1 ))) { ret = 0 ; ctx - >error = X509_V_ERR_INVALID_CA; } else ret = 1 ; break ; } / / 这里是判断是否验证 X509_V_FLAG_X509_STRICT 这个标志· / / 这里应该就是漏洞的主要原因,没有判断上面check_ca的返回值直接进行 X509_V_FLAG_X509_STRICT 的验证 / / 如果check_ca的返回结果异常,在继续进行其他验证,会把异常结果覆盖,从而绕过ca检查 / / 所以补丁文件在这里加上了ret> 0 的时候才进行 X509_V_FLAG_X509_STRICT 的验证 if ((ctx - >param - >flags & X509_V_FLAG_X509_STRICT) && num > 1 ) { / * Check for presence of explicit elliptic curve parameters * / ret = check_curve(x); / / 这里进行 X509_V_FLAG_X509_STRICT 标志验证,会把check_ca的结果给覆盖掉 if (ret < 0 ) ctx - >error = X509_V_ERR_UNSPECIFIED; else if (ret = = 0 ) ctx - >error = X509_V_ERR_EC_KEY_EXPLICIT_PARAMS; } if ((x - >ex_flags & EXFLAG_CA) = = 0 && x - >ex_pathlen ! = - 1 && (ctx - >param - >flags & X509_V_FLAG_X509_STRICT)) { ctx - >error = X509_V_ERR_INVALID_EXTENSION; ret = 0 ; } |
三、如何触发
通过官方的介绍,此漏洞触发需要显式设置 X509_V_FLAG_X509_STRICT 验证标志,那么该怎么设置这个标志呢???
通过对补丁文件的分析及源码的分析,发现需要在初始化X509_store的时候显式设置才行
1 2 | / / 这里必须显式设置才行!!!! X509_STORE_set_flags(store, X509_V_FLAG_X509_STRICT); / / 验证的时候设置一下,即可触发漏洞,绕过ca检查 |
这里只需要调用 X509_STORE_set_flags 函数设置一下X509_V_FLAG_X509_STRICT 标志即可
四、测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | / / 根证书 - - >签发中间证书 - - >签发待验证证书 / / 这里构造一条证书链 static const char * roots_f = "ca.cer" ; / / 根证书 static const char * untrusted_f = "crt.cer" ; / / 待验证证书 static const char * interrept = "crt_inter.cer" ; / / 中间证书 static int test_chains_cert() { int ret = 0 ; int i; X509 * x = NULL; STACK_OF(X509) * untrusted = NULL; BIO * bio = NULL; X509_STORE_CTX * sctx = NULL; X509_STORE * store = NULL; X509_LOOKUP * lookup = NULL; STACK_OF(X509) * intermediates = NULL; X509 * icert = NULL; BIO * bio_in = NULL; store = X509_STORE_new(); if (store = = NULL) goto err; lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); if (lookup = = NULL) goto err; if (!X509_LOOKUP_load_file(lookup, roots_f, X509_FILETYPE_PEM)) goto err; / / 这里必须显式设置才行!!!! X509_STORE_set_flags(store, X509_V_FLAG_X509_STRICT); / / x = load_cert_pem(untrusted_f); if ((bio = BIO_new_file(untrusted_f, "r" )) = = NULL) goto err; if ((x = PEM_read_bio_X509(bio, NULL, 0 , NULL)) = = NULL) goto err; sctx = X509_STORE_CTX_new(); if (sctx = = NULL) goto err; intermediates = sk_X509_new_null(); bio_in = BIO_new_file(interrept, "r" ); icert = PEM_read_bio_X509(bio_in, NULL, NULL, NULL); BIO_free(bio_in); sk_X509_push(intermediates, icert); if (!X509_STORE_CTX_init(sctx, store, x, intermediates)) goto err; printf( "here here\n" ); i = X509_verify_cert(sctx); if (i = = 0 && X509_STORE_CTX_get_error(sctx) = = X509_V_ERR_INVALID_CA) { / * This is the result we were expecting: Test passed * / fprintf(stderr, "X509_verify_cert is Failed. error: %d\n" , GetLastError()); goto err; } fprintf(stderr, "X509_verify_cert is OK! return -> %s\n" , i ? "true" : "false" ); err: X509_STORE_CTX_free(sctx); X509_free(x); BIO_free(bio); sk_X509_pop_free(untrusted, X509_free); X509_STORE_free(store); return ret; } |
···
···
···
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
最后于 2021-5-21 14:47
被陌殇编辑
,原因:
赞赏
他的文章
[原创]第一题,签到题wp
1304
[原创]Android应用完整性保护总结
15958
看原图