深入理解在ISC BIND服务器中潜藏了15年的RCE漏洞
该漏洞的成因,是位于lib/dns/spnego.c中的函数der_get_oid()存在堆溢出漏洞。 static int der_get_oid(const unsigned char *p, size_t len, oid *data, size_t *size) { // ... data->components = malloc(len * sizeof(*data->components)); // components == NULL) { return (ENOMEM); } data->components[0] = (*p) / 40; // components[1] = (*p) % 40; --len; // 0U; ++n) { unsigned u = 0;
do { --len; uu = u * 128 + (*p++ % 128); } while (len > 0U && p[-1] & 0x80); data->components[n] = u; // <-- (4) } // ... } 这个函数在(1)处分配一个数组缓冲区。变量len用于跟踪缓冲区中剩余的元素数量。同时,代码在(2)处对前2个元素进行了填充处理,但是,它在(3)处只将len减去了1。因此,循环(4)可以使缓冲区溢出1个元素。data->components的类型是int,所以,这将导致4字节的堆溢出。 触发机制由于该漏洞存在于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生成的。 现在,相应的测试环境已经准备好了。当接收到一个手工请求时,该漏洞就会被触发,并产生以下调用栈: #0 der_get_oid at spnego.c:841 #1 decode_oid at spnego.c:1054 #2 decode_MechType at spnego_asn1.c:213 #3 decode_MechTypeList at spnego_asn1.c:290 #4 decode_NegTokenInit at spnego_asn1.c:523 #5 gss_accept_sec_context_spnego at spnego.c:591 #6 dst_gssapi_acceptctx at gssapictx.c:729 #7 process_gsstkey at tkey.c:551 #8 dns_tkey_processquery at tkey.c:882 #9 ns_query_start at query.c:11315 #10 ns__client_request at client.c:2161 #11 processbuffer at tcpdns.c:227 #12 dnslisten_readcb at tcpdns.c:294 #13 read_cb at tcp.c:814 ... 漏洞利用这个漏洞的可利用性高度依赖于glibc的版本,而下面的解释是基于Ubuntu18.04和glibc2.27的,后者支持tcache。 首先,我们要确定这个溢出漏洞所能控制的内容: 在der_get_oid()中分配的易受攻击的缓冲区的大小和内容是可控的。顺便说一下,当当前请求完成后,该缓冲区将被释放。 (编辑:ASP站长网) |