xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
添加文章 English Version

Bro:一个开放源码的高级NIDS系统


创建时间:2003-10-12
文章属性:原创
文章提交:stardust (stardust_at_xfocus.org)

在介绍Bro之前先总结一下几个常见的开放源码NIDS系统:

Snort ( http://www.snort.org/ )
目前最著名最活跃的开放源码NIDS项目,定位于轻量级的入侵检测系统,已经实现了网络探测器和许多第三方的管理及日志分析工具,广泛使用在对检测准确性要求不高的非高速网络环境下。对其进行介绍及分析资料辅天盖地,国内当前大多数的IDS产品都是在其基础上修改出来的。Snort的成功之处在于其高效的整体设计编码、简洁明了的规则描述设计及已经成一定规模的攻击检测规则集,它的规则集已经被很多其他开放源码IDS项目所兼容。

Prelude IDS ( http://www.prelude-ids.org/ )
目前比较活跃的开放源码混和IDS项目,比较注重IDS各组件的模块化设计,组件之间使用标准IDMEF格式进行通讯,从设计的方式来看定位于适应大型网络的需求,当前已经实现了网络探测器、日志分析器、告警信息集中查看分析工具。其网络探测器部分基本上翻版了Snort的功能,完全兼容Snort的规则集。

Firestorm ( http://www.scaramanga.co.uk/firestorm/index.html )
不太出名的开放源码NIDS项目,目前仅实现了探测器部分,完全兼容Snort的规则集,功能上也基本上差不多,可以把告警信息记录到Prelude IDS的管理器,但自称性能上比Snort强很多。

NetSTAT ( http://www.cs.ucsb.edu/~rsg/STAT/ )
一个学术界的开放源码的IDS项目,基于STAT(State Transition Analysis Technique,状态迁移分析技术)描述攻击的研究成果,使用特有的STATL语言描述攻击,攻击描述文本被STATL解释工具转换为C++代码编译进检测引擎来实现检测功能,目前已经发布了STATL语言解释转换工具及一个基本的示例网络探测器部分(很少的几个检测功能例子)。要熟练使用这个IDS工具需要比较强的编程功底,但用此IDS可以实现很复杂的检测功能。

可以看到当前大多数开放源码的NIDS项目都以Snort为基础,还有一些主要用于学术研究的IDS(比如NetSTAT),Snort是用于了解NIDS技术的很好入门方式,学术领域的IDS主要用于验证某些思想概念的可行性,这类IDS一般实现使用复杂,注重检测的严谨准确,如果商业化还需要很大的努力。Snort实现简单,使用方便,但本质上只是一个可以匹配分析数据包payload的嗅探器而已,对于有丰富网络知识的网管来说是个称手的网络流量过滤分析工具。由于只能对TCP/UDP/ICMP数据包的payload及某些数据包属性做些简单的匹配操作,而且没有特定的规则关联机制,Snort对于攻击的检测只能是粗线条的。当前的主流商业NIDS产品已经普遍采用细致的应用层协议分析技术,应用层协议分析技术不仅能够极大地提高检测准确性和效率,更能带来基于协议分析的异常检测能力,基于规则类似病毒检测的方式只能检测已知攻击,而基于异常分析的检测却可能检测到某些未知攻击,这点是新NIDS产品的发展热点和方向,基于异常并结合误用的IDS产品将会是未来IDS/IPS产品的主流。Snort虽然对一些应用层协议如HTTP、PORTMAP、TELNET做了些简单的解码和处理,这些处理只是对匹配做的一些优化,远没有到协议分析的程度,Snort与当今主流IDS产品的差距越来越大,相对而言越来越落后,学习参考的价值越来越少。是不是存在技术上更有意思的开放源码的NIDS项目呢,我注意到了一个叫Bro的NIDS软件,它几乎提供了一切所希望见到的特性。

下面介绍一下Bro ( http://www.icir.org/vern/bro-info.html ),本文旨在分析Bro本身提供的一些很有意思且很有用的功能特性,不对其如何安装、配置、使用做介绍,那部分的内容可以参考软件包中的相关说明。

Bro是一个Vern Paxson实现的实时网络入侵检测软件,于98年对外发布,BSD license,它的最初设计目标是实现一个在100M网络下实时告警、机制与策略分离、高可扩展性的入侵检测及网络监视审计系统。

Bro的系统结构如下图示:
            
                      | 下发          ^ 实时
                      V 策略          | 告警
                   +-----------------------------+
                   |        策略脚本解释器       |
                   +-----------------------------+
                    | 事件  | 规则       ^ 事件
                    V 控制  | 控制       | 流
                   +--------+--------------------+
              规则 +--------V-+                  |
             ----->| 规则引擎 |   事件生成引擎   |
                   +----------+                  |
                   +-----------------------------+
                      | Tcpdump       ^ 过滤出
                      V 过滤器        | 的流量
                   +-----------------------------+
                   |          libpcap            |
                   +-----------------------------+
                                 ^
                                 | 网络数据包
                   +-----------------------------+
                   |           网络              |
                   +-----------------------------+
                  

Bro的设计实现遵循分层的原则,利用libpcap从网络上获取的数据包经过事件生成引擎和规则引擎被抽象成一系列的事件,这些事件被策略脚本做进一步的深入分析,基本事件本身就可以触发告警,策略脚本分析基本事件以后既可以生成新的事件也可以触发告警。如下详细描述各个组件的功能,对于Bro提供的高级功能特性分析会穿插其间:
    
$ libpcap

Bro使用libpcap库来获取网络数据包,这样可以使Bro忽略具体的链路层细节,从而获得最大的可移植性。当前Bro支持FreeBSD、Solaris、Linux、Digital Unix等操作系统。

libpcap从上层的事件生成引擎获得Tcpdump格式的网络流量过滤器,从网卡上过滤出Bro感兴趣的那部分流量做进一步的分析。比如“tcp port 80 or port 23”这个过滤器会从网络上过滤出源目的源口为80或23的TCP数据包做分析,也就是分析目标是HTTP和TELNET协议相关的数据包。


$ 事件生成引擎

从libpcap获取的数据包经过事件生成引擎,引擎首先对数据包头做些合法性检查,生成一系列事件,比如引擎收到一个TCP包会先检查TCP头的checksum是否正确,检查通过以后引擎检查包头的标记位,如果是SYN标记引擎会启动一个定时器并生成一个connection_attempt事件,当引擎再收到一个对方的确认包时,引擎会关掉定时器并生成一个connection_established事件,如果收到一个RST包,引擎则会生成一个connection_rejected事件。这些事件都会被提交给上层的策略脚本解释器,如果用户关心这些事件可以编写策略脚本定义相应的事件处理例程来处理。

在这一层,Bro还会对于一些应用层协议做更深层次的分析,比如对于HTTP协议,引擎会进一步分析数据包是请求还是回应、访问的对象、请求的方法等信息并生成相应的事件,比如http_request事件并包含了相关的数据字段。目前的最新版本软件支持FINGER、FTP、HTTP、IDENT、PORTMAP、SMTP等应用层协议的分析,随着软件的开发会有更多的应用层协议分析加入进来,Bro在这一层上实现了应用层的协议分析并向上层策略脚本解释器提供抽象出来的事件。


$ 策略脚本解释器

Bro的策略脚本用于对事件生成引擎生成的事件进行分析处理,如果发现攻击特征引发相应的操作比如告警、记录或生成新的事件。Bro的策略脚本是用Bro语言编写的类似NFR N-Code的全功能解释执行的分析脚本,Bro语言提供了丰富的数据类型、流程控制手段及许多有用的分析函数,从实现上功能上与N-Code非常相似甚至有过之。如此功能强大的脚本语言可以对抽象出来的事件做充分细致的分析,组合Bro事件生成引擎中应用层协议分析和Bro脚本的分析能力可以提供非常准确强大的入侵检测功能。

下面分析一个检测某些恶意HTTP请求的Bro脚本:

------------------------------------------------------------------------------
# $Id: http-request.bro,v 1.14 2003/08/31 00:40:21 vern Exp $

# Analysis of HTTP requests.

@load http  # 装入此脚本依赖的http.bro脚本。

redef capture_filters +=  {
    ["http-request"] = "tcp dst port 80 or tcp dst port 8080 or tcp dst port 8000"
};  # 定义过滤HTTP协议相关流量的tcpdump过滤器

const sensitive_URIs =
      /etc.*\/.*(passwd|shadow|netconfig)/
    | /IFS[ \t]*=/
    | /nph-test-cgi\?/
    | /(%0a|\.\.)\/(bin|etc|usr|tmp)/
    | /[iI][iI][sS][aA][dD][mM][pP][wW][dD]/
    | /\/(cmd|root|tftp)\.exe/
    | /\/Admin_files\/order\.log/
    | /\/carbo\.dll/
    | /\/cgi-bin\/(phf|php\.cgi|test-cgi)/
    | /\/cgi-dos\/args\.bat/
    | /\/cgi-win\/uploader\.exe/
    | /\/search97\.vts/
    &redef;  # 用正则表达式定义一些常见的恶意HTTP请求

# URIs that match sensitive_URIs but can be generated by worms, and hence
# should not be flagged (because they're so common).
const worm_URIs =
      /.*\/c\+dir/
    | /.*cool.dll.*/
    | /.*Admin.dll.*Admin.dll.*/
    &redef; # 定义ssl-worm相关的几个请求

# URIs that should not be considered sensitive if accessed remotely,
# i.e. by a local client.
const skip_remote_sensitive_URIs: pattern &redef;
# In order to redef skip_remote_sensitive_URIs, you have to also
# redef the following to T, because there's currently no way in Bro
# to specify an empty pattern that can otherwise be used as the default
# for skip_remote_sensitive_URIs.
const have_skip_remote_sensitive_URIs = F &redef;

const sensitive_post_URIs = /wwwroot/ &redef;

event http_request(c: connection, method: string, original_URI: string,  # http_request事件的处理
            unescaped_URI: string, version: string)          # 例程,对同一事件可以定
    {                                                                # 义多个不同的处理例程。
    local log_it = F;                                                # 事件生成引擎随同事件提
    local URI = unescaped_URI;                                       # 供了相关的连接情况、请
                                                                         # 求方法、请求的URI等字
                                                                         # 段信息。

    if ( (sensitive_URIs in URI && URI != worm_URIs) ||              # 如果URI中包含了某些恶意
         (method == "POST" && sensitive_post_URIs in URI) )          # 请求。  
        {
        if ( have_skip_remote_sensitive_URIs &&                  # 排除某些不需关心的情况。
             is_local_addr(c$id$orig_h) &&
             skip_remote_sensitive_URIs in URI )
            ; # don't flag it after all
        else
            log_it = T;                                      # 设置记录标记。
        }

    local s = lookup_http_request_stream(c);                         # 查找此连接相关的HTTP
                                                                         # 信息,如果不存在,则
                                                                         # 创建一个此网络连接相
                                                                         # 关的HTTP信息。lookup
                                                                         # _http_request_stream
                                                                         # 函数在http.bro脚本中
                                                                         # 定义。
                                                                        
    if ( process_HTTP_replies )                                      # 如果还要处理HTTP请求
                                                                     # 的回应。
        {
        # To process HTTP replies, we need to record the corresponding
        # requests.
        local n = s$first_pending_request + s$num_pending_requests; # 获取此连接相关的HTTP
                                                                    # 信息的索引号。

        s$requests[n] = [$method=method, $URI=URI, $log_it=log_it]; # 存储相关的HTTP信息到
                                                                    # 索引号指向的记录。
        ++s$num_pending_requests;

        # if process_HTTP_messages
        local msg = s$next_request;  

        init_http_message(msg);   # 初始化pending HTTP请求队列下一个记录的信息。
                                  # init_http_message函数在http.bro脚本中定义。
        msg$initiated = T;
        }
    else
        {
        if ( log_it )             # 做记录。
            ALERT([$alert=HTTP_SensitiveURI, $conn=c,
                $msg=fmt("%s %s: %s %s",
                    id_string(c$id), c$addl, method, URI)]);
        print http_log,
            fmt("%.6f %s %s %s", network_time(), s$id, method, URI);
        }
    }
------------------------------------------------------------------------------

Bro策略脚本的编写需要不少时间和精力的投入,Bro的作者提供了完整的使用手册,包括各种特定数据类型的说明、引擎本身支持的函数说明、如何使用的示例,用户经过一定时间的学习可以完全掌握。


$ 规则引擎

规则引擎是新近Bro 0.8版本加入的一个组件功能,此功能允许用户把一些数据包特征定义成简单的规则,事件生成引擎根据规则匹配网络数据包生成相应的事件,这些事件可能直接导致告警信息的产生,也有可能被策略脚本作进一步的分析形成高级别的告警。

与Snort的规则相比,Bro的规则不仅格式不同,而且提供了Snort规则所不具备的高级功能:匹配正则表达式、多个规则的关联、规则与策略脚本的交互。下面通过几个例子来展示Bro规则提供的高级特性。

可以用如下的Bro规则来准确地检测前阵子流行的对RPC DCOM的两个溢出漏洞的攻击尝试。

signature rpc-dcom_bind-req {    # 定义一个叫rpc-dcom_bind-req的规则,此规则匹配一个攻击会话
                                 # 中必定先于实际攻击数据包出现的绑定RPC DCOM的数据包,以此规
                                 # 则匹配出来的事件不单独触发告警,用于被以下的两个规则做关联。
  header ip[9:1] == 6            # 匹配IP包头的协议字段为6,也就是TCP包。
  header tcp[2:2] == 135         # 匹配TCP包头的目标端口字段为135,也就是RPC DCOM漏洞常用的攻击端口。
  tcp-state originator,established # 匹配TCP包状态为已建立且从连接的发起者发出。
  payload /\x05\x00\x0b.{100,}\xa0\x01\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46.*\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60/ # 用正则表达式匹配RPC DCOM BIND包的payload特
                                                 # 征(RPC包头及两个特定的UUID)。
  event "RPC DCOM BIND request"                  # 定义可能出现在告警日志的事件信息。
  }

signature rpc-dcom_servername-overflow {  # 定义名为rpc-dcom_servername-overflow的规则,此规则
                                          # 匹配RPC DCOM长主机名的栈溢出攻击。
  header ip[9:1] == 6
  header tcp[2:2] == 135
  requires-signature rpc-dcom_bind-req    # 指定需要rpc-dcom_bind-req规则先要匹配到本规则才能
                                          # 成立,也就是说此规则关联了同一个TCP会话中的另一个
                                          # 规则,这是Bro规则提供的特有功能。具体到本规则,表
                                          # 达的意思就是只有在看到有RPC DCOM BIND操作以后才会
                                          # 再去匹配接下来的数据包中是否有攻击数据包。
  tcp-state originator,established
  payload /.*\x05\x00\x00.{100,}\x5c\x00\x5c\x00[^\\]{32,}/  # 匹配攻击数据包的payload特征,用正
                                                             # 表达式检查是否存在超过32字节的主机
                                                             # 名。
  event "RPC DCOM servername stack overflow attempt"
  }

signature rpc-dcom_pathname-overflow {   # 定义名为rpc-dcom_pathname-overflow的规则,此规则
                                         # 匹配RPC DCOM长路径名的堆溢出攻击。
  header ip[9:1] == 6
  header tcp[2:2] == 135
  requires-signature rpc-dcom_bind-req   # 同上条规则一样的前提条件。
  tcp-state originator,established
  payload /.*\x05\x00\x00.{100,}\x5c\x00\x5c\x00[^\\]{5,32}\x5c\x00.{520,}\x00\x00/ # 匹配攻击数据
                                                                                    # 包的payload特
                                                                                    # 征,用正则表达
                                                                                    # 检测是否存在超
                                                                                    # 长的路径名。
  event "RPC DCOM pathname heap overflow attempt"
  }

以上的Bro规则展示了规则支持正则表达式匹配和规则关联的强大功能特性。正则表达式可以非常精确地匹配攻击数据的特征,当然正则表达式也存在效率低下的缺陷,但对于面向百兆网络流量注重检测准确性的引擎来说,这样的代价还是可以接受并允许的。规则关联其实是一个非常强大的功能特性:利用规则之间的关联可以很大程度上提高检测的准确性,比如上面的检测RPC DCOM攻击的例子;通过关联攻击请求及回应数据可以判定攻击的成功与否,比如通过关联一个CGI攻击请求和它的回应数据包中的HTTP状态码可判定攻击是否成功;通过关联某些相关事件序列可以触发更高级别的事件,比如可以关联多个明显的恶意CGI请求可以得出系统正在受到CGI漏洞扫描攻击的结论。

如下的用于检测ssl-worm攻击的Bro规则展示规则与策略脚本的交互特性。

signature sslworm-probe {  # 定义一个名为sslworm-probe的规则,检测ssl-worm的扫描操作
  header ip[9:1] == 6      # TCP包
  header tcp[2:2] == 80    # TCP目标端口80
  payload /.*GET \/ HTTP\/1\.1\x0d\x0a\x0d\x0a/  # 匹配payload中ssl-worm扫描数据特征
  event "Host may have been probed by Apache/SSL worm" # 定义日志记录中的告警信息
  }

signature sslworm-vulnerable-probe { # 定义扫描到有漏洞主机的规则,通过关联上面的扫描检测规则
  requires-signature sslworm-probe   # 引用上面的扫描检测规则
  eval sslworm_is_server_vulnerable  # 执行Bro策略脚本中预先定义的功能函数,此函数用于通过判
                                     # 断被扫描的主机的OPENSSL软件的版本来确定攻击方是否扫描
                                     # 到了有漏洞的主机。
  event "Host may have been probed by Apache/SSL worm and is vulnerable"  # 告警信息
  }
  
以上的规则展示了规则与策略脚本之间的交互,从规则可以引用策略脚本中的完成特定功能的函数,函数向规则返回函数执行完后的成功或失败的信息,规则根据返回的信息来确定是否触发相关的事件。规则中引用的功能函数可以对可以判定某些匹配条件进行判定返回结果,如上面的ssl-worm例子;也可以对多个事件进行更深入的分析关联;甚至可以利用规则中引用的功能函数执行响应操作,比如切断连接。


总结:与Snort简单粗糙的检测方式相比,Bro提供了一些关键的高级特性:在事件生成引擎中实现的应用层协议功能、强大的用于对事件做深入分析的策略脚本、支持正则表达式的数据匹配、规则关联(同一会话的规则直接关联,跨会话规则的关联可以用策略脚本实现)、规则与策略脚本的交互,这些特性使Bro这个开放源码的NIDS系统所能实现的功能更接近于当前主流商业NIDS产品,对之细调以后完全可以做到很多商业NIDS产品的准确检测,Bro是一个值得NIDS开发者参考和借鉴的好东西,也是具有一定网管经验的用户定制某些攻击检测功能的好工具。