步骤详解Exim base64解码函数漏洞,其波及至少400,000台服务器

大家好,我是小编

明天就是3月8号女王节了

在这里祝我的女朋友节日快乐

毕竟她不淘气、不粘人、不存在

前言

研究人员在2月5日报告了Exim base64解码函数中的溢出漏洞,该漏洞被标识为CVE-2018-6789。

利用它可以获得预授权远程代码执行,目前至少有400,000台服务器处于风险之中。

受影响设备:

Exim所有低于4.90.1的版本

漏洞脆弱性分析

先来看一下在b64decode函数中,解码缓冲区长度的计算错误:

base64.c来源地址——

如上所示,Exim分配了一个3*(len/4)+1字节缓冲区来存储解码后的base64数据。但是,如果输入无效的base64字符串且长度为4n+3,Exim则会在解码时分配3n+1字节但占用3n+2字节。这就会导致一个字节堆溢出。

通常情况下,因为内存覆盖未被使用,这个错误是没有攻击性的。但是,当字符串符合某些特定长度时,该字节就会覆盖一些关键数据。此外,这个字节是可控的,这使得开发操作更加方便。

Base64解码作为一个基本功能,该漏洞很容易导致远程代码执行。

开发

漏洞攻击

为了评估这个漏洞的严重性,研究人员开发了一个针对Exim的SMTP守护进程的攻击。

实现预授权远程代码执行的开发机制:

Debian(stretch)和Ubuntu(zesty)

使用apt-get安装Exim4软件包的SMTP守护进程

启用配置CRAM-MD5身份验证器(或使用base64的其他身份验证程序)

基本的SMTP命令(EHLO,MAIL FROM/RCPT TO)和AUTH

内存分配

首先,看一下源代码并搜索有用的内存分配。Exim使用自定义函数进行动态分配:

函数store_free()和store_malloc()直接调用glibc中的malloc()和free()。对于glibc,我们使用的是0x10字节大小的块,并将其元数据存储在每个函数的第一个字节(x86-64)中,然后返回其数据位置。块的结构如下图所示:

元数据包括前一个块的大小、当前块的大小和一些其他标志,元数据的前三位用于存储标志。例如,0x81隐含当前块的大小是0x80字节,且前一个块正在被使用中。

在Exim中使用的已发布的块,被放入一个双向链表中,称为未排序的bin。glibc根据标志来维护它,并将相邻的已发布块合并到一个更大的块中以避免碎片化。

对于每个分配请求,glibc都会以FIFO(先进先出)顺序检查这些块,并将这些块重新使用。

对于一些性能问题,Exim通过store_get()、store_release()、store_extend()和store_reset()维护自己的链表结构。

存储块的主要特点是每个块至少有0x2000字节,这也就限制了我们的使用。请注意,存储块也属于块数据,我们可以这样查看记录:

这里我们列出用来排列堆数据的函数:

1.EHLO主机名。

对于每个EHLO(或HELO)命令,Exim将主机名的指针存储在sender_host_name中。主机名分别是:

旧名字:store_free()

新名字:store_malloc()

smtp_in.c来源地址——

2.无法识别的命令。

对于每个无法识别的、具有不可打印字符的命令,Exim会分配一个缓冲区以将其转换为可打印字符。缓冲区为:

store_get(),在其中存储错误消息

smtp_in.c来源地址——

3. AUTH 。

在大多数身份验证过程中,Exim使用base64编码与客户端进行通信。编码和解码字符串存储在缓冲区store_get()中,store_get()的特点包括:

用于存储字符串

可以包含不可打印的字符和null字节

不一定由null终止

4.在EHLO/HELO,MAIL,RCPT中复位。

当命令正确完成时,调用store_reset()将块链重置,所有由store_get()命令分配的存储块被释放。需要注意的是:

store_reset()重置点需在函数的开始处设置

存储块会一次性被释放

smtp_in.c来源地址——

利用步骤

解码后的base64数据的块比较易于释放和控制。经过多次尝试,发现地址sender_host_name是更好的选择。安排堆布局使得sender_host_name为base64数据留下一个空闲的块。

1.把一个大的块放入未排序的bin中。

首先,发送一个带有大的主机名(huge hostname)的EHLO消息,以使其分配和释放,在未排序的bin留下一个块,长度为0x6060(3个存储块长)。

2.切下第一个存储块 。

然后发送一个无法识别的字符串来触发store_get(),并在释放的chunk中分配存储块。

3.切下第二个存储块并释放第一个存储块。

再次发送EHLO消息以获得第二个存储块。由于EHLO完成后调用了smtp_reset,所以第一个块被释放。

堆布局准备好后,使用off-by-one覆盖原始块大小。 将0x2021修改为0x20f1,这样就可以扩展块的大小。

4.发送base64数据并逐个触发。

启动一个AUTH命令来发送base64数据。 溢出字节正好覆盖下一个块的第一个字节并扩展下一个块。

5.形成合理的块大小。

由于块已经扩展,下一个块的开始被更改到原始块内部。 因此,需要让它伪装成正常的块来通过glibc的检查。 在这里发送另一个base64字符串,因为它需要空字节和不可打印字符来伪造块大小。

6.释放扩展块。

要控制扩展块的内容,首先要释放块,因为块无法直接被编辑。 也就是说,我们应该发送一个新的EHLO消息来释放旧的主机名。 但是,正常的EHLO消息在成功之后会调用smtp_reset,这可能会导致程序中止或崩溃。 为了避免这种情况,我们发送一个无效的主机名称,如a+。

7.覆盖重叠的存储区块的下一个指针。

块发布后,我们可以使用AUTH进行检索并覆盖部分重叠的存储块。 这里我们使用一种部分写入的技巧。

有了这个技巧,我们可以在不破坏ASLR(地址空间布局随机化)的情况下修改指针。 我们改变了部分包含ACL字符串存储块的下一个指针。 ACL字符串通过一组全局指针来指向,例如:

这些指针在exim进程开始时初始化,根据配置进行设置。 例如,如果configure中有一行代码:acl_smtp_mail = acl_check_mail,则指针acl_smtp_mail指向字符串acl_check_mail。

无论何时使用MAIL FROM,Exim执行ACL检查,都需要首先扩展acl_check_mail。 在扩展时,Exim在遇到$ }时会尝试执行命令,所以只要我们控制ACL字符串,就可以实现代码执行。

另外,我们不需要直接劫持程序控制流程,就可以轻松地绕过诸如PIE、NX等缓解措施。

8.重置存储区块并检索ACL存储区块。

现在,ACL存储块位于链接列表中。 一旦smtp_reset()被触发,它将被释放。我们可以通过分配多个块来再次检索它。

9.覆盖ACL字符串并触发ACL检查。

最后,我们覆盖包含ACL字符串的整个块。 发送诸如EHLO,MAIL,RCPT等命令来触发ACL检查。 一旦我们触及配置中定义的acl,就可以实现远程代码执行。

预防措施

将设备升级到4.90.1或以上版本。(这可能是一句废话)

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180307A1KGEA00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励