深入分析在ISC BIND服务器中潜藏了15年的RCE漏洞

2020年10月,我们收到了一份针对ISC BIND服务器的匿名安全报告。在这份报告中发现的安全问题,实际上基于之前曝出的漏洞CVE-2006-5989,该漏洞影响Apache模块mod_auth_kerb,并且,它最初也是由匿名研究人员发现的。ISC BIND服务器SPNEGO(the Simple and Protected GSSAPI Negotiation Mechanism,SPNEGO)组件内共享了含有该漏洞的代码,但ISC当时并没有合并相应的安全补丁。15年后,ISC对BIND中的这个漏洞进行了修复,并为其分配了相应的漏洞编号,即CVE-2020-8625。

对于BIND服务器来说,从9.11到9.16的版本都受到该漏洞的影响。并且,攻击者可以在无需身份验证的情况下远程触发该漏洞,进而导致一个4字节的堆溢出。这份安全报告的内容符合Targeting Incentive Program的要求,但缺乏获得全额奖金所需的完整exploit。不过,这仍然不失为一个优秀的安全报告,而且这个漏洞也值得我们深入进行研究。

漏洞分析

该漏洞的成因,是位于lib/dns/spnego.c中的函数der_get_oid()存在堆溢出漏洞。

  1. static int 
  2. der_get_oid(const unsigned char *p, size_t len, oid *data, size_t *size) { 
  3. // ... 
  4. data->components = malloc(len * sizeof(*data->components));   // components == NULL) { 
  5.       return (ENOMEM); 
  6.     } 
  7.     data->components[0] = (*p) / 40;    // components[1] = (*p) % 40; 
  8.     --len;               //  0U; ++n) { 
  9.         unsigned u = 0
  10.   
  11.         do { 
  12.             --len; 
  13.             uu = u * 128 + (*p++ % 128); 
  14.         } while (len > 0U && p[-1] & 0x80); 
  15.         data->components[n] = u;      // <-- (4) 
  16.     } 
  17. // ... 

这个函数在(1)处分配一个数组缓冲区。变量len用于跟踪缓冲区中剩余的元素数量。同时,代码在(2)处对前2个元素进行了填充处理,但是,它在(3)处只将len减去了1。因此,循环(4)可以使缓冲区溢出1个元素。data->components的类型是int,所以,这将导致4字节的堆溢出。

触发机制

由于该漏洞存在于SPNEGO组件中,因此,必须在BIND中对TKEY-GSSAPI进行相应的配置。

  1. # cat /etc/bind/named.conf.options 
  2. options { 
  3.     directory "/var/cache/bind"; 
  4.     tkey-gssapi-keytab "/etc/bind/dns.keytab"; 
  5. }; 
  6.   
  7. # cat /etc/bind/named.conf.local 
  8. zone "example.nil." IN { 
  9.     type master; 
  10.     file "/etc/bind/example.nil.db"; 
  11. }; 

其中,dns.keytab文件位于bin/tests/system/tsiggss/ns1/中,而example.nil.db文件则是由脚本bin/tests/system/tsiggss/setup.sh生成的。

现在,相应的测试环境已经准备好了。当接收到一个手工请求时,该漏洞就会被触发,并产生以下调用栈:

  1. #0  der_get_oid at spnego.c:841 
  2. #1  decode_oid at spnego.c:1054 
  3. #2  decode_MechType at spnego_asn1.c:213 
  4. #3  decode_MechTypeList at spnego_asn1.c:290 
  5. #4  decode_NegTokenInit at spnego_asn1.c:523 
  6. #5  gss_accept_sec_context_spnego at spnego.c:591 
  7. #6  dst_gssapi_acceptctx at gssapictx.c:729 
  8. #7  process_gsstkey at tkey.c:551 
  9. #8  dns_tkey_processquery at tkey.c:882 
  10. #9  ns_query_start at query.c:11315 
  11. #10 ns__client_request at client.c:2161 
  12. #11 processbuffer at tcpdns.c:227 
  13. #12 dnslisten_readcb at tcpdns.c:294 
  14. #13 read_cb at tcp.c:814 
  15. ... 

漏洞利用

这个漏洞的可利用性高度依赖于glibc的版本,而下面的解释是基于Ubuntu18.04和glibc2.27的,后者支持tcache。

