漏洞细节
该漏洞的根本原因位于der_match_tag_and_length()函数。该函数被用于标签匹配,并从网络数据包中获取以下长度字段。基于BIND中der_get_length()的正常使用,解析的长度字段length_ret应该由调用者验证。但是, der_match_tag_and_length()却是一个例外。
static int
der_match_tag_and_length(const unsigned char *p, size_t len, Der_class xclass,
Der_type type, int tag, size_t *length_ret,
size_t *size) {
size_t l, ret = 0;
int e;
e = der_match_tag(p, len, xclass, type, tag, &l);
if (e) {
return (e);
}
p += l;
len -= l;
ret += l;
e = der_get_length(p, len, length_ret, &l); // (1)
if (e) {
return (e);
}
/* p += l; */
len -= l;
POST(len);
ret += l;
if (size) {
*size = ret;
}
return (0);
}
OM_uint32
gss_accept_sec_context_spnego(...)
{
// ...
ret = der_match_tag_and_length(buf, buf_size, ASN1_C_CONTEXT, CONS, 0,
&len, &taglen);
if (ret) {
return (ret);
}
ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len); // (2)
// ...
}
然后,使用不受信任的len解码标注(2)处的negTokenInit。decode_NegTokenInit()中的许多检查都基于len。现在,这些检查是不正确的,这可能会导致对不同子字段的越界访问,例如mechTypes、reqFlags、mechToken等。
触发机制
由于该漏洞存在于SPNEGO组件中,因此,必须在BIND中对TKEY-GSSAPI进行相应的配置。
# cat /etc/bind/named.conf.options
options {
directory "/var/cache/bind";
tkey-gssapi-keytab "/etc/bind/dns.keytab";
};
# cat /etc/bind/named.conf.local
zone "example.nil." IN {
type master;
file "/etc/bind/example.nil.db";
};
其中,dns.keytab文件可以在bin/tests/system/tsiggss/ns1/中找到,而example.nil.db文件则是bin/tests/system/tsiggss/setup.sh脚本生成。
现在,相应的测试环境已经准备好了,可以通过构造以下SPNEGO请求,远程触发漏洞。
精心制作的SPNEGO请求
标注(1)处的length_ret由偏移量0xa5控制为 0x91929394。子字段mechToken是一个八位字节串。它的长度是被精心设计为0x727374,出现在偏移量0xcd处。
当收到此精心设计的请求后,会在处理mechToken字段时触发越界读取,并产生以下调用栈。
#0 __memmove_avx_unaligned_erms at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:494
#1 der_get_octet_string at spnego.c:830
#2 decode_octet_string at spnego.c:1015
#3 decode_NegTokenInit at spnego_asn1.c:607
#4 gss_accept_sec_context_spnego at spnego.c:593
#5 dst_gssapi_acceptctx at gssapictx.c:730
#6 process_gsstkey at tkey.c:551
#7 dns_tkey_processquery at tkey.c:882
#8 ns_query_start at query.c:11653
#9 ns__client_request at client.c:2169
#10 isc__nm_async_readcb at netmgr.c:1861
#11 isc__nm_readcb at netmgr.c:1836
#12 processbuffer at tcpdns.c:997
#13 process_sock_buffer at tcpdns.c:1639
#14 read_cb at tcpdns.c:1060
漏洞利用
以下是可能在受影响的服务器上造成信息泄漏的一种方法。
OM_uint32
gss_accept_sec_context_spnego(...)
{
// ...
ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len);
if (ret) {
*minor_status = EINVAL; /* XXX */
return (GSS_S_DEFECTIVE_TOKEN);
}
for (i = 0; !found && i < init_token.mechTypes.len; ++i) { // (3)
unsigned char mechbuf[17];
size_t mech_len;
ret = der_put_oid(mechbuf + sizeof(mechbuf) - 1,
sizeof(mechbuf), &init_token.mechTypes.val[i],
&mech_len);
if (ret) {
free_NegTokenInit(&init_token);
return (GSS_S_DEFECTIVE_TOKEN);
}
if (mech_len == GSS_KRB5_MECH->length &&
isc_safe_memequal(GSS_KRB5_MECH->elements,
mechbuf + sizeof(mechbuf) - mech_len,
mech_len))
{
found = 1;
break;
}
if (mech_len == GSS_MSKRB5_MECH->length &&
isc_safe_memequal(GSS_MSKRB5_MECH->elements,
mechbuf + sizeof(mechbuf) - mech_len,
mech_len))
{
found = 1;
if (i == 0) {
pref = GSS_MSKRB5_MECH;
}
break;
}
}
if (!found) {
free_NegTokenInit(&init_token);
return (send_reject(minor_status, output_token)); // (5)
}
// ...
ret = send_accept(&minor_status2, output_token, ot, pref); // (4)
// ...
}
在decode_NegTokenInit()解析了negTokenInit及其子字段后,标注(3)处的循环会在解析的mechType中搜索有效的OID。如果找到有效的OID,服务器将在标注(4)处响应接收消息。否则,服务器在标注(5)处响应拒绝接收。这使我们能够获取某些堆块的偏移量。
总结
ISC BIND是互联网上最流行的DNS服务器,所以该漏洞的影响范围相当大,特别是该漏洞可以在远程且无需身份验证的情况下触发。因此建议受影响的用户尽快更新相应的DNS服务器。
END
本文始发于微信公众号(SecTr安全团队):深入分析ISC BIND服务器中的信息泄露漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论