bingoohuang / blog

write blogs with issues
MIT License
177 stars 23 forks source link

HTTP的会话保持信息咋看不到呢? #134

Open bingoohuang opened 4 years ago

bingoohuang commented 4 years ago

KEEP-ALIVE

为啥服务端返回没有Connection: keep-alive的信息

使用curl -v 查看keep alive,服务端没有返回任何keep-alive的信息,比较疑惑,于是补充了一下知识。

$ curl -v http://127.0.0.1:8812 http://127.0.0.1:8812
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8812 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8812
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 31 Dec 2019 03:52:27 GMT
< Content-Length: 6
< Content-Type: text/plain; charset=utf-8
<
Hello
* Connection #0 to host 127.0.0.1 left intact
* Found bundle for host 127.0.0.1: 0x7fda13614800 [can pipeline]
* Could pipeline, but not asked to!
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 8812 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8812
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 31 Dec 2019 03:52:27 GMT
< Content-Length: 6
< Content-Type: text/plain; charset=utf-8
<
Hello
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
➜  http-upload-benchmark git:(master)

“left intact”意思是“保留不变的”,例句:when the nest is upset no egg is left intact [idiom.]—覆巢之下无完卵

什么是Keep-Alive模式?

HTTP协议采用“请求-应答”模式,当使用普通模式,即非KeepAlive模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议);当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。来源

image image

在 HTTP 1.1 中 所有的连接默认都是持续连接,除非特殊声明不支持。

根据wiki HTTP持久连接

在 HTTP 1.0 中, 没有官方的 keepalive 的操作。通常是在现有协议上添加一个指数。如果浏览器支持 keep-alive,它会在请求的包头中添加:

Connection: Keep-Alive

然后当服务器收到请求,作出回应的时候,它也添加一个头在响应中:

Connection: Keep-Alive

这样做,连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个连接。这一直继续到客户端或服务器端认为会话已经结束,其中一方中断连接。

在 HTTP 1.1 中 所有的连接默认都是持续连接,除非特殊声明不支持。HTTP 持久连接不使用独立的 keepalive 信息,而是仅仅允许多个请求使用单个连接

按照这个思路,我做了一下验证:

HTTP/1.0,果然发完,服务端主动关闭连接了

$ telnet 127.0.0.1 8812
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0
Host: 127.0.0.1:8812

HTTP/1.0 200 OK
Date: Tue, 31 Dec 2019 03:59:34 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8

Hello
Connection closed by foreign host.
$

HTTP/1.0,加上Connection: Keep-Alive,服务器端没关闭连接

http-upload-benchmark git:(master) telnet 127.0.0.1 8812
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0
Host: 127.0.0.1:8812
Connection: Keep-Alive

HTTP/1.0 200 OK
Date: Tue, 31 Dec 2019 04:00:38 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Connection: keep-alive

Hello
GET / HTTP/1.0
Host: 127.0.0.1:8812

HTTP/1.0 200 OK
Date: Tue, 31 Dec 2019 04:00:48 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8

Hello
Connection closed by foreign host.
➜  http-upload-benchmark git:(master)

HTTP/1.1,默认连接保持了

➜  http-upload-benchmark git:(master) telnet 127.0.0.1 8812
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
Host: 127.0.0.1:8812

HTTP/1.1 200 OK
Date: Tue, 31 Dec 2019 04:02:11 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8

Hello
GET / HTTP/1.1
Host: 127.0.0.1:8812

HTTP/1.1 200 OK
Date: Tue, 31 Dec 2019 04:02:12 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8

