目录
前言
1. 控制抓包行为
2. 控制信息如何显示
3. 控制显示什么数据
4. 过滤命令
4.1 地址过滤
4.2 协议及端口过滤
4.3 报文特征过滤
4.3.1 IP选项设置(20字节,可变部分(0-20)B,最大40字节)
4.3.2 TCP选项设置(基础长度20字节,最长可达60字节)
4.3.3.匹配TCP标志位
Tcpdum是Linux上强大的网络数据采集分析工具。
tcpdump选项可划分为四大类型:
1.控制抓包行为
2.控制信息如何显示
3.控制显示什么数据
4.过滤命令
#tcpdump --help
Usage: tcpdump [-aAbdDefhHIJKlLnNOpqStuUvxX#] [ -B size ] [ -c count ]
[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]
[ -i interface ] [ -j tstamptype ] [ -M secret ] [ --number ]
[ -Q|-P in|out|inout ]
[ -r file ] [ -s snaplen ] [ --time-stamp-precision precision ]
[ --immediate-mode ] [ -T type ] [ --version ] [ -V file ]
[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ]
[ -Z user ] [ expression ]
抓包文件保存:
#tcpdump -i any -s 0 -X -w /tmp/tcpdump.pcap
抓包文件解析:
#tcpdump -A -r /tmp/tcpdump.pcap|less
这一类命令行选项影响抓包行为,包括数据收集的方式。之前已介绍了两个例子:-r 和 -w。
-w 选项允许用户将输出重定向到一个文件,之后可通过-r选项将捕获数据显示出来。
如果用户知道需要捕获的报文数量或对于数量有一个上限,可使用-c选项。
则当达到该数量时程序自动终止,而无需使用kill命令或Ctrl-C。
如收集到100个报文之后tcpdump终止:
tcpdump -c 100
通过 -i 选项指定接口。在不确定的情况下,可使用ifconfig -a来检查哪一个接口可用及对应哪一个网络。
#tcpdump -i enp131s0 icmp
通过 -p 选项将网卡接口设置为非混杂模式。这一选项理论上将限制为捕获接口上的正常数据流,来自或发往主机,多播数据,以及广播数据。
-s 选项控制数据的截取长度。通常,tcpdump默认为一最大字节数量并只会从单一报文中截取到该数量长度。实际字节数取决于操作系统的设备驱动。通过默认值来截取合适的报文头,而舍弃不必要的报文数据。
如果用户需截取更多数据,通过-s选项来指定字节数。也可以用-s来减少截取字节数。
对于少于或等于200字节的报文,以下命令会截取完整报文:
#tcpdump -s 200
#tcpdump -n -tt -i bond_mgmt port 22
1639923887.526816 IP 172.30.136.59.ssh > 172.30.136.114.40948: Flags [P.], seq 1931446332:1931446528, ack 3414141942, win 318, options [nop,nop,TS val 2697135390 ecr 1876028651], length 196
-a,-n,-N和-f选项决定了地址信息是如何显示的。
-a 选项强制将网络地址显示为名称。
-n 阻止将地址显示为名字。
-N 阻止将域名转换。
-f 选项阻止远端名称解析。
-t 和 -tt 选项控制时间戳的打印。-t选项不显示时间戳而 -tt 选项显示无格式的时间戳。
可以通过 -v 和 -vv 选项来打印更多详细信息。例如,-v选项将会打印TTL字段。
要显示较少信息,使用 -q,或quiet选项。
-e 选项用于显示链路层头信息。上例中-e选项的输出为:
#tcpdump -ne -tt -i bond_mgmt port 22
1639923776.246051 00:e0:ed:2e:43:6e > 6c:92:bf:22:7f:d5, ethertype IPv4 (0x0800), length 262: 172.30.136.59.ssh > 172.30.136.114.40948: Flags [P.], seq 1929912564:1929912760, ack 3414140358, win 318, options [nop,nop,TS val 2697024109 ecr 1875917372], length 196
-x 选项将报文以十六进制形式dump出来,排除了链路层报文头。-x 和 -vv 选项报文显示如下:
#tcpdump -ne -v -x -i bond_mgmt port 22
22:26:32.122212 00:e0:ed:2e:43:6e > 6c:92:bf:22:7f:d5, ethertype IPv4 (0x0800), length 190: (tos 0x10, ttl 64, id 23674, offset 0, flags [DF], proto TCP (6), length 176)
172.30.136.59.ssh > 172.30.136.114.40948: Flags [P.], cksum 0x698d (incorrect -> 0x5143), seq 1934042648:1934042772, ack 3414143698, win 318, options [nop,nop,TS val 2697239985 ecr 1876133235], length 124
0x0000: 4510 00b0 5c7a 4000 4006 74d3 ac1e 883b
0x0010: ac1e 8872 0016 9ff4 7347 2618 cb7f b2d2 ... ...
要有效地使用tcpdump,掌握过滤器非常必要的。过滤允许用户指定想要抓取的数据流,从而用户可以专注于感兴趣的数据。
如果用户很清楚对何种数据流不感兴趣,可以将这部分数据排除在外。如果用户不确定需要什么数据,可以将源数据收集到文件之后再读取时应用过滤器。
实际应用中,需要经常在两种方式之间转换。
简单的过滤器是加在命令行之后的关键字。但是,复杂的命令是由逻辑和关系运算符构成的。对于这样的情况,通常最好用-F选项将过滤器存储在文件中。
非 : ! or "not" (without the quotes)
且 : && or "and"
或 : || or "or"
过滤器可以按照ip地址选择数据流。例如,考虑如下命令:
#tcpdump -ne -i bond_virt host 192.168.1.1
22:31:21.456181 fa:16:3e:ed:88:5a > fa:16:3e:f0:51:b2, ethertype 802.1Q (0x8100), length 102: vlan 4030, p 0, ethertype IPv4, 192.168.1.4 > 192.168.1.1: ICMP echo request, id 9327, seq 54, length 64
通过机器的以太网mac地址来选择数据流:
#tcpdump -ne -i bond_virt ether host fa:16:3e:ed:88:5a
22:32:58.459154 fa:16:3e:ed:88:5a > fa:16:3e:f0:51:b2, ethertype 802.1Q (0x8100), length 102: vlan 4030, p 0, ethertype IPv4, 192.168.1.4 > 192.168.1.1: ICMP echo request, id 9327, seq 151, length 64
数据流可进一步限制为单向,分别用src或dst指定数据流的来源或目的地。
#tcpdump -ne -i bond_virt dst 192.168.1.4
22:35:15.465312 fa:16:3e:f0:51:b2 > fa:16:3e:ed:88:5a, ethertype 802.1Q (0x8100), length 102: vlan 4030, p 0, ethertype IPv4, 192.168.1.1 > 192.168.1.4: ICMP echo reply, id 9327, seq 288, length 64
广播和多播数据相应可以使用broadcast和multicast。由于多播和广播数据流在链路层和网络层所指定的数据流是不同的,所以这两种过滤器各有两种形式。过滤器ether multicast抓取以太网多播地址的数据流,ip multicast抓取IP多播地址数据流。广播数据流也是类似的使用方法。注意多播过滤器也会抓到广播数据流。
除了抓取特定主机以外,还可以抓取特定网络。例如,以下命令限制抓取来自或发往224.0.0的vrrp报文:
#tcpdump -ne -i bond_virt net 224.0.0
22:37:15.092693 fa:16:3e:3a:19:69 > 01:00:5e:00:00:12, ethertype 802.1Q (0x8100), length 58: vlan 4003, p 0, ethertype IPv4, 169.254.192.11 > 224.0.0.18: VRRPv2, Advertisement, vrid 69, prio 50, authtype none, intvl 2s, length 20
#tcpdump -ne -i bond_virt net 224.0.0.0 mask 255.255.255.0
22:38:14.880908 fa:16:3e:09:5a:de > 01:00:5e:00:00:12, ethertype 802.1Q (0x8100), length 58: vlan 3994, p 0, ethertype IPv4, 169.254.192.143 > 224.0.0.18: VRRPv2, Advertisement, vrid 72, prio 50, authtype none, intvl 3s, length 20
抓取目的地址是192.168.1.254或192.168.1.200端口是80的TCP数据
#tcpdump '((tcp) and (port 80) and ((dst host 192.168.1.254) or (dst host 192.168.1.200)))'
制抓取指定协议如IP,UDP或TCP。还可以限制建立在这些协议之上的服务,如DNS或HTTP。这类抓取可以通过三种方式进行:使用tcpdump关键字,通过协议关键字proto,或通过服务使用port关键字。
当我们继续之前,必须了解tcp/ip包头的头部信息
proto[x:y] : 过滤从x字节开始的y字节数。比如ip[2:2]过滤出2、3字节(第三、第四字节)(第一字节从0开始排)
proto[x:y] & z = 0 : proto[x:y]和z的与操作为0
proto[x:y] & z !=0 : proto[x:y]和z的与操作不为0
proto[x:y] & z = z : proto[x:y]和z的与操作为z
proto[x:y] = z : proto[x:y]等于z
操作符:
> : greater 大于
< : lower 小于
>= : greater or equal 大于或者等于
<= : lower or equal 小于或者等于
= : equal 等于
!= : different 不等于
一些协议名能够被tcpdump识别到因此可通过关键字来指定。以下命令限制抓取UDP数据流:
# tcpdump -ne -i bond_virt udp
22:40:17.030950 90:1b:0e:ea:ea:7e > Broadcast, ethertype 802.1Q (0x8100), length 86: vlan 3122, p 0, ethertype IPv4, 10.34.244.146.49664 > 255.255.255.255.sentinelsrm: UDP, length 40
有很多传输层服务没有可以识别的关键字。在这种情况下,可以使用关键字proto或 ip proto 加上 /etc/protocols能够找到的协议名或相应的协议编号。
# tcpdump -ne -i bond_virt proto 112
22:48:07.891750 fa:16:3e:f3:fb:c8 > 01:00:5e:00:00:12, ethertype 802.1Q (0x8100), length 64: vlan 3981, p 0, ethertype IPv4, 169.254.192.22 > 224.0.0.18: VRRPv2, Advertisement, vrid 234, prio 50, authtype none, intvl 2s, length 20
对于更高层级的建立于底层协议之上的服务,必须使用关键字port。
# tcpdump -nnne -i bond_virt port 137
22:51:58.320013 94:c6:91:7f:bb:f7 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 96: vlan 3122, p 0, ethertype IPv4, 172.16.0.89.137 > 172.16.0.255.137: NBT UDP PACKET(137): QUERY; REQUEST; BROADCAST
过滤器也可以基于报文特征比如报文长度或特定字段的内容,过滤器必须包含关系运算符。要指定长度,使用关键字less或greater。如下例所示:
# tcpdump -nnne -i bond_virt greater 200
22:54:38.206873 3c:8c:40:ad:11:ff > 01:80:c2:00:00:0e, ethertype LLDP (0x88cc), length 342: LLDP, length 328: NET-ACCE-05-XX.XX.XXX
该命令收集长度大于200字节的报文。
根据报文内容过滤更加复杂,因为用户必须理解报文头的结构。但是尽管如此,或者说正因如此,这一方式能够使用户最大限度的控制抓取的数据。
4.3.1.1 IP version & Hdr Len
一般”的IP头是20字节,但IP头有选项设置,不能直接从偏移21字节处读取数据。IP头有个长度字段可以知道头长度是否大于20字节。
通常第一个字节的二进制值是:01000101,分成两个部分:
0100 = 4 表示IP版本 0101 = 5 表示 IP头32 bit的块数,5 x 32 bits = 160 bits or 20 bytes
如果第一字节第二部分的值大于5,那么表示头有IP选项。
#tcpdump 'ip[0] > 69'
0100 0101 : 第一字节的二进制
0000 1111 : 与操作
0000 0101 : 结果
#tcpdump 'ip[0] & 15 > 5'
或者
#tcpdump 'ip[0] & 0x0f > 5'
4.3.1.2 DF分片标记
当发送端的MTU大于到目的路径链路上的MTU时就会被分片
分片信息在IP头的第七和第八字节:
Bit 0: 保留,必须是0
Bit 1: (DF) 0 = 可能分片, 1 = 不分片
Bit 2: (MF) 0 = 最后的分片, 1 = 还有分片
Fragment Offset字段只有在分片的时候才使用。
#tcpdump 'ip[6] = 64'
# tcpdump -nnne -c 10 -i bond_mgmt 'ip[6] = 64'
11:18:00.670704 00:e0:ed:2e:43:6e > 6c:92:bf:22:7f:d5, ethertype IPv4 (0x0800), length 262: 172.30.136.59.22 > 172.30.136.114.57414: Flags [P.], seq 3649304248:3649304444, ack 1818371249, win 318, options [nop,nop,TS val 2743528534 ecr 1922421795], length 196
a:匹配MF,分片包(测试方法:ping -M want -s 3000 114.114.114.114)
# tcpdump -nnne -c 5 -i bond_mgmt 'ip[6] = 32'
11:20:49.542309 00:e0:ed:2e:43:6e > 00:00:5e:00:01:33, ethertype IPv4 (0x0800), length 1514: 172.30.136.59 > 114.114.114.114: ICMP echo request, id 24301, seq 1, length 1480
b:匹配分片和最后分片
# tcpdump -nnne -c 10 -i bond_mgmt '((ip[6:2] > 0) and (not ip[6] = 64))'
11:23:34.367123 00:e0:ed:2e:43:6e > 00:00:5e:00:01:33, ethertype IPv4 (0x0800), length 1514: 172.30.136.59 > 114.114.114.114: ICMP echo request, id 24873, seq 1, length 1480
11:23:34.367136 00:e0:ed:2e:43:6e > 00:00:5e:00:01:33, ethertype IPv4 (0x0800), length 1514: 172.30.136.59 > 114.114.114.114: ip-proto-1
11:23:34.367140 00:e0:ed:2e:43:6e > 00:00:5e:00:01:33, ethertype IPv4 (0x0800), length 82: 172.30.136.59 > 114.114.114.114: ip-proto-1
4.3.1.3 匹配小于ttl的数据报
TTL字段在第九字节,并且正好是完整的一个字节,TTL最大值是255,二进制为11111111。
可以来验证下,我们试着制定一个特需的ttl长度为 256 (ping -M want -s 3000 -t 256 192.168.1.200 ping: ttl 256 out of range)
# tcpdump -nnne -c 10 -i bond_mgmt 'ip[8] < 80'
11:31:08.076553 00:e0:ed:2e:43:6e > 6c:92:bf:22:7f:d5, ethertype IPv4 (0x0800), length 262: 172.30.136.59.22 > 172.30.136.114.57414: Flags [P.], seq 3649329072:3649329268, ack 1818381829, win 318, options [nop,nop,TS val 2744315940 ecr 1923209214], length 196
4.3.1.4匹配协议头
一般使用语法 proto [ expr : size ]。字段proto指定要查看的报文头,ip则查看IP头,tcp则查看TCP头,以此类推。
expr字段给出从报文头索引0开始的位移。
即:报文头的第一个字节为0,第二字节为1,以此类推。
size字段是可选的,指定需要使用的字节数,1,2或4。
# tcpdump -nnne -vv -i bond_virt "ip[9] = 6"
tcpdump: listening on bond_virt, link-type EN10MB (Ethernet), capture size 262144 bytes
23:05:43.092721 fa:16:3e:26:67:7c > fa:16:3e:ed:88:5a, ethertype 802.1Q (0x8100), length 78: vlan 4030, p 0, ethertype IPv4, (tos 0x0, ttl 64, id 8074, offset 0, flags [DF], proto TCP (6), length 60)
查看第十字节的IP头,协议值为6。注意这里必须使用引号。撇号或引号都可以,但反引号将无法正常工作。
# tcpdump -nnne -vv -i bond_virt "ip[9] = 1" (ICMP的协议值为1)
23:08:52.719465 fa:16:3e:ed:88:5a > fa:16:3e:f0:51:b2, ethertype 802.1Q (0x8100), length 102: vlan 4030, p 0, ethertype IPv4, (tos 0x0, ttl 64, id 58371, offset 0, flags [DF], proto ICMP (1), length 84)
这一方式常常作为掩码来选择特定比特位。值可以是十六进制。可通过语法&加上比特掩码来指定。
4.3.2.1.抓取源端口TCP数据包
#tcpdump 'tcp[0:2] > 1024'
or
#tcpdump 'tcp src portrange 1025-65535'
4.3.2.2.匹配TCP数据包的特殊标记
TCP标记定义在TCP头的第十四个字节
++++++++++++++
|C|E|U|A|P|R|S|F|
|W|C|R|C|S|S|Y|I|
|R|E|G|K|H|T|N|N|
++++++++++++++
在TCP 3次握手中,两个主机是如何交换数据
1、源端发送 SYN
2、目标端口应答 SYN,ACK
3、源端发送 ACK
只抓取SYN包,第十四字节是二进制的00000010,也就是十进制的2
#tcpdump 'tcp[13] = 2'
#tcpdump 'tcp[13] = 18'
#tcpdump 'tcp[13] & 2 = 2'
#tcpdump 'tcp[13] = 24'
#tcpdump 'tcp[13] & 1 = 1'
#tcpdump 'tcp[13] & 4 = 4'
# tcpdump -nnne -vv -i bond_mgmt "tcp[13] & 0x03 != 0"
23:14:51.210165 00:e0:ed:2e:43:6e > 00:e0:ed:49:3d:cd, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 53928, offset 0, flags [DF], proto TCP (6), length 60)
172.30.136.59.53094 > 172.30.136.89.6789: Flags [S], cksum 0x6900 (incorrect -> 0x276e), seq 3618113769, win 29200, options [mss 1460,sackOK,TS val 2700139073 ecr 0,nop,wscale 7], length 0
23:14:51.211618 00:e0:ed:2e:43:6e > 00:e0:ed:49:3d:cd, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 53936, offset 0, flags [DF], proto TCP (6), length 52)
172.30.136.59.53094 > 172.30.136.89.6789: Flags [F.], cksum 0x68f8 (incorrect -> 0x646a), seq 332, ack 433, win 237, options [nop,nop,TS val 2700139075 ecr 1881252428], length 0
# tcpdump -nnne -vv -i bond_mgmt "tcp[0:2] & 0xffff > 0x0017"
23:12:56.113286 6c:92:bf:22:7f:d5 > 00:e0:ed:2e:43:6e, ethertype IPv4 (0x0800), length 66: (tos 0x10, ttl 64, id 53673, offset 0, flags [DF], proto TCP (6), length 52)
172.30.136.114.40948 > 172.30.136.59.22: Flags [.], cksum 0x614b (correct), seq 3414195790, ack 1936528380, win 4372, options [nop,nop,TS val 1878917274 ecr 2700023976], length 0
4.3.3.1 TCP标记值:
tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-push, tcp-ack, tcp-urg
#tcpdump 'tcp[tcpflags] == tcp-ack'
#tcpdump 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0
下图表示了TCP各状态转换的标记:
tcpdump 提供了常用的字段偏移名字:
icmptype (ICMP类型字段)
icmpcode (ICMP符号字段)
tcpflags (TCP标记字段)
ICMP类型值有:
icmp-echoreply, icmp-unreach, icmp-sourcequench, icmp-redirect, icmp-echo, icmp-routeradvert, icmp-routersolicit,icmp-timxceed, icmp-paramprob, icmp-tstamp, icmp-tstampreply, icmp-ireq, icmp-ireqreply, icmp-maskreq, icmp-maskreply
4.3.3.2 HTTP数据过滤
十六进制转换方法:python -c ‘print “MAIL”.encode(“hex”)’ --> 4d41494c
#tcpdump 'tcp[32:4] = 0x47455420'
$offset = ((tcp[12:1] & 0xf0) >> 2)
tcp[$offset:4]
#tcpdump -nnne -i bond_virt -s 0 -A 'tcp dst port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'
查看HTTP请求响应头以及数据
#tcpdump -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
#tcpdump -X -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
# tcpdump -nnne -i bond_virt -c 5 -X -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
4.3.3.3 wireshark分析http包过滤:
按请求头、请求体
http contains "XXXXX" #显示HTTP包中包含指定内容的数据包
http.request.uri contains "XXXXX" #显示HTTP请求包中包含指定内容的数据包
http.time >=0.1 #显示HTTP响应时间大于等于指定时间的数据包
http.request #显示HTTP请求数据包
http.respone #显示HTTP应答数据包
http.response.code == 404 #显示代码为404的HTTP响应数据包
http.content_length < 10 #显示HTTP的内容长度小于指定长度的数据包
#显示特定时间的数据包
frame.time == "May 27, 2021 15:23:57.932344000"
#显示时间间隔的数据包
frame.time >= "May 27, 2021 15:23:57.0" && frame.time < "May 27, 2021 15:23:58.0"
4.3.3.4 使用tcpdump方式抓取uri包:
过滤表达式:
tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420 && tcp[((tcp[12:1] & 0xf0) >> 2) + 4:4] = 0x2f6d652d && tcp[((tcp[12:1] & 0xf0) >> 2) + 8:4] = 0x612d6d69 && tcp[((tcp[12:1] & 0xf0) >> 2) + 12:4] = 0x6c6b7368 && tcp[((tcp[12:1] & 0xf0) >> 2) + 16:4] = 0x616b652d && tcp[((tcp[12:1] & 0xf0) >> 2) + 20:4] = 0x706c6561 && tcp[((tcp[12:1] & 0xf0) >> 2) + 24:4] = 0x73652f20 && tcp[((tcp[12:1] & 0xf0) >> 2) + 28:4] = 0x48545450 && tcp[((tcp[12:1] & 0xf0) >> 2) + 32:4] = 0x2f312e31
tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420 && tcp[((tcp[12:1] & 0xf0) >> 2) + 4:4] = 0x2f70726f && tcp[((tcp[12:1] & 0xf0) >> 2) + 8:4] = 0x64756374 && tcp[((tcp[12:1] & 0xf0) >> 2) + 12:4] = 6c697374'
4.3.4 链路层数据头选项设置匹配(18字节)
Ethernet II 帧头:6+6+2+4=18Bytes
目标MAC|源MAC|类型|数据|FCS
最小帧长6+6+2+46+4 = 64字节,最大6+6+2+1500+4 = 1518字节。(注:ISL封装后可达1548字节(ISL标记的长度为30字节,思科私有),802.1Q封装后可达1522字节(+4字节vlan信息))
# tcpdump -nnne -vv -i bond_mgmt "ether[0] & 1 != 0"
tcpdump: listening on bond_mgmt, link-type EN10MB (Ethernet), capture size 262144 bytes
23:13:52.540436 a0:36:9f:89:0c:66 > 01:00:5e:00:00:12, ethertype IPv4 (0x0800), length 60: (tos 0xc0, ttl 255, id 23668, offset 0, flags [none], proto VRRP (112), length 40)
172.30.140.129 > 224.0.0.18: vrrp 172.30.140.129 > 224.0.0.18: VRRPv2, Advertisement, vrid 151, prio 50, authtype none, intvl 2s, length 20, addrs: 172.30.140.252
Dest MAC: 目的MAC地址
Src MAC: 源MAC地址
帧类型: 0x0806
硬件类型: 1( 以太网)
协议类型: 0x0800( IP地址)
硬件地址长度: 6
协议地址长度: 4
OP: 1( ARP请求) , 2( ARP应答) , 3( RARP请求) , 4( RARP应答)
#tcpdump -nnne -c 3 -i bond_virt vlan 1012
#tcpdump -nnne -c 3 -i bond_virt 'ether[12:2] = 0x8100'
#tcpdump -nnne -c 3 -i bond_virt 'ether[12:2] = 0x8100 and ((ether[14] << 8)+ ether[15]) = 1021 '