首页
社区
课程
招聘
[原创]OpenSSL漏洞分析(CVE-2021-3450)
发表于: 2021-4-26 10:21 8837

[原创]OpenSSL漏洞分析(CVE-2021-3450)

2021-4-26 10:21
8837

前段时间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检查。

通过官方的介绍,此漏洞触发需要显式设置 X509_V_FLAG_X509_STRICT 验证标志,那么该怎么设置这个标志呢???
通过对补丁文件的分析及源码的分析,发现需要在初始化X509_store的时候显式设置才行

这里只需要调用 X509_STORE_set_flags 函数设置一下X509_V_FLAG_X509_STRICT 标志即可

···
···
···

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不等于01的时候,会把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;
}
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不等于01的时候,会把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_STORE_set_flags(store, X509_V_FLAG_X509_STRICT);//验证的时候设置一下,即可触发漏洞,绕过ca检查
//这里必须显式设置才行!!!!
X509_STORE_set_flags(store, X509_V_FLAG_X509_STRICT);//验证的时候设置一下,即可触发漏洞,绕过ca检查
//根证书-->签发中间证书-->签发待验证证书
//这里构造一条证书链
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;
}
//根证书-->签发中间证书-->签发待验证证书
//这里构造一条证书链
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()
{

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-5-21 14:47 被陌殇编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//