伪造电子邮件以及制造电子邮件炸弹的攻防探讨

想必熟悉 kali 或者接触过 smtp 相关分析的人都听说过 Swaks 这个工具。它号称 SMTP 界的瑞士军刀。工具会使固然厉害,但是不知道原理总觉得缺了点什么。于是我就用 Python 自己写了一个类似的工具,加上了邮件炸弹的功能,顺带分析一波原理。

此篇文章已在 freebuf 发表。

SMTP 协议

SMTP 协议即简单邮件传输协议,属于 TCP/IP 协议簇,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。

SMTP 模型如下:

为了与 SMTP 服务器通信,我们先获取 163.com 的 SMTP 服务器地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> nslookup -q=MX 163.com
Server: 202.117.112.3
Address: 202.117.112.3#53

Non-authoritative answer:
163.com mail exchanger = 10 163mx01.mxmail.netease.com.
163.com mail exchanger = 10 163mx02.mxmail.netease.com.
163.com mail exchanger = 10 163mx03.mxmail.netease.com.
163.com mail exchanger = 50 163mx00.mxmail.netease.com.

Authoritative answers can be found from:
163.com nameserver = ns2.166.com.
163.com nameserver = ns6.nease.net.
163.com nameserver = ns1.nease.net.
163.com nameserver = ns8.166.com.
163.com nameserver = ns4.nease.net.
163.com nameserver = ns3.nease.net.
163.com nameserver = ns5.nease.net.
ns1.nease.net internet address = 123.58.173.177
ns3.nease.net internet address = 220.181.36.234
ns4.nease.net internet address = 123.125.48.245
ns5.nease.net internet address = 121.195.179.18
ns6.nease.net internet address = 52.215.24.44

选一个 mail exchanger 即可,一般选数值最小的那个。具体这个数值代表什么含义,简单地说是 MX 记录的值代表邮件的路由过程,具体可在 维基百科 看看。

简单的通信过程如下(以 163 邮箱为例;以下返回均为正常情况):

  1. 第一步是请求建立连接:
    • telnet:telnet 163mx03.mxmail.netease.com 25
    • 返回:220 163.com Anti-spam GT for Coremail System (163com[20141201])
  2. 第二步是打开传输通道:
    • 发送:HELO <domain> <CRLF> 或 EHLO <domain/address-literal> <CRLF>。有什么区别呢?EHLO 更新一些,会返回 smtp 服务器支持的命令,相对比 HELO 要有用,所以基本用的都是 EHLOHELO/EHLO 命令用于主机介绍它自己,可以被翻译为 Hello, I am <domain>
    • 返回:250 OK 或:
      1
      2
      3
      4
      5
      6
      7
      8
      250-mail
      250-PIPELINING
      250-AUTH LOGIN PLAIN
      250-AUTH=LOGIN PLAIN
      250-coremail 1Uxr2xKj7kG0xkI17xGrU7I0s8FY2U3Uj8Cz28x1UUUUU7Ic2I0Y2Urz44XGUCa0xDrUUUUj
      250-STARTTLS
      250-SIZE 73400320
      250 8BITMIME

      可以看到,EHLO 返回信息更加详细。
  3. 第三步是 MAIL 命令:
    • 发送:MAIL FROM:<发送者邮箱> <CRLF>
    • 返回:250 Mail OK
  4. 第四步是 RCPT 命令:
    • 发送:RCPT TO:<接受者邮箱>
    • 返回:250 Mail OK
  5. 第五步是 DATA 命令:
    • 发送:DATA <CRLF>
    • 返回:354 End data with <CR><LF>.<CR><LF>
    • 然后就可以输入邮件内容了,包括主题,邮件正文等等。
  6. 第五步结束后,服务端会返回邮件发送成功与否的情况:
    • 返回:<= 250 Mail OK queued as mx13,P8CowAB3KDAxa5tbCxAiEg--.29768S2 1536912177

