1.2 Web流量的抓取方式
20世纪80年代,Tim Berners-Lee创造了万维网并在1991年使用HTTP编写了第一个网站。到如今,HTTP依然是互联网的主要流量。抓取并分析Web流量对于安全分析有重要意义。本节主要介绍HTTP流抓取的基础原理和快速实践。
1.2.1 TCP流还原
HTTP是一个资源请求响应的协议,通常工作在TCP之上,这里主要讨论HTTP 1.0和HTTP 1.1[1]。
要抓取HTTP流量,首先要进行TCP流的抓取。在上一节中我们已经探讨了流量的抓取方式。从libpcap直接抓取到的数据是一个以太网帧,以太网帧前14个字节分别是目标MAC地址、源MAC地址、两个字节的TYPE类型;类型号0x0800表明承载的网络层协议是一个IP,如图1-4所示。
图1-4 流量示例
IP头中协议号字段为6,表明IP承载的是传输层协议(TCP),如图1-5所示。
图1-5 TCP示例
TCP的payload部分即为应用层数据;不同于以太网帧和IP,TCP头中没有标识HTTP的专有字段,如图1-6所示。
不过HTTP头特征明显,可以很容易地识别出来。
HTTP1.1头总是为[METHOD][URL]HTTP/1.1\r\n。其中METHOD有HEAD/GET/POST/PUT/DELETE/TRACE/OPTIONS/PATCH/CONNECT等,每个方法的具体说明可以参考RFC 2616,此处不再赘述。
图1-6 HTTP
TCP是C/S结构,流还原时最重要的概念是四元组,即客户端IP、客户端端口、服务端IP、服务端端口。TCP是流传输的,支持全双工。一个四元组确定一条流一组双向通信。捕获到TCP流之后再按照TCP的seq序号将其安装顺序组装成TCP流即完成TCP流还原。
如果使用C语言开发,推荐使用libndis(Library Network Intusion Detection System)。libndis是基于libpcap抓包的一个网络入侵检测系统C库。其支持TCP流还原,大致调用流程类似于libpcap,自带例子里有多种使用场景。这里只简单介绍TCP流还原的基础。
在完成libnids初始化之后,使用nids_register_tcp注册一个TCP捕获的回调函数,其函数原型为:
#接受一个回调函数为参数 void nids_register_tcp (void (*)); #回调函数原型 #ns为TCP流结构 #param可以用来指向某些临时数据 void callback(struct tcp_stream *ns, void **param)
根据ns->nids_state标记了TCP流的建立连接、传输数据、断开等多个状态。在数据状态为NIDS_DATA时,ns->client.count_new值为0,表示服务端发送数据,值为非0,表示客户端发送数据。TCP流还原步骤简单总结如下:
·接收到TCP流后建立连接状态NIDS_JUST_EST,创建四元组hash缓存。
·接收到TCP数据NIDS_DATA,将数据按照四元组hash缓存,并重复此过程,直到接收到其他状态。
·接收到其他状态,取出指定四元组数据即为一组完整TCP流。
[1] Google为解决HTTP 1.1的不足而提出的QUIC协议,即HTTP-over-QUIC就是基于UDP的,IETF将HTTP-over-QUIC重命名为HTTP/3,为最新版本的HTTP。
1.2.2 HTTP
在上面我们得到了还原好的TCP流,接下来需要将TCP流解析成HTTP。
这里推荐使用http_parser。http-parser是Node.js使用的HTTP解析C库[1],用户数量巨大,代码质量和效率经得住考验。如果使用C调用,核心方式也是回调函数。将得到的TCP流数据传递给http_parser_execute函数,在http_parser_settings的回调函数中即可得到header、url、body等HTTP字段内容。
使用http_parser_execute所需要传输的数据就是上面NIDS_DATA中捕获到的TCP数据。
在使用http_parser进行HTTP解析时,可以先进行简单的HTTP过滤,例如,可以先用接收到的第一份客户端请求数据来判断是否是[METHOD][URL]HTTP/1.1\r\n形式。
[1] 大多数语言都支持调用C库,http_parser可以被Node.js调用,也可以封装成Python扩展包。
1.2.3 使用packetbeat抓取网络流量
1.1节中,我们介绍了scapy和gopacket,这一节中我们介绍gopacket的成品软件packetbeat。packetbeat是Elastic公司ELK Stack里的重要组成成员,是Beats系列[1]软件中的抓包软件。使用此开源软件配合ELK可以快速实现Web流量可视化,同时packetbeat也更适用于云环境无法对流量进行镜像的情况。
1.下载安装packetbeat
在官方网站下载并安装:
curl -L -0 https://artifacts.elastic.co/downloads/beats/packetbeat/ packetbeat-7.4.0-linux-x86_64.tar.gz tar xzvf packetbeat-7.4.0-linux-x86_64.tar.gz
Linux环境中推荐使用CentOS 7以上版本。
2.配置packetbeat
解压之后的目录下有默认的配置文件packetbeat.yml,关键字段说明如下。
抓包网卡名称,any表示抓取全部,示例如下:
packetbeat.interfaces.device: any
抓包snaplen长度,在没有巨帧的情况下用默认值即可,示例如下:
packetbeat.interfaces.snaplen: 1514
抓包类型pcap/af_packet,af_packet模式更为高效,建议按如下方式使用:
packetbeat.interfaces.type: af_packet
抓包时使用的内存缓存区大小,根据流量大小情况调整,示例如下:
packetbeat.interfaces.buffer_size_mb: 100
抓包协议和端口,协议填写http,端口根据实际情况填写,示例如下:
packetbeat.protocols: - type: http ports: [80, 8080, 8081, 5000, 8002]
输出:
output.XXX
详细配置请查看官方文档https://www.elastic.co/guide/en/beats/packetbeat/master/configuring-howto-packetbeat.html。
packetbeat支持多种输出目标,包括Elasticsearch/Logstash/kafka/Redis/File/Console/Cloud,根据实际情况进行配置。其中输出到Elasticsearch中时需要配置Kibana,packetbeat会自动创建可视化的指标。
3.启动程序
packetbeat运行需要使用root权限,示例如下:
sudo ./packetbeat -e
[1] Beats系列软件是使用Go开发的全品类收集器,可以用于所有数据类型,以性能高和数据类型全著称。
1.2.4 其他方案
理论上讲,任何一种NIDS都应当具备流还原的能力,如Bro、Snort、Suricata等。研究这些开源项目可以较快速地得到想要的Web流量抓取方案。
1.2.5 一些常见问题
下面总结了抓取Web流量时遇到的一些常见问题,希望对读者有所裨益。
1.gzip/zlib/deflate压缩编码
抓包时首先解析header中的Content-Encoding,根据内容进行解压缩。注意解压缩后的数据如果依然要通过json等明文协议进行传输,建议使用Base64编码。
2.标准头和自定义头
Web网络流量来自互联网,HTTP头部数据多种多样,而且HTTP允许自定义头,有一些约定俗成的非标准头正被使用。关于标准头和常见自定义头请参考https://en.wikipedia.org/wiki/List_of_HTTP_header_fields。众多的HTTP头会导致在存储和分析时增加不必要的麻烦,特别是像Elasticsearch等文档数据库,字段的数量对性能有很大影响,这里建议在程序中保留标准头部和常用头部,将其他头部合并到一个字段中。
3.转义
packetbeat会将抓取到的数据序列化为JSON并发送给output,而在JSON序列化过程中,为了防止JSON内容以HTML的方式输出时转义而造成XSS漏洞,会默认将&、<、>进行转义。其他语言中也有类似的情况,转义后的内容可能对我们的分析造成不便。
如果需要,可以修改JSON序列化函数,或者在分析前将转义还原。
4.消息体的大小和存储
我们收集的Web流量总是要进行传输、存储之后才能进行分析。常见的Kafka消息队列默认接受1MB以内的消息体,超过的消息将会被丢弃。如果发现大的请求无法获取到,可能是被消息中间件丢弃了。可以在消息体压缩和修改消息队列最大消息体之间做一个平衡。此外,对于网络安全分析人员来说,对于占请求量较大的静态文件,可以直接丢弃响应体。过滤方式是可以先过滤URL中的扩展名,再过滤header中的Content-Type。如果使用packetbeat,则可以使用processors机制来进行过滤。
5.SSL证书卸载
大多数抓包软件无法直接解析HTTPS,在理论上只有使用RSA交换密钥的方式才可以通过旁路流量解析,而Diffie-Hellman方式无法解析。所以应当在网络设备,如F5或者应用nginx卸载证书之后再进行抓包。
6.HTTP chunk
有时候,Web服务器生成HTTP Response是无法在Header中就确定消息大小的,这时服务器一般将不会提供Content-Length的头信息,而是采用Chunked编码动态地提供body内容的长度。
7.监控丢包(流)率
有抓包总会有丢包,丢包率比较好统计。一个TCP流中,丢失一个包可能会导致一条流的还原失败。从镜像流量到网卡抓包,再到消息传输过程,都可能造成丢包。要解决这一问题,一个简单的方案是编写模拟程序,周期性地以固定频率请求某特定网站,加入特殊标记,例如自定义特殊UserAgent;在抓包之后统计抓到的数量,进行抽样,就得到了整体的丢包(流)率。