Hello
^[
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close

400 Bad RequestConnection closed by foreign host.

通过netstat查看端口监听情况

$ netstat -an  | grep 8812
tcp4       0      0  127.0.0.1.8812         127.0.0.1.52493        ESTABLISHED
tcp4       0      0  127.0.0.1.52493        127.0.0.1.8812         ESTABLISHED
tcp46      0      0  *.8812                 *.*                    LISTEN

HTTP头中的Keep-Alive

服务端可以这么告知客户端:Keep-Alive: timeout=15, max=100 15秒内还可以再发100个请求,然后就会关闭连接了。

  1. The Keep-Alive header is a hop-by-hop header that provides information about a persistent connection. Both client and server are able to provide information independently. Hypertext Transfer Protocol (HTTP) Keep-Alive Header
  2. 客户端不能设置keep-alive的超时时间,这个由服务器端配置。The client cannot specify the timeout, it is the server configuration that determines the maximum timeout value. The extra Keep-Alive header can inform the client how long the server is willing to keep the connection open (timeout=N value) and how many requests you can do over the same connection (max=M) before the server will force a close of the connection.

抓个包玩玩

参考 TCP connection reuse when HTTP in golang

$ sudo tcpdump -i lo0 -s 0 -n tcp -c 1000 -w /tmp/p1.pcap

tcpdump: listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes
1000 packets captured
1032 packets received by filter
0 packets dropped by kernel
➜  ~

再用wireshark打开p1.pcap,指定过滤条件tcp.port == 8087,过滤src或dst port是8087的,就是我们感兴趣的tcp包

image

go-http-client关闭keep-alive再抓包试试,

$ go-http-client -sleep 1s -keepalive=false
2019/12/31 13:18:46 server http://127.0.1:8812
2019/12/31 13:18:46 sleep 1s
2019/12/31 13:18:46 keepalive false
2019/12/31 13:18:46 retry mode false
2019/12/31 13:18:46 dumpResponse HTTP/1.1 200 OK
Connection: close
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Date: Tue, 31 Dec 2019 05:18:46 GMT

Hello
2019/12/31 13:18:46 start sleep 1s
2019/12/31 13:18:47 dumpResponse HTTP/1.1 200 OK
Connection: close
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Date: Tue, 31 Dec 2019 05:18:47 GMT

Hello

从图中,就可以看出每次都是从头三次握手🤝:姑娘贵庚?小女18,大哥您贵庚?我81了。如果反复这么聊,姑娘还没怒,也是有失忆症了。

image

回顾一下三次握手:

image

因为双方都需要和对方同步 seq 号,所以需要来回确认。服务器把 SYN 和 ACK 合并成一条消息,所以最终只需要三次交流就可以了。当然如果你想把 SYN 和 ACK 拆开成两个消息也可以,只不过协议栈一般不这样实现。

比如你参加一次相亲聚会,看到一个漂亮的姑娘,你想去搭讪,首先得先了解一下。

你:姑娘您好,贵庚啊?
姑娘:小女子18,请问大哥您贵庚啊?
你:我81了
......

这样寒暄之后你们双方就可以进一步的深入的交流了。

回顾一下四路挥手

image

客户端和服务器端都可以主动关闭连接。主动关闭的一方我们称之为发起者,被动关闭接收的那一方我们称之为接受者。

发起者要关闭连接,需要发送FIN,然后接收者发送ACK。这个时候被动者有可能恋恋不舍,还有数据想发送给你,所以接受者这一端它的连接还没有释放,直到它发送FIN,发起者回复ACK,接收端的连接才释放。

姑娘:我要走了
你:再见

......你依依不舍

你:我也要走了
姑娘:再见

tcpdump

tcpdump是分析网络情况的神器,经常用来分析疑难杂症,并且让狡辩者哑口无言。

image

网络异常状况

image

感谢

  1. 谁拔了我的网线?Go 网络异常对程序行为的影响
bingoohuang commented 4 years ago

那么,从北京到上海的建立一个TCP连接大概需要多久呢

北京到上海的直线距离约为 1000 多公里,而光速是目前通信速度的极限,所以 RTT 一定会大于 6.7ms:

输入图片说明

在网络通信中,从发送方发出数据开始到收到来自接收方的确认的时间被叫做往返时延(Round-Trip Time,RTT)。

光在光纤中不是直线传播的,真正的传输速度会比光速慢 ~31%,而且数据需要在各种网络设备之间来回跳转,所以很难达到理论的极限值。实际环境中从北京到上海的 RTT 大概在 40ms 左右,所以 TCP 建立连接所需要最短时间也需要 60ms(1.5RTT)。

bingoohuang commented 4 years ago

Tcpdump 示例教程

  1. Tcpdump 示例教程
  2. Tcpdump Examples

tcpdump 是一款强大的网络抓包工具,它使用 libpcap 库来抓取网络数据包,这个库在几乎在所有的 Linux/Unix 中都有。熟悉 tcpdump 的使用能够帮助你分析调试网络数据,本文将通过一个个具体的示例来介绍它在不同场景下的使用方法。不管你是系统管理员,程序员,云原生工程师还是 yaml 工程师,掌握 tcpdump 的使用都能让你如虎添翼,升职加薪。

基本语法和使用方法

tcpdump 的常用参数如下:

$ tcpdump -i eth0 -nn -s0 -v port 80

  1. -i : 选择要捕获的接口,通常是以太网卡或无线网卡,也可以是 vlan 或其他特殊接口。如果该系统上只有一个网络接口,则无需指定。
  2. -nn : 单个 n 表示不解析域名,直接显示 IP;两个 n 表示不解析域名和端口。这样不仅方便查看 IP 和端口号,而且在抓取大量数据时非常高效,因为域名解析会降低抓取速度。
  3. -s0 : tcpdump 默认只会截取前 96 字节的内容,要想截取所有的报文内容,可以使用 -s number, number 就是你要截取的报文字节数,如果是 0 的话,表示截取报文全部内容。
  4. -v : 使用 -v,-vv 和 -vvv 来显示更多的详细信息,通常会显示更多与特定协议相关的信息。
  5. port 80 : 这是一个常见的端口过滤器,表示仅抓取 80 端口上的流量,通常是 HTTP。

额外再介绍几个常用参数:

  1. -p : 不让网络接口进入混杂模式。默认情况下使用 tcpdump 抓包时,会让网络接口进入混杂模式。一般计算机网卡都工作在非混杂模式下,此时网卡只接受来自网络端口的目的地址指向自己的数据。当网卡工作在混杂模式下时,网卡将来自接口的所有数据都捕获并交给相应的驱动程序。如果设备接入的交换机开启了混杂模式,使用 -p 选项可以有效地过滤噪声。
  2. -e : 显示数据链路层信息。默认情况下 tcpdump 不会显示数据链路层信息,使用 -e 选项可以显示源和目的 MAC 地址,以及 VLAN tag 信息。例如:
$ tcpdump -n -e -c 5 not ip6

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br-lan, link-type EN10MB (Ethernet), capture size 262144 bytes
18:27:53.619865 24:5e:be:0c:17:af > 00:e2:69:23:d3:3b, ethertype IPv4 (0x0800), length 1162: 192.168.100.20.51410 > 180.176.26.193.58695: Flags [.], seq 2045333376:2045334484, ack 3398690514, win 751, length 1108
18:27:53.626490 00:e2:69:23:d3:3b > 24:5e:be:0c:17:af, ethertype IPv4 (0x0800), length 68: 220.173.179.66.36017 > 192.168.100.20.51410: UDP, length 26
18:27:53.626893 24:5e:be:0c:17:af > 00:e2:69:23:d3:3b, ethertype IPv4 (0x0800), length 1444: 192.168.100.20.51410 > 220.173.179.66.36017: UDP, length 1402
18:27:53.628837 00:e2:69:23:d3:3b > 24:5e:be:0c:17:af, ethertype IPv4 (0x0800), length 1324: 46.97.169.182.6881 > 192.168.100.20.59145: Flags [P.], seq 3058450381:3058451651, ack 14349180, win 502, length 1270
18:27:53.629096 24:5e:be:0c:17:af > 00:e2:69:23:d3:3b, ethertype IPv4 (0x0800), length 54: 192.168.100.20.59145 > 192.168.100.1.12345: Flags [.], ack 3058451651, win 6350, length 0
5 packets captured

显示 ASCII 字符串

-A 表示使用 ASCII 字符串打印报文的全部数据,这样可以使读取更加简单,方便使用 grep 等工具解析输出内容。-X 表示同时使用十六进制和 ASCII 字符串打印报文的全部数据。这两个参数不能一起使用。例如:

$ tcpdump -A -s0 port 80

抓取特定协议的数据

后面可以跟上协议名称来过滤特定协议的流量,以 UDP 为例,可以加上参数 udp 或 protocol 17,这两个命令意思相同。

$ tcpdump -i eth0 udp $ tcpdump -i eth0 proto 17

同理,tcp 与 protocol 6 意思相同。

抓取特定主机的数据

使用过滤器 host 可以抓取特定目的地和源 IP 地址的流量。

$ tcpdump -i eth0 host 10.10.1.1

也可以使用 src 或 dst 只抓取源或目的地:

$ tcpdump -i eth0 dst 10.10.1.20

将抓取的数据写入文件

使用 tcpdump 截取数据报文的时候,默认会打印到屏幕的默认输出,你会看到按照顺序和格式,很多的数据一行行快速闪过,根本来不及看清楚所有的内容。不过,tcpdump 提供了把截取的数据保存到文件的功能,以便后面使用其他图形工具(比如 wireshark,Snort)来分析。

-w 选项用来把数据报文输出到文件:

$ tcpdump -i eth0 -s0 -w test.pcap

行缓冲模式

如果想实时将抓取到的数据通过管道传递给其他工具来处理,需要使用 -l 选项来开启行缓冲模式(或使用 -c 选项来开启数据包缓冲模式)。使用 -l 选项可以将输出通过立即发送给其他命令,其他命令会立即响应。

$ tcpdump -i eth0 -s0 -l port 80 | grep 'Server:'

组合过滤器

过滤的真正强大之处在于你可以随意组合它们,而连接它们的逻辑就是常用的 与/AND/&& 、 或/OR/|| 和 非/not/!。

and or &&
or or ||
not or !

过滤器

关于 tcpdump 的过滤器,这里有必要单独介绍一下。

机器上的网络报文数量异常的多,很多时候我们只关系和具体问题有关的数据报(比如访问某个网站的数据,或者 icmp 超时的报文等等),而这些数据只占到很小的一部分。把所有的数据截取下来,从里面找到想要的信息无疑是一件很费时费力的工作。而 tcpdump 提供了灵活的语法可以精确地截取关心的数据报,简化分析的工作量。这些选择数据包的语句就是过滤器(filter)!

Host 过滤器

Host 过滤器用来过滤某个主机的数据报文。例如:

$ tcpdump host 1.2.3.4

该命令会抓取所有发往主机 1.2.3.4 或者从主机 1.2.3.4 发出的流量。如果想只抓取从该主机发出的流量,可以使用下面的命令:

$ tcpdump src host 1.2.3.4

Network 过滤器

Network 过滤器用来过滤某个网段的数据,使用的是 CIDR 模式。可以使用四元组(x.x.x.x)、三元组(x.x.x)、二元组(x.x)和一元组(x)。四元组就是指定某个主机,三元组表示子网掩码为 255.255.255.0,二元组表示子网掩码为 255.255.0.0,一元组表示子网掩码为 255.0.0.0。例如,

抓取所有发往网段 192.168.1.x 或从网段 192.168.1.x 发出的流量:

$ tcpdump net 192.168.1

抓取所有发往网段 10.x.x.x 或从网段 10.x.x.x 发出的流量:

$ tcpdump net 10

和 Host 过滤器一样,这里也可以指定源和目的:

$ tcpdump src net 10

也可以使用 CIDR 格式:

$ tcpdump src net 172.16.0.0/12

Proto 过滤器

Proto 过滤器用来过滤某个协议的数据,关键字为 proto,可省略。proto 后面可以跟上协议号或协议名称,支持 icmp, igmp, igrp, pim, ah, esp, carp, vrrp, udp 和 tcp。因为通常的协议名称是保留字段,所以在于 proto 指令一起使用时,必须根据 shell 类型使用一个或两个反斜杠(/)来转义。Linux 中的 shell 需要使用两个反斜杠来转义,MacOS 只需要一个。

例如,抓取 icmp 协议的报文:

$ tcpdump -n proto \\icmp

或者

$ tcpdump -n icmp

Port 过滤器

Port 过滤器用来过滤通过某个端口的数据报文,关键字为 port。例如:

$ tcpdump port 389

理解 tcpdump 的输出

截取数据只是第一步,第二步就是理解这些数据,下面就解释一下 tcpdump 命令输出各部分的意义。

21:27:06.995846 IP (tos 0x0, ttl 64, id 45646, offset 0, flags [DF], proto TCP (6), length 64)
    192.168.1.106.56166 > 124.192.132.54.80: Flags [S], cksum 0xa730 (correct), seq 992042666, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 663433143 ecr 0,sackOK,eol], length 0