这样,一封简单的邮件就发出去了。当然,邮件服务器在这个过程中会做各种判断,以免轻易接受垃圾邮件。
完整的演示如下:

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
<= 220 163.com Anti-spam GT for Coremail System (163com[20141201])
=> ehlo antispam
<= 250-mail
<= 250-PIPELINING
<= 250-AUTH LOGIN PLAIN
<= 250-AUTH=LOGIN PLAIN
<= 250-coremail 1Uxr2xKj7kG0xkI17xGrU7I0s8FY2U3Uj8Cz28x1UUUUU7Ic2I0Y2UFQXaMIUCa0xDrUUUUj
<= 250-STARTTLS
<= 250-SIZE 73400320
<= 250 8BITMIME
=> mail from:<[email protected]>
<= 250 Mail OK
=> rcpt to:<手动打码@163.com>
<= 250 Mail OK
=> data
<= 354 End data with <CR><LF>.<CR><LF>
=> to: 手动打码@163.com
=> from: [email protected]
=> subject: 这是一封垃圾邮件
=>
=> DKIM?听上去很好吃
=> SPF 不是防晒系数吗
=> 我觉得 SMTP 很安全啊
=> 我们不存在垃圾邮件,那些都是正常的邮件
=> 邮件炸弹也是不存在的
=> .
<= 250 Mail OK queued as mx3,NcCowADX4z1_bJtbcsNOQw--.37583S2 1536912511

邮箱结果:
Telnet

那么,说了这么多 Telnet 发邮件,和正常发邮件的的方式很不一样。我们都是通过 web 界面或者 GUI 发的,如下:

正常发邮件
而我们使用 Telnet 直接发邮件,实际上是在模拟第二个 Send。
说清楚 SMTP 了,接下来说伪造邮件发件人。

伪造邮件发件人

回顾之前的 Telnet 发邮件的过程,我们可以看到,我们使用 mail from: 声称自己是 [email protected],而且 SMTP 协议本身也不要求对此声明做认证,所以伪造就是这样达成的。那么,经过这么多年的发展,厂商有对此做出了什么努力来解决这一问题呢?

SPF:发送方策略框架

SPF 是为了防范垃圾邮件而提出来的一种 DNS 记录类型,它是一种 TXT 类型的记录,它用于登记某个域名拥有的用来外发邮件的所有 IP 地址。发送人向接收方发送一封电子邮件后,邮件接收服务器接收电子邮件并执行如下操作:

  1. 检查哪一个域声称发送了该邮件并检查该域的 SPF 记录的 DNS。
  2. 确定发送服务器的 IP 地址是否与 SPF 记录中的某个已发布 IP 地址相匹配。
  3. 对电子邮件进行打分:如果 IP 地址匹配,则邮件通过身份验证并获得一个正分。如果 IP 地址不匹配,则邮件无法通过身份验证并获得一个负分。然后,对现有的防垃圾邮件筛选策略和启发式筛选应用这些结果。或者直接拒绝接受,返回 550 MI:SPF。

我们查一下 163 的 SPF 记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> nslookup -q=TXT 163.com
Server: 202.117.112.3
Address: 202.117.112.3#53

Non-authoritative answer:
163.com text = "v=spf1 include:spf.163.com -all"

Authoritative answers can be found from:
163.com nameserver = ns5.nease.net.
163.com nameserver = ns3.nease.net.
163.com nameserver = ns6.nease.net.
163.com nameserver = ns1.nease.net.
163.com nameserver = ns4.nease.net.
163.com nameserver = ns8.166.com.
163.com nameserver = ns2.166.com.
ns1.nease.net internet address = 123.58.173.177
ns3.nease.net internet address = 220.181.36.234
ns4.nease.net internet address = 123.125.48.245
ns5.nease.net internet address = 121.195.179.18
ns6.nease.net internet address = 52.215.24.44

v=spf1 include:spf.163.com -all 是什么意思呢?SPF 记录包含在一个 TXT 记录之中,格式为:v=spf1 [[pre] type [ext] ] ... [mod]