首先,我们要确定这个溢出漏洞所能控制的内容:

  • 在der_get_oid()中分配的易受攻击的缓冲区的大小和内容是可控的。顺便说一下,当当前请求完成后,该缓冲区将被释放。
  • decode_MechTypeList()中有一个while循环,用于重复执行der_get_oid()函数,并且循环次数也是可控的。

有了这两点,我们就可以轻松地操纵堆了。为了准备堆,我们可以耗尽任意大小的tcache bins,并在请求完成后重新对其进行填充。同时,重新填充的分块(chunk)在内存中可以是连续的。这使得内存布局相当有利于通过缓冲区溢出发动攻击。

实现任意写原语

在这个阶段,通过滥用tcache空闲列表可轻松实现任意写原语。

触发一个4字节的溢出来扩展下一个空闲的chunk大小。

在下一个请求中,在受损的chunk中分配内存空间。当请求结束时,它将被移动到新的tcache bin中。

用新的大小再次分配受损的chunk。这时,受损的chunk将与下一个空闲的chunk发生重叠,然后,用一个任意的值覆盖其freelist。

从“中毒的”tcache freelist上分配内存空间。它将返回一个任意地址。

泄漏内存地址

默认情况下,会为BIND启用所有Linux缓解措施。因此,我们首先要搞定ASLR,这意味着我们需要找到一种从内存中泄漏地址的方法。一个可能实现内存泄漏的机会,是利用code_NegTokenArg()函数。该函数用于将响应消息编码到一个缓冲区中,并将其发送给客户端。

  1. static OM_uint32 
  2. code_NegTokenArg(OM_uint32 *minor_status, const NegTokenResp *resp, 
  3.                  unsigned char **outbuf, size_t *outbuf_size) { 
  4. // ... 
  5.     buf_size = 1024
  6.     buf = malloc(buf_size);    // <-- (5) 
  7. //... 
  8.     do { 
  9.         ret = encode_NegTokenResp(buf + buf_size - 1, buf_size, resp, 
  10.                                   &buf_len); 
  11. // ... 
  12.     } while (ret == ASN1_OVERFLOW); 
  13.   
  14.     *outbuf = malloc(buf_len);    // <-- (6) 
  15.     if (*outbuf == NULL) { 
  16.         *minor_status = ENOMEM
  17.         free(buf); 
  18.         return (GSS_S_FAILURE); 
  19.     } 
  20.     memmove(*outbuf, buf + buf_size - buf_len, buf_len); 
  21.     *outbuf_size = buf_len
  22.   
  23.     free(buf);       // <-- (7) 
  24.   
  25.     return (GSS_S_COMPLETE); 

位于(5)处的buf是一个临时缓冲区,它的初始大小是1024字节,正好在tcache处理的范围内。而(6)处的outbuf是将被发送到客户端的缓冲区,其大小也在tcache的范围内。如果可以对这两个缓冲区的大小进行tcache dup攻击,那么,在(5)和(6)处的两次malloc()调用将返回相同的地址。在执行(7)处的free()函数之后,一个tcache->next指针将被更新到buf中,但是,这时它已经和outbuf重叠在一起了。这意味着堆指针将泄露给客户端。

理想情况下,位于(6)处的buf_len应该选择得足够大,以避免干扰较小的tcache bins。不幸的是,最大值似乎只有96个字节。由于这个问题,进程根本无法存活,并在客户端得到泄漏的堆指针后不久就会崩溃。因此,我们需要进行更深入的研究,以便来找到一种可以充分利用该漏洞的方法。

漏洞的修复

在BIND 9.16.12和BIND 9.11.28中,已经修复了该漏洞。为了修复BIND 9.16,ISC完全放弃了SPNEGO的使用。在BIND 9.11中,他们针对原始问题应用了补丁程序。

小结

这个安全漏洞表明,即使软件是开源的,并且得到了广泛使用,漏洞也会存在多年而难以被发现。软件维护人员需要密切监视他们使用的所有外部模块,以确保应用了最新的安全补丁。同时,该漏洞也表明这是一个非常棘手的挑战。ISC BIND是Internet上最流行的DNS服务器,所以,该漏洞的影响范围相当大,特别是该漏洞可以在远程且无需身份验证的情况下触发。我们建议大家尽快更新相应的DNS服务器。

本文翻译自:https://www.thezdi.com/blog/2021/2/24/cve-2020-8625-a-fifteen-year-old-rce-bug-returns-in-isc-bind-server

 

 

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/127266.html<

(0)
管理的头像管理
上一篇2025-02-24 14:33
下一篇 2025-02-24 14:34

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注