21:27:07.030487 IP (tos 0x0, ttl 51, id 0, offset 0, flags [DF], proto TCP (6), length 44)
    124.192.132.54.80 > 192.168.1.106.56166: Flags [S.], cksum 0xedc0 (correct), seq 2147006684, ack 992042667, win 14600, options [mss 1440], length 0

21:27:07.030527 IP (tos 0x0, ttl 64, id 59119, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.1.106.56166 > 124.192.132.54.80: Flags [.], cksum 0x3e72 (correct), ack 2147006685, win 65535, length 0

最基本也是最重要的信息就是数据报的源地址/端口和目的地址/端口,上面的例子第一条数据报中,源地址 ip 是 192.168.1.106,源端口是 56166,目的地址是 124.192.132.54,目的端口是 80。 > 符号代表数据的方向。

此外,上面的三条数据还是 tcp 协议的三次握手过程,第一条就是 SYN 报文,这个可以通过 Flags [S] 看出。下面是常见的 TCP 报文的 Flags:

而第二条数据的 [S.] 表示 SYN-ACK,就是 SYN 报文的应答报文。

例子

下面给出一些具体的例子,每个例子都可以使用多种方法来获得相同的输出,你使用的方法取决于所需的输出和网络上的流量。我们在排障时,通常只想获取自己想要的内容,可以通过过滤器和 ASCII 输出并结合管道与 grep、cut、awk 等工具来实现此目的。

例如,在抓取 HTTP 请求和响应数据包时,可以通过删除标志 SYN/ACK/FIN 来过滤噪声,但还有更简单的方法,那就是通过管道传递给 grep。在达到目的的同时,我们要选择最简单最高效的方法。下面来看例子。

提取 HTTP 用户代理

从 HTTP 请求头中提取 HTTP 用户代理:

$ tcpdump -nn -A -s1500 -l | grep "User-Agent:"

通过 egrep 可以同时提取用户代理和主机名(或其他头文件):

$ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:'

只抓取 HTTP GET 和 POST 流量

抓取 HTTP GET 流量:

$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'

也可以抓取 HTTP POST 请求流量:

$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354'

注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST 请求会被分割为多个 TCP 数据包。

上述两个表达式中的十六进制将会与 GET 和 POST 请求的 ASCII 字符串匹配。例如,tcp[((tcp[12:1] & 0xf0) >> 2):4] 首先会确定我们感兴趣的字节的位置(在 TCP header 之后),然后选择我们希望匹配的 4 个字节。

提取 HTTP 请求的 URL

提取 HTTP 请求的主机名和路径:

$ tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:"

tcpdump: listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
    POST /wp-login.php HTTP/1.1
    Host: dev.example.com
    GET /wp-login.php HTTP/1.1
    Host: dev.example.com
    GET /favicon.ico HTTP/1.1
    Host: dev.example.com
    GET / HTTP/1.1
    Host: dev.example.com

提取 HTTP POST 请求中的密码

从 HTTP POST 请求中提取密码和主机名:

$ tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:"

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:25:54.799014 IP 10.10.1.30.39224 > 10.10.1.125.80: Flags [P.], seq 1458768667:1458770008, ack 2440130792, win 704, options [nop,nop,TS val 461552632 ecr 208900561], length 1341: HTTP: POST /wp-login.php HTTP/1.1
.....s..POST /wp-login.php HTTP/1.1
Host: dev.example.com
.....s..log=admin&pwd=notmypassword&wp-submit=Log+In&redirect_to=http%3A%2F%2Fdev.example.com%2Fwp-admin%2F&testcookie=1

提取 Cookies

提取 Set-Cookie(服务端的 Cookie)和 Cookie(客户端的 Cookie):

$ tcpdump -nn -A -s0 -l | egrep -i 'Set-Cookie|Host:|Cookie:'

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp58s0, link-type EN10MB (Ethernet), capture size 262144 bytes
Host: dev.example.com
Cookie: wordpress_86be02xxxxxxxxxxxxxxxxxxxc43=admin%7C152xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxfb3e15c744fdd6; _ga=GA1.2.21343434343421934; _gid=GA1.2.927343434349426; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_86be654654645645645654645653fc43=admin%7C15275102testtesttesttestab7a61e; wp-settings-time-1=1527337439

抓取 ICMP 数据包

查看网络上的所有 ICMP 数据包:

$ tcpdump -n icmp

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:34:21.590380 IP 10.10.1.217 > 10.10.1.30: ICMP echo request, id 27948, seq 1, length 64
11:34:21.590434 IP 10.10.1.30 > 10.10.1.217: ICMP echo reply, id 27948, seq 1, length 64
11:34:27.680307 IP 10.10.1.159 > 10.10.1.1: ICMP 10.10.1.189 udp port 59619 unreachable, length 115

抓取非 ECHO/REPLY 类型的 ICMP 数据包

通过排除 echo 和 reply 类型的数据包使抓取到的数据包不包括标准的 ping 包:

$ tcpdump 'icmp[icmptype] != icmp-echo and icmp[icmptype] != icmp-echoreply'

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:37:04.041037 IP 10.10.1.189 > 10.10.1.20: ICMP 10.10.1.189 udp port 36078 unreachable, length 156

抓取 SMTP/POP3 协议的邮件

可以提取电子邮件的正文和其他数据。例如,只提取电子邮件的收件人:

$ tcpdump -nn -l port 25 | grep -i 'MAIL FROM\|RCPT TO'

抓取 NTP 服务的查询和响应

$ tcpdump dst port 123

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
21:02:19.112502 IP test33.ntp > 199.30.140.74.ntp: NTPv4, Client, length 48
21:02:19.113888 IP 216.239.35.0.ntp > test33.ntp: NTPv4, Server, length 48
21:02:20.150347 IP test33.ntp > 216.239.35.0.ntp: NTPv4, Client, length 48
21:02:20.150991 IP 216.239.35.0.ntp > test33.ntp: NTPv4, Server, length 48

抓取 SNMP 服务的查询和响应

通过 SNMP 服务,渗透测试人员可以获取大量的设备和系统信息。在这些信息中,系统信息最为关键,如操作系统版本、内核版本等。使用 SNMP 协议快速扫描程序 onesixtyone,可以看到目标系统的信息:

$ onesixtyone 10.10.1.10 public

Scanning 1 hosts, 1 communities
10.10.1.10 [public] Linux test33 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64

可以通过 tcpdump 抓取 GetRequest 和 GetResponse:

$ tcpdump -n -s0  port 161 and udp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp58s0, link-type EN10MB (Ethernet), capture size 262144 bytes
23:39:13.725522 IP 10.10.1.159.36826 > 10.10.1.20.161:  GetRequest(28)  .1.3.6.1.2.1.1.1.0
23:39:13.728789 IP 10.10.1.20.161 > 10.10.1.159.36826:  GetResponse(109)  .1.3.6.1.2.1.1.1.0="Linux testmachine 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64"

切割 pcap 文件

当抓取大量数据并写入文件时,可以自动切割为多个大小相同的文件。例如,下面的命令表示每 3600 秒创建一个新文件 capture-(hour).pcap,每个文件大小不超过 200*1000000 字节:

$ tcpdump -w /tmp/capture-%H.pcap -G 3600 -C 200

这些文件的命名为 capture-{1-24}.pcap,24 小时之后,之前的文件就会被覆盖。

抓取 IPv6 流量

可以通过过滤器 ip6 来抓取 IPv6 流量,同时可以指定协议如 TCP:

$ tcpdump -nn ip6 proto 6 从之前保存的文件中读取 IPv6 UDP 数据报文:

$ tcpdump -nr ipv6-test.pcap ip6 proto 17

检测端口扫描

在下面的例子中,你会发现抓取到的报文的源和目的一直不变,且带有标志位 [S] 和 [R],它们与一系列看似随机的目标端口进行匹配。当发送 SYN 之后,如果目标主机的端口没有打开,就会返回一个 RESET。这是 Nmap 等端口扫描工具的标准做法。

$ tcpdump -nn

21:46:19.693601 IP 10.10.1.10.60460 > 10.10.1.199.5432: Flags [S], seq 116466344, win 29200, options [mss 1460,sackOK,TS val 3547090332 ecr 0,nop,wscale 7], length 0
21:46:19.693626 IP 10.10.1.10.35470 > 10.10.1.199.513: Flags [S], seq 3400074709, win 29200, options [mss 1460,sackOK,TS val 3547090332 ecr 0,nop,wscale 7], length 0
21:46:19.693762 IP 10.10.1.10.44244 > 10.10.1.199.389: Flags [S], seq 2214070267, win 29200, options [mss 1460,sackOK,TS val 3547090333 ecr 0,nop,wscale 7], length 0
21:46:19.693772 IP 10.10.1.199.389 > 10.10.1.10.44244: Flags [R.], seq 0, ack 2214070268, win 0, length 0
21:46:19.693783 IP 10.10.1.10.35172 > 10.10.1.199.1433: Flags [S], seq 2358257571, win 29200, options [mss 1460,sackOK,TS val 3547090333 ecr 0,nop,wscale 7], length 0
21:46:19.693826 IP 10.10.1.10.33022 > 10.10.1.199.49153: Flags [S], seq 2406028551, win 29200, options [mss 1460,sackOK,TS val 3547090333 ecr 0,nop,wscale 7], length 0
21:46:19.695567 IP 10.10.1.10.55130 > 10.10.1.199.49154: Flags [S], seq 3230403372, win 29200, options [mss 1460,sackOK,TS val 3547090334 ecr 0,nop,wscale 7], length 0
21:46:19.695590 IP 10.10.1.199.49154 > 10.10.1.10.55130: Flags [R.], seq 0, ack 3230403373, win 0, length 0
21:46:19.695608 IP 10.10.1.10.33460 > 10.10.1.199.49152: Flags [S], seq 3289070068, win 29200, options [mss 1460,sackOK,TS val 3547090335 ecr 0,nop,wscale 7], length 0
21:46:19.695622 IP 10.10.1.199.49152 > 10.10.1.10.33460: Flags [R.], seq 0, ack 3289070069, win 0, length 0
21:46:19.695637 IP 10.10.1.10.34940 > 10.10.1.199.1029: Flags [S], seq 140319147, win 29200, options [mss 1460,sackOK,TS val 3547090335 ecr 0,nop,wscale 7], length 0
21:46:19.695650 IP 10.10.1.199.1029 > 10.10.1.10.34940: Flags [R.], seq 0, ack 140319148, win 0, length 0
21:46:19.695664 IP 10.10.1.10.45648 > 10.10.1.199.5060: Flags [S], seq 2203629201, win 29200, options [mss 1460,sackOK,TS val 3547090335 ecr 0,nop,wscale 7], length 0
21:46:19.695775 IP 10.10.1.10.49028 > 10.10.1.199.2000: Flags [S], seq 635990431, win 29200, options [mss 1460,sackOK,TS val 3547090335 ecr 0,nop,wscale 7], length 0
21:46:19.695790 IP 10.10.1.199.2000 > 10.10.1.10.49028: Flags [R.], seq 0, ack 635990432, win 0, length 0

过滤 Nmap NSE 脚本测试结果

本例中 Nmap NSE 测试脚本 http-enum.nse 用来检测 HTTP 服务的合法 URL。

在执行脚本测试的主机上:

$ nmap -p 80 --script=http-enum.nse targetip

在目标主机上:

$ tcpdump -nn port 80 | grep "GET /"

GET /w3perl/ HTTP/1.1
GET /w-agora/ HTTP/1.1
GET /way-board/ HTTP/1.1
GET /web800fo/ HTTP/1.1
GET /webaccess/ HTTP/1.1
GET /webadmin/ HTTP/1.1
GET /webAdmin/ HTTP/1.1

抓取 DNS 请求和响应

向 Google 公共 DNS 发起的出站 DNS 请求和 A 记录响应可以通过 tcpdump 抓取到:

$ tcpdump -i wlp58s0 -s0 port 53

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp58s0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:19:06.879799 IP test.53852 > google-public-dns-a.google.com.domain: 26977+ [1au] A? play.google.com. (44)
14:19:07.022618 IP google-public-dns-a.google.com.domain > test.53852: 26977 1/0/1 A 216.58.203.110 (60)

抓取 HTTP 有效数据包

抓取 80 端口的 HTTP 有效数据包,排除 TCP 连接建立过程的数据包(SYN / FIN / ACK):

$ tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

将输出内容重定向到 Wireshark

通常 Wireshark(或 tshark)比 tcpdump 更容易分析应用层协议。一般的做法是在远程服务器上先使用 tcpdump 抓取数据并写入文件,然后再将文件拷贝到本地工作站上用 Wireshark 分析。

还有一种更高效的方法,可以通过 ssh 连接将抓取到的数据实时发送给 Wireshark 进行分析。以 MacOS 系统为例,可以通过 brew cask install wireshark 来安装,然后通过下面的命令来分析:

$ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - not port 22' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - 例如,如果想分析 DNS 协议,可以使用下面的命令:

$ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - port 53' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i -

抓取到的数据:

image

-c 选项用来限制抓取数据的大小。如果不限制大小,就只能通过 ctrl-c 来停止抓取,这样一来不仅关闭了 tcpdump,也关闭了 wireshark。

找出发包最多的 IP 找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 IP,可以使用下面的命令:

$ tcpdump -nnn -t -c 200 | cut -f 1,2,3,4 -d '.' | sort | uniq -c | sort -nr | head -n 20

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
200 packets captured
261 packets received by filter
0 packets dropped by kernel
    108 IP 10.10.211.181
     91 IP 10.10.1.30
      1 IP 10.10.1.50

cut -f 1,2,3,4 -d ‘.' : 以 . 为分隔符,打印出每行的前四列。即 IP 地址。 sort | uniq -c : 排序并计数 sort -nr: 按照数值大小逆向排序

抓取用户名和密码

本例将重点放在标准纯文本协议上,过滤出于用户名和密码相关的报文:

$ tcpdump port http or port ftp or port smtp or port imap or port pop3 or port telnet -l -A | egrep -i -B5 'pass=|pwd=|log=|login=|user=|username=|pw=|passw=|passwd=|password=|pass:|user:|username:|password:|login:|pass |user '

抓取 DHCP 报文

最后一个例子,抓取 DHCP 服务的请求和响应报文,67 为 DHCP 端口,68 为客户机端口。

$ tcpdump -v -n port 67 or 68

tcpdump: listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:37:50.059662 IP (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328)
    0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:0c:xx:xx:xx:d5, length 300, xid 0xc9779c2a, Flags [none]
      Client-Ethernet-Address 00:0c:xx:xx:xx:d5
      Vendor-rfc1048 Extensions
        Magic Cookie 0x63825363
        DHCP-Message Option 53, length 1: Request
        Requested-IP Option 50, length 4: 10.10.1.163
        Hostname Option 12, length 14: "test-ubuntu"
        Parameter-Request Option 55, length 16: 
          Subnet-Mask, BR, Time-Zone, Default-Gateway
          Domain-Name, Domain-Name-Server, Option 119, Hostname
          Netbios-Name-Server, Netbios-Scope, MTU, Classless-Static-Route
          NTP, Classless-Static-Route-Microsoft, Static-Route, Option 252
14:37:50.059667 IP (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328)
    0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:0c:xx:xx:xx:d5, length 300, xid 0xc9779c2a, Flags [none]
      Client-Ethernet-Address 00:0c:xx:xx:xx:d5
      Vendor-rfc1048 Extensions
        Magic Cookie 0x63825363
        DHCP-Message Option 53, length 1: Request
        Requested-IP Option 50, length 4: 10.10.1.163
        Hostname Option 12, length 14: "test-ubuntu"
        Parameter-Request Option 55, length 16: 
          Subnet-Mask, BR, Time-Zone, Default-Gateway
          Domain-Name, Domain-Name-Server, Option 119, Hostname
          Netbios-Name-Server, Netbios-Scope, MTU, Classless-Static-Route
          NTP, Classless-Static-Route-Microsoft, Static-Route, Option 252
14:37:50.060780 IP (tos 0x0, ttl 64, id 53564, offset 0, flags [none], proto UDP (17), length 339)
    10.10.1.1.67 > 10.10.1.163.68: BOOTP/DHCP, Reply, length 311, xid 0xc9779c2a, Flags [none]
      Your-IP 10.10.1.163
      Server-IP 10.10.1.1
      Client-Ethernet-Address 00:0c:xx:xx:xx:d5
      Vendor-rfc1048 Extensions
        Magic Cookie 0x63825363
        DHCP-Message Option 53, length 1: ACK
        Server-ID Option 54, length 4: 10.10.1.1
        Lease-Time Option 51, length 4: 86400
        RN Option 58, length 4: 43200
        RB Option 59, length 4: 75600
        Subnet-Mask Option 1, length 4: 255.255.255.0
        BR Option 28, length 4: 10.10.1.255
        Domain-Name-Server Option 6, length 4: 10.10.1.1
        Hostname Option 12, length 14: "test-ubuntu"
        T252 Option 252, length 1: 10
        Default-Gateway Option 3, length 4: 10.10.1.1

总结

本文主要介绍了 tcpdump 的基本语法和使用方法,并通过一些示例来展示它强大的过滤功能。将 tcpdump 与 wireshark 进行组合可以发挥更强大的功效,本文也展示了如何优雅顺滑地结合 tcpdump 和 wireshark。如果你想了解更多的细节,可以查看 tcpdump 的 man 手册。

bingoohuang commented 4 years ago

TCPDUMP CHEATSHEET

SOURCE

Installation Commands

OS Installation Commands
CENT OS and REDHAT $ sudo yum install tcpdump
Fedora $ dnf install tcpdump
Ubuntu, Debian and Linux Mint #apt-get install tcpdump

Packet Capturing Options

Switch Syntax Description
-i any tcpdump -i any Capture from all interfaces
-i eth0 tcpdump -i eth0 Capture from specific interface ( Ex Eth0)
-c tcpdump -i eth0 -c 10 Capture first 10 packets and exit
-D tcpdump -D Show available interfaces
-A tcpdump -i eth0 -A Print in ASCII
-w tcpdump -i eth0 -w tcpdump.txt To save capture to a file
-r tcpdump -r tcpdump.txt Read and analyze saved capture file
-n tcpdump -n -I eth0 Do not resolve host names
-nn tcpdump -n -i eth0 Stop Domain name translation and lookups (Host names or port names )
tcp tcpdump -i eth0 -c 10 -w tcpdump.pcap tcp Capture TCP packets only
port tcpdump -i eth0 port 80 Capture traffic from a defined port only
host tcpdump host 192.168.1.100 Capture packets from specific host
net tcpdump net 10.1.1.0/16 Capture files from network subnet
src tcpdump src 10.1.1.100 Capture from a specific source address
dst tcpdump dst 10.1.1.100 Capture from a specific destination address
\<service> tcpdump http Filter traffic based on a port number for a service
\<port> tcpdump port 80 Filter traffic based on a service
port range tcpdump portrange 21-125 Filter based on port range
-S tcpdump -S http Display entire packet
ipv6 tcpdunp -IPV6 Show only IPV6 packets
-d tcpdump -d tcpdump.pcap display human readable form in standard output
-F tcpdump -F tcpdump.pcap Use the given file as input for filter
-I tcpdump -I eth0 set interface as monitor mode
-L tcpdump -L Display data link types for the interface
-N tcpdump -N tcpdump.pcap not printing domian names
-K tcpdump -K tcpdump.pcap Do not verify checksum
-p tcpdump -p -i eth0 Not capturing in promiscuous mode

Logical Operators

Operator Syntax Example Description
AND and, && tcpdump -n src 192.168.1.1 and dst port 21 Combine filtering options
OR or, || tcpdump dst 10.1.1.1 && !icmp Either of the condition can match
EXCEPT not, ! tcpdump dst 10.1.1.1 and not icmp Negation of the condition
LESS < tcpdump <32 Shows packets size less than 32
GREATER > tcpdump >=32 Shows packets size greater than 32

Display / Output Options

Switch Description
-q Quite and less verbose mode display less details
-t Do not print time stamp details in dump
-v Little verbose output
-vv More verbose output
-vvv Most verbose output
-x Print data and headers in HEX format
-xx Print data with link headers in HEX format
-X Print output in HEX and ASCII format excluding link headers
-XX Print output in HEX and ASCII format including link headers
-e Print Link (Ethernet) headers
-S Print sequence numbers in exact format

Protocols

Ether, fddi, icmp ,ip, ip6 , ppp, radio, rarp, slip, tcp , udp, wlan

Common Commands with Protocols for Filtering Captures

Commands Meaning
src/ dsthost (host name or IP) Filter by source or destination IP address or host
ether src/ dst host (ethernet host name or IP) Ethernet host filtering by source or destination
src/ dstnet (subnet mask in CIDR) Filter by subnet
tcp/udp src/dst port ( port number) Filter TCP or UDP packets by source or destination port
tcp/udp src/dst port range ( port number range) Filter TCP or UDP packets by source or destination port range
ether/ip broadcast Filter for Ethernet or IP broadcasts
ether/ip multicast Filter for Ethernet or IP multicasts