解释一下:

  1. v=spf1:SPF 的版本。如果使用 Sender ID 的话,这个字段就应该是 v=spf2
  2. pre:定义匹配时的返回值。可能的返回值包括:
    • +:缺省值。在测试完成的时候表示通过。
    • -:表示测试失败。这个值通常是 -all,表示没有其他任何匹配发生。
    • ~:表示软失败,通常表示测试没有完成。
    • ?:表示不置可否。这个值也通常在测试没有完成的时候使用。
  3. type:定义使用的确认测试的类型:
    • include:包含一个给定的域名的测试。以 include:domain 的形式书写。
    • all:终止测试序列。比如,如果选项是 -all,那么到达这条记录也就意味着测试失败了。但是如果无法确定,可以使用 ?all 来表示,这样,测试将被接受。
    • ip4:使用 IPv4 进行验证。这个可以以 ip4:ipv4ip4:ipv4/cidr 的形式使用。
  4. ext:定义对 type 的可选扩展。如果没有这个字段,那么仅使用单个记录进行问询。
  5. mod:这是最后的类型指示,作为记录的一个修正值。测试后有以下结果:
    1
    2
    3
    4
    5
    通过;SPF 记录指定要允许发送的主机;接受
    硬失败;SPF 记录已将主机指定为不允许发送;拒绝
    软失败;SPF 记录已将主机指定为不被允许发送但正在转换;接受但标记
    中性;SPF 记录明确指出,对有效性无关;接受
    没有;该域没有 SPF 记录,或者 SPF 记录不对结果进行评估;接受

    所以,当我们声称 [email protected] 向 163 发送邮件的时候,163 会不予接受(因为 qq 是有 spf 记录的):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <= 220 163.com Anti-spam GT for Coremail System (163com[20141201])
    => ehlo antispam
    <= 250-mail
    <= 250-PIPELINING
    <= 250-AUTH LOGIN PLAIN
    <= 250-AUTH=LOGIN PLAIN
    <= 250-coremail 1Uxr2xKj7kG0xkI17xGrU7I0s8FY2U3Uj8Cz28x1UUUUU7Ic2I0Y2UFsDjanUCa0xDrUUUUj
    <= 250-STARTTLS
    <= 250-SIZE 73400320
    <= 250 8BITMIME
    => mail from:<[email protected]>
    <= 550 MI:SPF 163 mx45,X8CowEB5qUXsdZtbeT4JTQ--.35331S2 1536914924 http://mail.163.com/help/help_spam_16.htm?ip=手动打码&hostid=mx45&time=1536914924

    SPF

DKIM 与 DMARC

DKIM 是一种防范电子邮件欺诈的验证技术,通过消息加密认证的方式对邮件发送域名进行验证。

DMARC 是一种基于现有的 SPF 和 DKIM 协议的可扩展电子邮件认证协议,在邮件收发双方建立了邮件反馈机制,便于邮件发送方和邮件接收方共同对域名的管理进行完善和监督。

感兴趣的话可以自行查阅资料。

漏网之鱼

又说了那么多,那么是否有了 SPF 可以一劳永逸呢?其实并不是。拿 163 与 qq 举例,查到发送方的 spf 记录,并且是标记的是硬失败,当然是最好的,直接进行判断。但是如果发送方用的是软失败甚至没有 spf 记录呢?比如 huawei.com 就是软失败:

1
huawei.com text = "v=spf1 ip4:45.249.212.32 ip4:45.249.212.35 ip4:119.145.14.93 ip4:58.251.152.93 ip4:194.213.3.17 ip4:206.16.17.72 ip4:45.249.212.255 ip4:45.249.212.187/29 ip4:45.249.212.191 ip4:185.176.76.210 ~all"

如果出现这样的情况,就得看厂商怎么做了,一般来说都是放行,那就有机可乘了。

邮件炸弹

说完了伪造发件人,再来说说邮件炸弹。

