2020年10月,我们收到了一份针对ISCBIND服务器的匿名安全报告。这份报告中发现的安全问题其实是基于此前曝光的漏洞CVE-2006-5989,影响Apache模块mod_auth_kerb,最初也是由匿名研究人员发现的。ISCBIND服务器SPNEGO(简单且受保护的GSSAPI协商机制,SPNEGO)组件共享了包含该漏洞的代码,但ISC当时并未打上相应的安全补丁。十五年后,ISC修补了BIND中的这个漏洞,并为其分配了相应的漏洞编号,CVE-2020-8625。对于BIND服务器,版本9.11到9.16受此漏洞影响。此外,攻击者无需身份验证即可远程触发漏洞,从而导致4字节堆溢出。此安全报告的内容符合TargetingIncentiveProgram的要求,但缺少获得全部赏金所需的完整漏洞利用程序。不过,这仍然是一份优秀的安全报告,这个漏洞值得我们深入研究。漏洞分析造成该漏洞的原因是位于lib/dns/spnego.c中的der_get_oid()函数存在堆溢出漏洞。staticintder_get_oid(constunsignedchar*p,size_tlen,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){unsignedu=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。所以loop(4)可以使缓冲区溢出1个元素。data->components的类型是int,所以这会造成4字节的堆溢出。触发机制由于该漏洞存在于SPNEGO组件中,因此必须在BIND中相应配置TKEY-GSSAPI。#cat/etc/bind/named.conf.optionsoptions{目录"/var/cache/bind";tkey-gssapi-keytab"/etc/bind/dns.keytab";};#cat/etc/bind/named。conf.localzone"example.nil."IN{typemaster;file"/etc/bind/example.nil.db";};其中,dns.keytab文件位于bin/tests/system/tsiggss/ns1/,而example.nil.db文件是由脚本bin/tests/system/tsiggss/setup.sh生成的。至此,相应的测试环境已经准备就绪。当收到手动请求时会触发漏洞,导致以下调用堆栈:#0der_get_oidatspnego.c:841#1decode_oidatspnego.c:1054#2decode_MechTypeatspnego_asn1.c:213#3decode_MechTypeListatspnego_asn1.c:290#4decode_NegTokenInitatspnego_asn1.c:290#4decode_NegTokenInitatspnego_asn1.c:523#decode_oidatspnego_asn1.c:523#decode_Oidatspnego_asn1.c:523#decode_Oidatspnego.c:1054#2decode_MechTypeatspnego_asn1.c:290#4decode_NegTokenInitatspnego_asn1.c:523#decode_oidatspnego.c:1054#2decode_MechTypeatspnego_asn1.c:291.c:591#6dst_gssapi_acceptctxatgssapictx.c:729#7process_gsstkeyattkey.c:551#8dns_tkey_processqueryattkey.c:882#9ns_query_startatquery.c:11315#10ns__client_requestatclient.c:2161#11processbufferattcpdns.c:227#12dnslisten_readcbattcpdns.c:294#13read_cbattcp.c:814...Exploitation该漏洞的可利用性高度依赖于glibc的版本,下面的解释是基于支持tcache的Ubuntu18.04和glibc2.27。首先,我们需要确定这个溢出漏洞控制的是什么:der_get_oid()中分配的漏洞缓冲区的大小和内容是可控的。顺便说一句,当当前请求完成时,这个缓冲区将被释放。decode_MechTypeList()中有一个while循环,用于重复执行der_get_oid()函数,循环次数也是可控的。有了这两点,我们就可以很容易地操作堆了。为了准备堆,我们可以清空任意大小的tcachebin,并在请求完成后重新填充它们。同时,重新填充的块在内存中可以是连续的。这使得内存布局非常有利于通过缓冲区溢出进行攻击。实现任意写原语在这个阶段,可以通过滥用tcache空闲列表轻松实现任意写原语。触发4字节溢出以扩展下一个空闲块大小。在下一个请求中,内存空间被分配到损坏的块中。当请求结束时,它将被移动到新的tcachebin中。使用新大小再次分配损坏的块。这时损坏的chunk会和下一个freechunk重叠,然后用任意值覆盖它的freelist。从“中毒”的tcache空闲列表分配内存空间。它将返回一个任意地址。泄漏内存地址默认情况下为BIND启用所有Linux缓解措施。所以我们首先需要修复ASLR,这意味着我们需要找到一种从内存中泄漏地址的方法。实现内存泄漏的一种可能机会是使用code_NegTokenArg()函数。此函数用于将响应消息编码到缓冲区中并将其发送给客户端。staticOM_uint32code_NegTokenArg(OM_uint32*minor_status,constNegTokenResp*resp,unsignedchar**outbuf,size_t*outbuf_size){//...buf_size=1024;buf=malloc(buf_size);//<--(5)//...做{ret=encode_NegTokenResp(buf+buf_size-1,buf_size,resp,&buf_len);//...}while(ret==ASN1_OVERFLOW);*outbuf=malloc(buf_len);//<--(6)if(*outbuf==NULL){*minor_status=ENOMEM;free(buf);return(GSS_S_FAILURE);}memmove(*outbuf,buf+buf_size-buf_len,buf_len);*outbuf_size=buf_len;free(buf);//<--(7)return(GSS_S_COMPLETE);}位于(5)处的buf是一个临时缓冲区,其初始大小为1024字节,正好在tcache处理的范围内。而(6)处的outbuf就是要发送给client的buffer,它的大小也在tcache的范围内。如果可以对这两个缓冲区的大小执行tcachedup攻击,那么(5)和(6)处的两个malloc()调用将返回相同的地址。执行完(7)处的free()函数后,会更新一个tcache->next指针到buf,但此时已经和outbuf重叠了。这意味着堆指针将泄漏给客户端。理想情况下,应将(6)处的buf_len选择得足够大,以避免干扰较小的tcache容器。不幸的是,最大值似乎只有96个字节。由于这个问题,该进程根本无法生存并在客户端获得泄漏的堆指针后不久崩溃。因此,我们需要更深入地挖掘才能找到充分利用此漏洞的方法。漏洞修复在BIND9.16.12和BIND9.11.28中,该漏洞已被修复。为了修复BIND9.16,ISC完全放弃了SPNEGO的使用。在BIND9.11中,他们应用了针对原始问题的补丁。总结这个安全缺陷表明,即使软件是开源的并被广泛使用,漏洞也可能存在多年而不被发现。软件维护人员需要密切监控他们使用的所有外部模块,以确保应用最新的安全补丁。同时,该漏洞也表明这是一个非常艰巨的挑战。ISCBIND是互联网上最流行的DNS服务器,因此漏洞的影响范围相当大,尤其是无需认证即可远程触发的漏洞。我们建议您尽快更新相应的DNS服务器。本文翻译自:https://www.thezdi.com/blog/2021/2/24/cve-2020-8625-a-fifteen-year-old-rce-bug-returns-in-isc-bind-服务器
