设为首页 - 加入收藏 - 网站地图 SecYe安全 Www.SecYe.Com - 国内网络信息安全IT技术门户网
当前位置:SecYe > 网络安全 > 系统和服务器安全 > 正文

OpenSSL-CVE-2015-1793漏洞分析

时间:2015-07-15 19:40 来源:未知 作者:www.secye.com 阅读:

OpenSSL官方在7月9日发布了编号为 CVE-2015-1793 的交叉证书验证绕过漏洞,其中主要影响了OpenSSL的1.0.1和1.0.2分支。1.0.0和0.9.8分支不受影响。

360安全研究员au2o3t对该漏洞进行了原理上的分析,确认是一个绕过交叉链类型证书验证的高危漏洞,可以让攻击者构造证书来绕过交叉验证,用来形成诸如“中间人”等形式的攻击。

0x01 漏洞基本原理


直接看最简单的利用方法(利用方法包括但不限于此):

攻击者从一公共可信的 CA (C)处签得一证书 X,并以此证书签发另一证书 V(含对X的交叉引用),那么攻击者发出的证书链 V, R (R为任意证书)对信任 C 的用户将是可信的。

显然用户对 V, R 链的验证会返回失败。

对不支持交叉链认证的老版本来说,验证过程将以失败结束。

对支持交叉认证的版本,则将会尝试构建交叉链 V, X, C,并继续进行验证。

虽然 V, X, C 链能通过可信认证,但会因 X 的用法不包括 CA 而导致验证失败。

但在 openssl-1.0.2c 版本,因在对交叉链的处理中,对最后一个不可信证书位置计数的错误,导致本应对 V, X 记为不可信并验证,错记为了仅对 V 做验证,而没有验证攻击者的证书 X,返回验证成功。

0x02 具体漏洞分析


漏洞代码位于文件:openssl-1.0.2c/crypto/x509/x509_vfy.c

函数:X509_verify_cert()

第 392 行:ctx->last_untrusted–;

对问题函数 X509_verify_cert 的简单分析:

( 为方便阅读,仅保留与证书验证强相关的代码,去掉了诸如变量定义、错误处理、资源释放等非主要代码)

问题在于由 <1> 处加入颁发者时及 <2> 处验证(颁发者)后,证书链计数增加,但 最后一个不可信证书位置计数 并未增加, 而在 <4> 处去除过程中 最后一个不可信证书位置计数 额外减少了,导致后面验证过程中少验。

(上述 V, X, C 链中应验 V, X 但少验了 X

代码分析如下

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
int X509_verify_cert(X509_STORE_CTX *ctx)
{
    // 将 ctx->cert 做为不信任证书压入需验证链  ctx->chain
    // STACK_OF(X509) *chain 将被构造为证书链,并最终送到 internal_verify() 中去验证
    sk_X509_push(ctx->chain,ctx->cert);
    // 当前链长度(==1)
    num = sk_X509_num(ctx->chain);
     // 取出第 num 个证书
    x = sk_X509_value(ctx->chain, num - 1);
     // 存在不信任链则复制之
    if (ctx->untrusted != NULL
        && (sktmp = sk_X509_dup(ctx->untrusted)) == NULL) {
        X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
         goto end;
    }
     // 预设定的最大链深度(100)
    depth = param->depth;
    // 构造需验证证书链
    for (;;) {
        // 超长退出
        if (depth < num)
            break;
        // 遇自签退出(链顶)
        if (cert_self_signed(x))
            break;
         if (ctx->untrusted != NULL) {
            xtmp = find_issuer(ctx, sktmp, x);
            // 当前证书为不信任颁发者(应需CA标志)颁发
            if (xtmp != NULL) {
                // 则加入需验证链
                if (!sk_X509_push(ctx->chain, xtmp)) {
                    X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
                    goto end;
                }
                CRYPTO_add(&xtmp->references, 1, CRYPTO_LOCK_X509);
                (void)sk_X509_delete_ptr(sktmp, xtmp);
                // 最后一个不可信证书位置计数 自增1
                ctx->last_untrusted++;
                x = xtmp;
                num++;
                continue;
            }
        }
        break;
    }
    do {
        i = sk_X509_num(ctx->chain);
        x = sk_X509_value(ctx->chain, i - 1);
        // 若最顶证书是自签的
        if (cert_self_signed(x)) {
            // 若需验证链长度 == 1
            if (sk_X509_num(ctx->chain) == 1) {
                // 在可信链中查找其颁发者(找自己)
                ok = ctx->get_issuer(&xtmp, ctx, x);
 
               // 没找到或不是相同证书
                if ((ok <= 0) || X509_cmp(x, xtmp)) {
                    ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
                    ctx->current_cert = x;
                    ctx->error_depth = i - 1;
                    if (ok == 1)
                        X509_free(xtmp);
                    bad_chain = 1;
                    ok = cb(0, ctx);
                    if (!ok)
                        goto end;
                // 找到
                } else {
                    X509_free(x);
                    x = xtmp;
                    // 入到可信链
                    (void)sk_X509_set(ctx->chain, i - 1, x);
                    // 最后一个不可信证书位置计数 置0
                    ctx->last_untrusted = 0;
                }
            // 最顶为自签证书 且 证书链长度>1
            } else {
                // 弹出
                chain_ss = sk_X509_pop(ctx->chain);
                // 最后一个不可信证书位置计数 自减
                ctx->last_untrusted--;
                num--;
                j--;
                // 保持指向当前最顶证书
                x = sk_X509_value(ctx->chain, num - 1);
            }
        }
        // <1>
        // 继续构造证书链(加入颁发者)
        for (;;) {
            // 自签退出
            if (cert_self_signed(x))
                break;
            // 在可信链中查找其颁发者
            ok = ctx->get_issuer(&xtmp, ctx, x);
            // 出错
            if (ok < 0)
                return ok;
            // 没找到
            if (ok == 0)
                 break;
            x = xtmp;
            // 将不可信证书的颁发者(证书)加入需验证证书链
            if (!sk_X509_push(ctx->chain, x)) {
                X509_free(xtmp);
                X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
                return 0;
            }
            num++;
        }
        // <2>
        // 验证 for(;;) 中加入的颁发者链
        i = check_trust(ctx);
        if (i == X509_TRUST_REJECTED)
            goto end;
        retry = 0;
         // <3>
        // 检查交叉链
        if (i != X509_TRUST_TRUSTED
            && !(ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST)
            && !(ctx->param->flags & X509_V_FLAG_NO_ALT_CHAINS)) {
            while (j-- > 1) {
                xtmp2 = sk_X509_value(ctx->chain, j - 1);
                 // 其实得到一个“看似合理”的证书就返回,这里实际上仅仅根据 CN域 查找颁发者
                ok = ctx->get_issuer(&xtmp, ctx, xtmp2);
                if (ok < 0)
                    goto end;
                // 存在交叉链
                if (ok > 0) {
                    X509_free(xtmp);
 
                    // 去除交叉链以上部分
                    while (num > j) {
                        xtmp = sk_X509_pop(ctx->chain);
                        X509_free(xtmp);
                        num--;
                        // <4>
                        // 问题所在
                        ctx->last_untrusted--;
                    }
                    // <5>
                    retry = 1;
                    break;
                }
            }
        }
    } while (retry);
    ……
}

本文来源:SecYe安全网[http://www.secye.com] (责任编辑:SecYe安全)

点击复制链接 与好友分享!

顶一下
(0)
0%
踩一下
(0)
0%