电子邮件炸弹是最古老的匿名攻击之一,通过设置一台机器不断的大量的向同一地址发送电子邮件,攻击者能够耗尽接受者网络的宽带。由于这种攻击方式简单易用,也有很多发匿名邮件的工具,而且只要对方获悉你的电子邮件地址就可以进行攻击,所以这是大家最值得防范的一个攻击手段。

既然能伪造发件人,那么发送邮件炸弹看上去也不难。事实上的确如此。另外,由于大量的请求以及链接,会触发接收方各种抵制。接下来通过邮件炸弹的三种不同的方式谈谈。

单线程

单线程,发就好了,拿起死循环就是干:while 1: xxxx

多线程:短连接

短连接就是我们最容易想到的,多线程,每个线程发起一次链接请求,发完一封就停止。线程数少点还好,一多就疯狂提示:421 Too many connections。在 ehlo antispam 的时候就被干掉了。实测大多数情况,100 线程发 能成功 20 封就已经很好了。而且发完一封就释放链接,蛮浪费的。而且在 smtp 服务器对频繁的连接请求很敏感的时候, ip 容易被 ban(如 qq)。

多线程:长连接

设定好线程数后,一旦 ehlo antispam 成功,就不停地重复 mail fromdata,邮件发送成功后也不释放链接,一直发。若接收方 smtp 服务器强制释放此链接,则重新 ehlo antispam。这样的话,如果不手动停止,就会一直尝试链接、发邮件。轰炸效率 plus。

email_hacker

根据上述的原理以及想法,我自己写了一个命令行的工具: email_hacker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
usage: email_hack.py [-h] -faddr FROM_ADDRESS -taddr TO_ADDRESS
[-tnum THREADS_NUM] [-v VERBOSE] [-c CRAZY_MODE]

optional arguments:
-h, --help show this help message and exit
-faddr FROM_ADDRESS, --from_address FROM_ADDRESS
fake from address
-taddr TO_ADDRESS, --to_address TO_ADDRESS
the address you want to delivery
-tnum THREADS_NUM, --threads_num THREADS_NUM
how many threads you want
-v VERBOSE, --verbose VERBOSE
verbose level
-c CRAZY_MODE, --crazy_mode CRAZY_MODE
Keep sending fake email (** Use with caution **)

详细内容可以到 gayhub 看看:https://github.com/Macr0phag3/email_hack

伪造邮件效果
163
伪造效果

qq
伪造效果

邮件炸弹效果
炸弹效果

炸弹效果
no_argv

防御

伪造邮件发件人

  1. 厂商:对 spf 记录采用 硬失败 的标记。且验证时遇到软失败标记的时候,尽可能拒绝。有可能的话,上 DKIMDMARC
  2. 用户:对于很重要的邮件信息,不妨查看一下邮件原文。
    邮件原文
    邮件原文

若只有一个 Received,且列出的 IP 与宣称的地址不一致,则就是伪造的。虽然 Received 头可以伪造,但是新的 Received 头会添加在消息的头部。所以,如果存在伪造的 Received,那么它总是在后面。

若有多个 Received,第一个 Received 中是正规的(网易、腾讯等等)SMTP 服务器 转发过的,那么肯定是经过验证的合法用户才能转发过来,因为这些厂商都不会开匿名转发,此时只需要看 IP 与宣称身份是否一致,一致就 OK。若看着就不正规,则就要小心一些了。

举 2 个例子:
未被伪造:
未被伪造
未被伪造
伪造:
伪造
伪造

邮件炸弹

对大量并发的连接请求,不但要拒绝,而且要禁止其 IP 一段时间。经过测试,qq 在大量请求后会进行封禁,163 只是拒绝连接,返回类似 “请 15 分钟后再试”,其实还是可以接着发起连接请求的... ╮(╯▽╰)╭。最后,在邮件多次发送失败的时候,直接断开连接,不要保留通道,强制发送端重新发起连接请求。


来呀快活呀


伪造电子邮件以及制造电子邮件炸弹的攻防探讨
https://www.tr0y.wang/2018/09/26/email-hacker/
作者
Tr0y
发布于
2018年9月26日
更新于
2024年6月3日
许可协议