邮件测试工具

请勿用于非法用途,仅供测试使用

  • 开发此软件初衷:
    原本一直使用:swaks 工具进行邮件发送测试, 忽然有一天想发送带中文标题的发现乱码(这能忍?),想尝试修改swaks,看了swaks 源码之后放弃了,非常难读懂,所以就写了该工具

  • 题外话: 网上只要涉及钓鱼邮件的文章都推荐用swaks,不是说这工具不好,swaks功能非常强大,但用来作邮件欺骗有些杀鸡用牛刀了,smtp发送大多数情况下并不需要发送方提供smtp服务器,SPF只是为了保护自己域名的,网上很多文章没有搞懂. 转发邮件伪装效果并不好,后面出一篇如何伪造相关的文章

  • 网上那么多类似的工具为什么要自己造轮子:
    自己造轮子的好处是真正了解功能如何实现,出现bug或有其它需求知道怎么快速改

待实现功能

  • CC 邮件抄送
  • BCC 密件抄送
  • 定义回复邮件者
  • web编辑邮件内容功能
  • tls 支持

推荐文章

2020-12-14-04-15-18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# -*- coding: utf-8 -*-
import argparse
from smtplib import SMTP
from base64 import encodebytes
from email.utils import make_msgid, formatdate
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
import random
import dns.resolver
from email.header import Header
import base64
import os
from io import open
from os.path import basename

def isBase64(content):
try:
return base64.b64encode(base64.b64decode(str)) == content
except Exception:
return False


def isFile(content):
try:
return os.path.isfile(content)
except Exception:
return False


def ColorPrint(string="", flag="", verbose=""):
colors = {
u"gray": "2",
u"red": "31",
u"green": "32",
u"yellow": "33",
u"blue": "34",
u"pink": "35",
u"cyan": "36",
u"white": "37",
}
flags = {
u"error": "[-] ",
u"warning": "[!] ",
u"info": "[*] ",
u"success": "[+] ",
u"debug": ">>> ",
u"echo": ">>> "
}
try:
if flag == 'error':
print(u"\033[%sm%s%s\033[0m" % (colors[u"red"], flags[flag], string))
if flag == "info":
print(u"\033[%sm%s%s\033[0m" % (colors[u"white"], flags[flag], string))
if flag == 'echo' or flag == '' or flag == 0:
print(u"\033[%sm%s%s\033[0m" % (colors[u"white"], flags[flag], string))
if flag == 'success':
print(u"\033[%sm%s%s\033[0m" % (colors[u"green"], flags[flag], string))
if verbose == 1:
if flag == 'warning':
print(u"\033[%sm%s%s\033[0m" % (colors[u"yellow"], flags[flag], string))
if flag == 'debug':
print("%s", string)
except:
return 0


def DNSQuery(mailaddr):
verbose = 1
dns_resolver = dns.resolver.Resolver(configure=False)
dns_resolver.timeout = 5
dns_resolver.lifetime = 5
dns_resolver.nameservers = ['119.29.29.29']
record = "MX"
domain = mailaddr[mailaddr.find(u"@") + 1:]
ColorPrint("query MX of DNS for %s" % domain, flag="echo", verbose=verbose)
try:
MX = dns_resolver.resolve(domain, record)
m = random.randint(0, len(MX))
mx = MX[0].exchange
strMx = str(mx)[:-1]
assert strMx != u""
except Exception as e:
ColorPrint("query MX of %s failed: %s" % (strMx, e), flag="error", verbose=verbose)
return 0
ColorPrint("MX Server: %s" % strMx, flag="success", verbose=verbose)
return strMx


def send_mail(server=None, port=None, mail_from=None, to=None, h_from=None, subject=None, content=None, html=None,
auth=None, user=None, password=None, tls=None, debuglevel=2, attachment=None):

