一、参考
Bro: A System for Detecting Network Intruders in Real-Time
二、名词解释
FDDI,fiber distributed data interface, 光纤分布式数据接口
DMZ,demilitarized zone, 隔离区,两个防火墙之间的空间
AST, abstact syntax tree, 抽象语法树
三、正文
1. 简介
随着互联网连接的不断增长,攻击者通过网络非法访问计算的机会也越来越多。检测这类攻击问题称之为网络入侵检测,一个相对较新的安全研究领域,可以将这些系统分为下列两类:
(1)依赖于试图保护的网络中的主机收集到的审计信息;
(2)直接独立监控网络流量,被动的通过数据报
现在,人们对于两类系统结合起来的混合系统更感兴趣
下面将介绍第二类系统,创建一个独立的监控网络流量的系统,称为监视器,虽然,监视器要面对比访问审计跟踪的系统更加有限的系统的困难,但是,监视器可以被添加到网络中,不需要对主机进行任何修改。同时,监控数千个不同管理的主机集合,是监视器的一个巨大优势。
监视器系统命名为bro,现在已经重命名为zeek, 在开发zeek之前,基于离线tcpdump数据报的分析经验,制定了zeek的设计目标和要求:
(1)高速、大容量监控,对于常见场景,我们将外部主机通过网络可以访问的主机当作最大的安全威胁来源,由于我们要保护的网络中,只有一部分连接到公有网络中的主机(在隔离区中),可以通过被动的监视这一部分主机的链路层网络流量进行网络保护,因为,链路层中采用FDDI环,所以,监控系统也需要实现高达100M/s的捕获流量速率;
(2)没有数据报过滤丢弃,如果数据报筛选器的应用程序无法在数据报到达受监视链路时候,尽快的消费,过滤器将缓存数据报以供之后消费,但是,最终,过滤器将耗尽缓冲区,从而丢弃更多到达的数据报,从安全监控的角度,数据报的丢弃将会破坏监视器,因为,丢弃的数据报可能包含有攻击信息,因此,避免数据报被过滤掉是另外一个要求;
(3)实时告警,在最初离线系统分析中,检测到攻击之前,会有长时间的延迟,如果快速检测到攻击或者试图进行的攻击,则可以更加容易的追踪攻击者,将损害降至最低,防止进一步的入侵,并启动对攻击者发起的网络活动的完整记录,因此,对于zeek,一个要求就是可以实时检测攻击。这并不是说,保存大量、永久性的网络活动日志的作用不大,通常,当我们检测到攻击,将会使用这些日志进行回顾性的损害评估,有时会回溯几个月前的日志;
(4)机制与安全策略的分离,良好的软件设计往往强调在机制和策略之间建立清晰的分离,有助于提高系统的简洁和灵活性,对于zeek系统,两者分离更有必要性:因为我们处理的网络流量非常大,需要能够在不同时间,考虑过滤、检查策略,对不同类型流量进行响应,而如果将这些响应硬编码到系统中,将会使得后期维护更加麻烦和更容易出错;
(5)扩展性,因为网络攻击非常多,更新速度快,需要不断更新攻击检测规则,因此,我们需要能够以较小的易于调试的增量去升级zeek系统;
(6)避免简单错误,我们总是想避免错误,特别希望网站定义安全、清晰的策略,所以我们不考虑使用C代码来表示策略;
(7)监视器本身可能被攻击,假设,攻击者完全了解监视器使用的技术,可以访问监视器的源代码,并且利用这些知识试图攻击监视器,导致其无法检测到攻击者的入侵活动,这个假设,使得监视器的设计更加复杂化,但是这是必须要解决的问题。我们可以进一步假设,监视器只会从一端收到攻击,也就是说,假设主机A,B之间存在网络连接,则最多有一个主机(A或者B)可能被破坏,可能会试图攻击监视器,这个假设极大的帮助我们处理监视器被攻击问题,因为它意味着我们可以信任其中的一个端点(尽管我们不知道是哪一个端点)。如果主机A和B都被破坏了,那么攻击者可以在两者之间建立复杂的秘密通道,这意味着,监视器无法察觉到威胁,取决于通道的迂回程度。
下文将介绍,zeek系统如何实现上面的目标,
第二章,介绍整个系统的结构框架,
第三章,介绍zeek系统的专用脚本语言zeek,
第四章,讲述现在zeek系统的实现方式,
第五章,讨论对于监视器本身的攻击方式,
第六章,分析zeek系统为6个常用应用层程序实现的检测规则,
第七章,包含我们对于zeek系统的简单评估,
第八章,对zeek系统未来的展望。
2. 系统结构
zeek在概念上可以分为一个事件引擎和一个解释器,其中,事件引擎用于将数据报数据流减少为更为高级抽象的网络事件流,解释器用于解释zeek语言编写的安全策略,
更为一般的,zeek系统可以看成是分层结构,最底层处理最大数量的数据报,因此,底层对于数据报的操作最少,越往上层结构,数据流将变得越少,对于数据的处理越多。这种基本设计反映了尽可能节省处理时间的需求,即在不丢包的情况下,监控高速、大流量的数据的需求。
2.1 libpcap
从分层结构图,可知网络层上面是libpcap(数据报捕获库,tcpdump程序使用),使用libpcap有下面的优势:
(1)将zeek和网络链接技术的细节(以太网、FDDI、SLIP等)隔离;
(2)有助于zeek程序在不同的unix变体中移植(升级到更高性能的硬件);
(3)可以直接对tcpdump保存文件进行操作,使得离线开发、分析变得更加容易;
(4)如果主机的操作系统提供了足够强大的内核包过滤器,例如BPF,则libpcap将下载用于减少内核流量的过滤器;因此,不必将每个数据报发送到用户级进程,然后再丢弃数据报(过滤器只接收少量流量),可以在内核中丢弃被过滤的数据报,不必进行上下文切换或者数据复制;
当使用包过滤器,必须设置快照长度参数,该长度决定了每个数据报的捕获长度,例如:tcpdump使用68字节的快照长度,它足以捕获到链路层和TCP/IP数据报头部,但是通常会丢弃包中的大部分数据,
快照长度越小,每个接收的数据报需要通过包过滤器复制到用户级别的数据就越少,有助于加快数据报处理和避免丢失;另一方面,为了在应用程序级别分析连接,zeek需要每个数据报的完整数据内容,因此,设置快照长度以捕获整个数据报。
2.2 事件引擎
过滤后的数据报流被传递到zeek系统的事件引擎层,该层首先执行几个完整性检查,确保数据报头部格式正确(检验IP头校验和),如果检查失败,zeek产生一个报错事件,丢弃该数据报。在检验时候,zeek同时重新组装IP分片,得到完整的IP数据报。
如果检查成功,则事件引擎将查找与两个IP地址、两个TCP端口号(或者两个UDP端口号)的元组关联的连接状态,如果不存在,则创建新的连接状态。然后,它将数据报分派给相应连接的处理程序。zeek维护一个tcpdump追踪文件,该文件与通信量相关联,连接处理程序在返回时告知引擎(1)是否应该将整个数据报记录到追踪文件中,(2)仅仅记录数据报头部,(3)根本不记录任何内容,这些分类权衡了流量跟踪的完整性和其生成追踪文件的大小与时间。
通常,如果zeek分析了整个数据报,则会记录完整的数据报;如果只分析了数据报的SYN/FIN/RST计算值,则仅仅记录数据报头部,如果数据报没有做任何处理,则跳过记录
下面,将概述对于TCP和UDP数据报所做的一般处理,这两种场景,都以调用处理程序处理数据报的数据负载而结束,对于zeek内置的应用层程序,将继续分析,对于不支持的应用程序,分析将在此时终止。
TCP处理程序:对于每个TCP数据报,连接处理函数(C++虚函数)验证(1)TCP头部存在(2)验证数据报头部和负载部分的校验和,如果成功,继续测试TCP头部中是否包含任何SYN/FIN/RST控制标志,如果包含,则对应调整连接的状态,最终,处理数据报头部中数据确认,调用处理程序处理有效载荷数据。连接状态的不同更改将会生成不同的事件,
(1)当看到请求连接的初始SYN包,事件引擎将定义计时器(目前为5分钟);
(2)如果计时器过期且连接状态没有更改,则事件引擎生成连接尝试事件(connetion_attempt);
(3)如果在计时器的有效时间,另一端使用正确的SYN确认数据报,进行响应,引擎立即生成建立连接事件(connection_established),同时取消计时器;如果在计时器的有效时间,另一端使用RST数据报进行响应,则连接尝试被拒绝,引擎立即生成连接拒绝事件(connection_rejected);
(4)如果连接正常终止,通过FIN标志,引擎生成连接完成事件(connection_finished)
(5)其他事件,反映连接终止的各种不同方式
UDP处理程序:UDP处理程序类似于TCP处理程序,但是更加简单,除了下面场景:
如果主机A向主机B发送一个源端口为PA,目标端口为PB的UDP数据报,则Zeek认为主机A向主机B发起了“请求”,建立与该请求的伪连接状态。如果主机B通过端口PB发送了一个UDP数据报到主机A的PA端口,则zeek认为该数据报反映了UDP请求的响应,UDP有效载荷数据的处理程序(虚拟函数)可以通过UDP通信的该模式,快速区分UDP请求与UDP响应,UDP请求和UDP响应分别生成udp_request事件和udp_reply事件。
2.3 策略脚本解释器
当事件引擎处理完数据报后,检查是否产生了事件,事件保存在队列(先进先出)中,如果队列中存在事件,则依次处理事件,还需要检查是否有任何计时器事件已经过期,如果过期,需要执行对应的处理程序
zeek设计的一个关键方面是明确区分事件的生成和处理,策略脚本解释器用于执行zeek语言编写的程序,zeek脚本指定了事件的处理程序,这些处理程序本质上和zeek函数相同,只是脚本程序没有返回值。对于每一个传递给解释器的事件,解释器检索对应的处理程序(半编译的代码),将事件的值映射到处理程序的参数,解释脚本语言。被解释的zeek程序可以执行任意的zeek脚本命令,包含:产生新事件,实时记录通知(unix中syslog函数),将数据记录到磁盘或者修改事件内部状态以便于随后调用事件处理程序(或者事件引擎本身)。
最终,随着机制和策略的分离,zeek系统强调异步事件作为事件引擎和策略脚本解释器之间的连接,有助于提高系统的可扩展性。向zeek添加新的功能通常包括(1)向事件引擎添加新的协议分析器(2)为协议分析器生成的事件编写新的事件处理程序。新的协议分析器和事件处理程序,都和现有功能尽量少的功能重叠,因此在大多数情况下,可以避免松耦合的多个模块之间的交互,防止出现各种程序维护问题。
3. zeek语言
下面将介绍zeek语言的特色,而不是精确描述语言细节,第一章中提到的目标6(防止简单错误)对于zeek语言的设计影响非常大。
因为入侵检测是站点安全的基石,所以策略脚本语言zeek的设计非常重要。根据经验,避免意外的一个重要步骤是使用强类型语言,可以在编译时候避免类型不一致,并且保证运行时候所有变量的引用都是有效值。开始时候,我们使用tcpdump, awk, shell拼凑了一个监视器,但使用后,更加急于找寻直接处理主机名,ip地址,端口号等的方法,而不是设计ASCII编码的等价物。通过在zeek中设置了这些实体(主机名、端口号、ip地址)为第一类函数,增强了语言表达的易用性,并且由于是强类型,例如:IP地址类型和端口号类型的值的比较这类错误将被避免。
3.1 数据类型和常量
原子类型,zeek支持几种传统语言熟悉的类型:bool, int, count(非负整数), double,string
除了string,其余四种类型可以称之为算术类型
不同于C语言,zeek中的string在内部表示为字节的计数和向量,而不是以0结尾的字节序列。这个区别很重要,因为0在网络流量派生的字符串中非常容易被引入,引入可以通过应用程序、破坏监视器的攻击者恶意引入,例如:通过FTP服务器,
USER nice\\0USER root
接收到上面代码的FTP应用程序会将其理解为两个单独的命令:
USER nice
USER root
如果监视器程序使用0结尾的字符串,则其只能得到第一部分,
USER nice
可能导致无法检测到威胁行为。
类似的,当zeek记录这些字符串或者直接输出到文件中时候,将内置的0保留当作转义字符非常重要。
zeek针对特定的问题领域,还定义了一些非传统类型,
(1)time类型表示绝对时间,
(2)interval类型定义了时间差(两个time类型变量的差即是一个interval类型值;一个time类型值加上interval类型值,得到一个time类型值;两个time类型相加产生错误)
目前,没有time类型的常量,但是可以使用数字+时间单位结构指定interval类型常量(例如: 30 min表示30分钟间隔)
(3)port类型,表示TCP/UDP端口号,TCP和UDP端口是不同的,因此,port类型的变量可以保存TCP或者UDP端口,但是任何给定时间,它只能保存两者之一。
port类型常量有两种形式,第一种方式为一个非负数整数加上TCP或者UDP,例如:80/tcp 表示TCP协议端口号80(万维网使用的HTTP协议),第二种形式的常量使用预定义的标识符指定,例如:http(等价于80/tcp),最初,使用getservbyname
库查找未定义的标识符,但是,当单个名称同时具有TCP/UDP定义时候,将遇到问题
更加根本的是,这样会减弱zeek系统的可移植性,因为一个服务器已知的通过getservbyname
库查找的服务,可以在另一台服务器丢失,从而导致关于该服务名称编写的任何zeek脚本都无法生效。
port类型可以按照是否相同或者顺序大小进行比较,例如: 20/tcp < telnet
值为true
(4)addr类型,表示一个IP地址,它们在内部表示为无符号的32位整数,在zeek脚本中,只能对addr类型的变量执行相等或者不相等的比较操作,addr类型的值通常形式为A1.A2.A3.A4
(5)域名常量,没有对应于主机域名的zeek类型,域名之间无法进行比较操作,因为一个域名可能对应于多个IP地址,比较相等会产生歧义。任何由点分隔的多个(包含两个)标识符序列都构成了主机名常量,例如:lbl.gov
和google.com
都是主机域名常量。域名常量包含了一个或者多个IP地址组成的列表,在zeek表达式中无法使用这个IP地址列表,但是在初始化zeek table类型或者set类型时候,具有重要作用
下面将介绍多种聚合类型,
(6)record类型是任意类型元素的一个集合,例如,下面的预定义conn_id类型,用于保存连接标识符,在zeek运行时候初始化,定义如下:
type conn_id: record {
orig_h: addr;
orig_p: port;
resp_h: addr;
resp_p: port;
};
使用$
运算符访问record类型的字段值
(7)table类型,分为两个组成部分,构造一个映射关系,相当于python中的dict, 两个部分indices和yield相对于dict的key, value。indices的值是原子类型或者是record类型,例如:
table[port] of string
表示一组string类型值和port类型值的映射关系
table[conn_id] of ftp_session_info
表示由一个conn_id类型记录(或者等效于2个addr类型,2个port类型),可以生成一个ftp_session_info记录
(8)set类型,类似于table类型,只是没有yield部分,set类型的作用是维护元组的集合,用集合的索引表示
(9)file类型,对于file类型的支持还有待完善,但是脚本可以打开文件进行写入,将生成的文件作为参数传递给print命令,指定文件写入的位置等等。
(10)list类型,其中包含了一个值的0或者多个实例,目前,主要应用于脚本解释器中,暂不做过多描述
(11)pattern类型,模式类型是unix风格的正则表达式,特别是flex使用程序使用的语法。模式类型常量使用/分隔符包含,例如:
/sync|lp|uucp|operator|ezsetup|4dgifts/
目前,模式类型的值只有两个操作,赋值和测试给定string类型值是否匹配
3.2 操作符
3.3 变量
zeek支持两个级别的作用域,(1)对于函数或者事件处理程序是本地变量(2)对整个zeek脚本是全局变量,经验表明,我们还需要添加第三个中间级别的作用域(可能作为module或者object的一部分,或者像C语言中的static作用域)
局部(本地)变量使用关键字local声明,声明语句需要位于函数或者事件处理程序的主体内,不需要再函数开始声明变量,变量的生命周期为声明语句到函数结束(或者程序主体结束)
全局变量使用global关键字声明,声明语句需要定义在函数体外部,
对于局部/全局变量,都可以定义属性为const,表示变量的值是常量,不能更改。
语法结构的声明语句如下:
{class} {identifier} [':' {type}] ['=' {init}]
其中,class取值为local, global, const标识符,identifier表示变量名称,type表示可选类型, init表示可选的初始值,后面两个可选必须指定一个,如果两个可选项都指定,则初始化的类型和指定的类型要保持一致。如果只是指定type选项,没有初始化值,则需要在使用变量值时候,先初始化,否则运行时候产生错误。
如果只是定义了init选项,zeek将根据初始值判断变量的类型,例如:
const IRC = { 666/tcp, 6667/tcp, 6668/tcp };
表示IRC是一个set类型常量,组成元素是port类型值
const ftp_serv = { ftp.lbl.gov, www.lbl.gov };
表示ftp_serv是一个set类型常量,组成元素是域名常量(即addr类型的IP地址列表)
const allowed_services = {
[ftp.lbl.gov, ftp], [ftp.lbl.gov, smtp],
[ftp.lbl.gov, ident], [ftp.lbl.gov, 20/tcp],
[www.lbl.gov, ftp], [www.lbl.gov, smtp],
[www.lbl.gov, ident], [www.lbl.gov, 20/tcp],
[nntp.lbl.gov, nntp]
};
等价于
const allowed_services: set[addr, port] = {
[ftp.lbl.gov, [ftp, smtp, ident, 20/tcp]],
[www.lbl.gov, [ftp, smtp, ident, 20/tcp]],
[nntp.lbl.gov, nntp]
};
const allowed_services: set[addr, port] = {
[ftp_serv, [ftp, smtp, ident, 20/tcp]], [nntp.lbl.gov, nntp]
};
定义一个table类型的变量
global port_names = {
[7/tcp] = "echo",
[9/tcp] = "discard",
[11/tcp] = "systat",
};
除了简洁和清晰之外,zeek强调table类型和set类型的另一个优势为执行速度:
例如:判断是否允许访问主机H的服务S,常见判断如下:
if ( H == ftp.lbl.gov || H == www.lbl.gov )
if ( S == ftp || S == smtp || ... )
else if ( H == nntp.lbl.gov )
if ( S == nntp )
现在只需要使用
if ( [S, H] in allowed_services )
... it's okay ...
in操作符为哈希表的查找问题,避免了级联if判断
3.4 表达式
函数和事件处理程序的定义不同表现在,zeek允许事件处理程序有多个不同的定义,每当生成事件时候,处理程序的每个实例将依次调用(按照它们在脚本中出现的顺序),所以,不同的模块可以自定义zeek_init处理程序用于初始化,这将大大简化创建模块化事件处理程序集的任务。
4. 实现问题
zeek的事件引擎和脚本解释器都是使用C++编写的,大概27,000代码量,下面,将讨论一些重要的实现决策和权衡
(1)为什么使用C++?
使用C++是在已经实现的另一个脚本解释器Glish的经验上,得到的结论。C++的类层次结构很好的映射到协议层,从而简化了对于事件引擎和脚本解释器的扩展,目前还没有遇到性能问题
(2)单线程模式,
由于事件处理是系统的核心,因此考虑多线程设计是很自然的,每个活跃事件处理程序都有一个对应线程,但是目前为止,我们一直抵制这种做法,因为它可能会导致zeek脚本中的各种竞争错误。
单线程设计的一个重要结果是,在启动任何可能阻止等待资源的活动之前,系统必须警惕,因为事件引擎无法消耗传入的通信量,从而导致数据报筛选器的丢失。一个特别的场景是DNS解析,这可能需要数秒时间完成或者超时。目前,zeek只是在解析输入文件时候执行这种查找,但我们希望可以动态转换地址和主机名,便于生成更加清晰的消息,检测某些类型的攻击。因此,zeek包括了自定义的非阻塞DNS解析,异步执行DNS查询。
未来,可能会采用多线程设计,一个更可能的设计是将zeek演化为分布式设计,在不同的主机上的松耦合的多个zeek系统,同时监视同一个网络链路。每个zeek系统监视不同类型的流量(例如:http或者nfs), 并且仅仅在高级别进行通信,传递威胁信息。更通用的演化是,更加通用的分布式设计,多个zeek监控系统监控多个链路层流量,对于监视工作负载进行均衡,并于基于主机的代理进行交互。
(3)计时器管理,
zeek在内部使用许多计数器进行操作,例如,超时连接建立尝试,有时候,同一个给定时刻,有数千个计时器在等待,因此,计时器必须设计的非常轻巧,设置和过期操作执行速度非常快。
初始实现,使用了一个单一的优先级堆,如果堆包含n个元素,则插入和删除操作只需要O(log(N)) 时间。然而,我们发现当堆变得非常大时候,开销将非常大(例如:在恶意端口扫描期间,每秒钟将创建数百个新连接),因此,需要重新设计计时器,使得开销更加接近于O(1), zeek使用calendar queues(日历队列)实现计时器。
计时器的相关联问题是计时器何时过期,zeek从libpacp库提供的时间戳中派生出时间概念,每当时钟时间前进到比计时器队列上的第一个元素晚的时间(第一个计时器时间为队列中的最早时间),zeek开始从队列中删除计时器并且处理计时器的过期时间,直到队列为空或者第一个元素的时间戳晚于当前时间。然而,这种方法是有缺陷的,在某些情况下,例如:端口扫描,事件引擎会发现它需要同时终止数百个突然到期的计时器,由于传入通信量的停顿,时钟提前了大量时间。通过堆时钟的任何一次提前都过期的计时器数量设置上限值,来避免产生较大的处理峰值。这样做,可以牺牲定时器的准确性,分散负载,由于不需要精确的计时器,所以可以接受这个折衷的方案。
(4)正则表达式的实现,
zeek使用自定义的正则表达式匹配库,而不是复用现有的库,因为下面的两个原因:
A. 无法找到一个高性能的正则表达式库,同时具有可以接受的分发许可证;
B. 入侵检测的模式匹配与比较典型的文本匹配,是两种不同的匹配方式:
B1. 首先,我们希望能够逐段匹配文本,这样就可以在匹配器到达时候为其提供新的文本块,而不必构造整个字符串的副本用于匹配;
B2. 其次,我们期望模式的匹配集,希望知道哪个子集和给定的文本集匹配,出于性能原因,希望使用单个有限自动机进行匹配,而不是按照顺序尝试每个模式。
由于我们有编写高性能正则表达式编译器的经验,而且编译器支持上面的B1,B2两个需求,所以,我们使用自定义的正则表达式编译器
实现正则表达式的最后一个方面涉及到缓存,在分析过程中将使用大量的模式,这可能会消耗大量的cpu时间用于编译,这在希望快速启动监视器时候会出现问题。因此,zeek维护了一个以前编译过的正则表达式的缓存,如果需要编译一个已经在缓存中的正则表达式,则只需要加载编译后的版本,而这只需要很短的时间。
(5)解释还是编译?
目前,zeek解释策略脚本,将脚本解析成一个C++对象树(抽象语法树, AST),然后根据需要通过调用给定子树根上的虚拟求值方法执行子树,递归调用子级的求值方法。这样设计,具有简单和易于调试的优点,但是要付出相当大的开销。从一开始,我们就打算让zeek接收对低级别虚拟机的编译。当前实现的执行配置文件表明,解释性开销相当大,因此,我们希望可以开发编译器和优化器。当前的解释器在构建AST时候进行了一些简单的常量折叠和优化,但需要更多的优化。
使用解释器也会引发一个实现问题,通过构造解释器,使其递归调用AST上的虚拟评估方法,我们需要将复杂的zeek评估堆栈和C++运行时候的堆栈绑定到一起。因此,我们不能轻易地将zeek函数的执行状态,绑定到闭包中,以便在稍后的某个时间点执行。但是我们希望添加某个功能,因此,zeek脚本引入计时器,这些计时器的语义是:在计时器过期时候,执行一个语句块,包括访问函数的局部变量或者调度计时器的事件处理程序。因此,向zeek添加计时器,至少需要实现zeek脚本的执行堆栈,而不是解释器的执行堆栈。
(6)检查点的实现?
我们需要持续运行zeek监视器监视DMZ网络,但是,我们需要定期检查监视器的工作,这样,即可以回收长时间休眠状态的连接所占用的内存(脚本语言中没有计时器),也可以收集快照用于存档和离线分析。
检查点程序目前为一个三阶段的过程,
首先,运行一个新的zeek实例,解析策略脚本并且解析其中所有的DNS域名,因为实现了异步的DNS解析,zeek可以并行的执行大量查询,以及在任何时刻选择超时查找。对于每个查找,将结果和以前的缓存结果进行比较,并且生成相应的事件(映射有效、超时但是映射未验证、更新映射),然后更新DNS缓存文件,实例终止;
在第二阶段,运行另一个zeek实例,指定其只是查询DNS缓存而不执行查找,因为它直接使用缓存,所以启动非常快,
第三阶段,在等待运行一小段时间后,发送信号到运行的zeek实例,终止长期运行的zeek实例,终止长期运行的zeek实例后,检查程序结束
从上面的三阶段检查中,我们发现两个缺陷:
首先,如果zeek的新实例可以直接向一个旧实例发送信号,告知旧实例准备好被接管,那么协调检查点会更加简单;其次,更为重要,当前没有状态可以保留在检查点,尤其是,旧实例中发现了可疑活动,并且密切监视这些可疑活动,则当新节点接管时候,旧实例中的活动信息会丢失,所有,需要解决该问题。
(7)离线分析,如上所述,检查系统的一个原因是为了方便离线分析,离线分析的第一步是将libpcap库中保存文件和策略脚本生成的任何文件复制到分析服务器中,策略脚本生成6个这样的文件:
A. 所有连接活动的摘要,包含开始时间、持续时间、每个方向的大小、协议、IP地址、连接状态、附加信息(如用户名等)
B. 网络接口和数据报筛选器统计信息的摘要
C. 所有生成的日志消息的列表
D. Finger命令的摘要
E. FTP命令的摘要
F. 异常网络事件的列表
最后,事件引擎识别超过70种不同类型的异常行为,如不正确的连接启动、连接终止,校验和错误、数据报长度不匹配、协议冲突,对于每一个异常行为,都生成一个conn_weird或者net_weird事件,用预定义的字符串标识异常行为。zeek策略脚本使用table[string] of count
结构保存ignore, file, log always, log once per connection, log once per originating source address
,表示忽略这些类型的行为,将其记录到异常文件,记录或者实施通知异常,记录到文件(仅仅在第一次发生时候)
所有的复制文件形成了当天的流量档案记录,可以无期限的保留这些文件,当发现过去几周或者几个月存在中断时候,档案启动重要作用,此外,一旦确定了一个攻击站点行为,可以在档案中运行它,找到它可能攻击的、监视没有检测到的其他主机(例如,攻击者使用密码嗅探器获取密码列表)
最后,可以利用离线分析生成一个流量摘要,突出显示负载最大的服务器,给出该服务器上的不同应用程序占用带宽情况(连接数和传输的字节数)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。