# Compose mail headers
msg = MIMEMultipart('alternative')
msg['Subject'] = Header(subject, 'utf-8')
msg['X-Mailer'] = "MSOffice365"
msg['To'] = to
msg['Message-id'] = make_msgid() # Message-ID标头
msg['Date'] = formatdate() # 日期
if h_from:
msg['From'] = Header(h_from, 'utf-8')
else:
msg['From'] = Header(mail_from, 'utf-8')
# Compose mail body
if html and content:
ColorPrint("The \"--html\" and \"--content\" args do not go together!", "error", verbose=1)
return 0
if html:
if isFile(html):
with open(html, "rb") as content_str:
new_content_str = content_str.read().decode("UTF-8")
if isBase64(new_content_str):
base64txt = base64.b64decode(new_content_str.strip())
html_part = MIMEText(base64txt.decode(), 'html', "utf-8")
else:
html_part = MIMEText(new_content_str, 'html', "utf-8")
msg.attach(html_part)
else:
ColorPrint("Example: \"--html\" ./msg.html !", "error", verbose=1)
return 0

if content:
if isFile(content):
with open(content, "rb") as content_str:
new_content_str = content_str.read().decode("UTF-8")
if isBase64(new_content_str):
base64txt = base64.b64decode(new_content_str.strip())
text_part = MIMEText(base64txt.decode(), 'plain', "utf-8")
else:
text_part = MIMEText(new_content_str, 'plain', "utf-8")
msg.attach(text_part)
else:
#print(content)
text_part = MIMEText(content, 'plain', "utf-8")
msg.attach(text_part)

if attachment:
with open(attachment, 'rb') as f:
attachfile = MIMEBase('application', "octet-stream")
attachfile.set_payload(encodebytes(f.read()).decode())
f.close()
attachfile.add_header('Content-Transfer-Encoding', 'base64')
attachfile.add_header('Content-Disposition', 'attachment', filename=attachment)
msg.attach(attachfile)


smtp = SMTP()
smtp.set_debuglevel(debuglevel)
if not server:
server = DNSQuery(to)
ehlo = domain = to[to.find(u"@") + 1:]
try:
smtp.connect(server, port)
smtp.ehlo(ehlo)
if auth:
smtp.login(user, password)
smtp.sendmail(mail_from, to, msg.as_string())
ColorPrint("send success!", flag="success", verbose=1)
except SMTP.SMTPException as e:
ColorPrint(e, flag="error", verbose=1)
smtp.quit()
smtp.close()
finally:
smtp.quit()
smtp.close()


parser = argparse.ArgumentParser(description="mailsender version 1.0 by wolvez", epilog='Example :\nmailsender.py --auth 1 --server smtp.mailgun.org --user admin@wolvez.club --password password@00001 -to ffffff00000@gmail.com --subject "测试邮件" --mail-from admin@wolvez.club --debuglevel 1 --html ./test.html --file ./test.txt')
parser.add_argument("-s", "--server", help="Smtp Server.") # smtp服务器地址
parser.add_argument("-p", "--port", help="Smtp Server Port.", default=25, type=int) # smtp服务器端口
parser.add_argument("--mail-from", dest="mail_from", help="from mail addresses.", required=True) # 实际发送者,MAIL FROM
parser.add_argument("-to", "--to", help="to mail addresses.", required=True) # 接收邮件者,可为多个
parser.add_argument("--h-from", help="mail Mailsender") # 显示的发件人,FROM,伪造邮件头
parser.add_argument("--subject", help="message title", default="testmail") # 邮件主题
parser.add_argument('--content', '--msg-content', "--body", help="message body") # 邮件内容,如果为文件可包含html文件,也可以为输入包含html内容邮件
parser.add_argument("--html", help="message html body")
parser.add_argument("--attachment", "--file", required=False, help="One or few files to attach") # 邮件附件
parser.add_argument("--auth", help="Smtp Auth", type=bool, default=False) # smtp登录
parser.add_argument("--user", help="Smtp Auth username") # smtp 用户名
parser.add_argument("--password", help="Smtp Auth Password") # smtp 密码
parser.add_argument('--tls', dest='tls', default=False, type=bool, help="Use TLS if specified") # smtpsll方式
parser.add_argument("--debuglevel", required=False, type=int, default=True, help="Debug level")

args = parser.parse_args()
kwargs = args.__dict__
send_mail(**kwargs)