Bro: A System for Detecting Network Intruders in Real-Time
FDDI,fiber distributed data interface, 光纖分佈式數據接口
DMZ,demilitarized zone, 隔離區,兩個防火牆之間的空間
AST, abstact syntax tree, 抽象語法樹html
隨着互聯網鏈接的不斷增加,攻擊者經過網絡非法訪問計算的機會也愈來愈多。檢測這類攻擊問題稱之爲網絡入侵檢測,一個相對較新的安全研究領域,能夠將這些系統分爲下列兩類:
(1)依賴於試圖保護的網絡中的主機收集到的審計信息;
(2)直接獨立監控網絡流量,被動的經過數據報
如今,人們對於兩類系統結合起來的混合系統更感興趣python
下面將介紹第二類系統,建立一個獨立的監控網絡流量的系統,稱爲監視器,雖然,監視器要面對比訪問審計跟蹤的系統更加有限的系統的困難,可是,監視器能夠被添加到網絡中,不須要對主機進行任何修改。同時,監控數千個不一樣管理的主機集合,是監視器的一個巨大優點。正則表達式
監視器系統命名爲bro,如今已經重命名爲zeek, 在開發zeek以前,基於離線tcpdump數據報的分析經驗,制定了zeek的設計目標和要求:shell
(1)高速、大容量監控,對於常見場景,咱們將外部主機經過網絡能夠訪問的主機看成最大的安全威脅來源,因爲咱們要保護的網絡中,只有一部分鏈接到公有網絡中的主機(在隔離區中),能夠經過被動的監視這一部分主機的鏈路層網絡流量進行網絡保護,由於,鏈路層中採用FDDI環,因此,監控系統也須要實現高達100M/s的捕獲流量速率;緩存
(2)沒有數據報過濾丟棄,若是數據報篩選器的應用程序沒法在數據報到達受監視鏈路時候,儘快的消費,過濾器將緩存數據報以供以後消費,可是,最終,過濾器將耗盡緩衝區,從而丟棄更多到達的數據報,從安全監控的角度,數據報的丟棄將會破壞監視器,由於,丟棄的數據報可能包含有攻擊信息,所以,避免數據報被過濾掉是另一個要求;安全
(3)實時告警,在最初離線系統分析中,檢測到攻擊以前,會有長時間的延遲,若是快速檢測到攻擊或者試圖進行的攻擊,則能夠更加容易的追蹤攻擊者,將損害降至最低,防止進一步的入侵,並啓動對攻擊者發起的網絡活動的完整記錄,所以,對於zeek,一個要求就是能夠實時檢測攻擊。這並非說,保存大量、永久性的網絡活動日誌的做用不大,一般,當咱們檢測到攻擊,將會使用這些日誌進行回顧性的損害評估,有時會回溯幾個月前的日誌;服務器
(4)機制與安全策略的分離,良好的軟件設計每每強調在機制和策略之間創建清晰的分離,有助於提升系統的簡潔和靈活性,對於zeek系統,二者分離更有必要性:由於咱們處理的網絡流量很是大,須要可以在不一樣時間,考慮過濾、檢查策略,對不一樣類型流量進行響應,而若是將這些響應硬編碼到系統中,將會使得後期維護更加麻煩和更容易出錯;網絡
(5)擴展性,由於網絡攻擊很是多,更新速度快,須要不斷更新攻擊檢測規則,所以,咱們須要可以以較小的易於調試的增量去升級zeek系統;session
(6)避免簡單錯誤,咱們老是想避免錯誤,特別但願網站定義安全、清晰的策略,因此咱們不考慮使用C代碼來表示策略;多線程
(7)監視器自己可能被攻擊,假設,攻擊者徹底瞭解監視器使用的技術,能夠訪問監視器的源代碼,而且利用這些知識試圖攻擊監視器,致使其沒法檢測到攻擊者的入侵活動,這個假設,使得監視器的設計更加複雜化,可是這是必需要解決的問題。咱們能夠進一步假設,監視器只會從一端收到攻擊,也就是說,假設主機A,B之間存在網絡鏈接,則最多有一個主機(A或者B)可能被破壞,可能會試圖攻擊監視器,這個假設極大的幫助咱們處理監視器被攻擊問題,由於它意味着咱們能夠信任其中的一個端點(儘管咱們不知道是哪個端點)。若是主機A和B都被破壞了,那麼攻擊者能夠在二者之間創建複雜的祕密通道,這意味着,監視器沒法察覺到威脅,取決於通道的迂迴程度。
下文將介紹,zeek系統如何實現上面的目標,
第二章,介紹整個系統的結構框架,
第三章,介紹zeek系統的專用腳本語言zeek,
第四章,講述如今zeek系統的實現方式,
第五章,討論對於監視器自己的攻擊方式,
第六章,分析zeek系統爲6個經常使用應用層程序實現的檢測規則,
第七章,包含咱們對於zeek系統的簡單評估,
第八章,對zeek系統將來的展望。
zeek在概念上能夠分爲一個事件引擎和一個解釋器,其中,事件引擎用於將數據報數據流減小爲更爲高級抽象的網絡事件流,解釋器用於解釋zeek語言編寫的安全策略,
更爲通常的,zeek系統能夠當作是分層結構,最底層處理最大數量的數據報,所以,底層對於數據報的操做最少,越往上層結構,數據流將變得越少,對於數據的處理越多。這種基本設計反映了儘量節省處理時間的需求,即在不丟包的狀況下,監控高速、大流量的數據的需求。
從分層結構圖,可知網絡層上面是libpcap(數據報捕獲庫,tcpdump程序使用),使用libpcap有下面的優點:
(1)將zeek和網絡連接技術的細節(以太網、FDDI、SLIP等)隔離;
(2)有助於zeek程序在不一樣的unix變體中移植(升級到更高性能的硬件);
(3)能夠直接對tcpdump保存文件進行操做,使得離線開發、分析變得更加容易;
(4)若是主機的操做系統提供了足夠強大的內核包過濾器,例如BPF,則libpcap將下載用於減小內核流量的過濾器;所以,沒必要將每一個數據報發送到用戶級進程,而後再丟棄數據報(過濾器只接收少許流量),能夠在內核中丟棄被過濾的數據報,沒必要進行上下文切換或者數據複製;
當使用包過濾器,必須設置快照長度參數,該長度決定了每一個數據報的捕獲長度,例如:tcpdump使用68字節的快照長度,它足以捕獲到鏈路層和TCP/IP數據報頭部,可是一般會丟棄包中的大部分數據,
快照長度越小,每一個接收的數據報須要經過包過濾器複製到用戶級別的數據就越少,有助於加快數據報處理和避免丟失;另外一方面,爲了在應用程序級別分析鏈接,zeek須要每一個數據報的完整數據內容,所以,設置快照長度以捕獲整個數據報。
過濾後的數據報流被傳遞到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事件。
當事件引擎處理完數據報後,檢查是否產生了事件,事件保存在隊列(先進先出)中,若是隊列中存在事件,則依次處理事件,還須要檢查是否有任何計時器事件已通過期,若是過時,須要執行對應的處理程序
zeek設計的一個關鍵方面是明確區分事件的生成和處理,策略腳本解釋器用於執行zeek語言編寫的程序,zeek腳本指定了事件的處理程序,這些處理程序本質上和zeek函數相同,只是腳本程序沒有返回值。對於每個傳遞給解釋器的事件,解釋器檢索對應的處理程序(半編譯的代碼),將事件的值映射處處理程序的參數,解釋腳本語言。被解釋的zeek程序能夠執行任意的zeek腳本命令,包含:產生新事件,實時記錄通知(unix中syslog函數),將數據記錄到磁盤或者修改事件內部狀態以便於隨後調用事件處理程序(或者事件引擎自己)。
最終,隨着機制和策略的分離,zeek系統強調異步事件做爲事件引擎和策略腳本解釋器之間的鏈接,有助於提升系統的可擴展性。向zeek添加新的功能一般包括(1)向事件引擎添加新的協議分析器(2)爲協議分析器生成的事件編寫新的事件處理程序。新的協議分析器和事件處理程序,都和現有功能儘可能少的功能重疊,所以在大多數狀況下,能夠避免鬆耦合的多個模塊之間的交互,防止出現各類程序維護問題。
下面將介紹zeek語言的特點,而不是精確描述語言細節,第一章中提到的目標6(防止簡單錯誤)對於zeek語言的設計影響很是大。
由於入侵檢測是站點安全的基石,因此策略腳本語言zeek的設計很是重要。根據經驗,避免意外的一個重要步驟是使用強類型語言,能夠在編譯時候避免類型不一致,而且保證運行時候全部變量的引用都是有效值。開始時候,咱們使用tcpdump, awk, shell拼湊了一個監視器,但使用後,更加急於找尋直接處理主機名,ip地址,端口號等的方法,而不是設計ASCII編碼的等價物。經過在zeek中設置了這些實體(主機名、端口號、ip地址)爲第一類函數,加強了語言表達的易用性,而且因爲是強類型,例如:IP地址類型和端口號類型的值的比較這類錯誤將被避免。
原子類型,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類型值是否匹配
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判斷
函數和事件處理程序的定義不一樣表如今,zeek容許事件處理程序有多個不一樣的定義,每當生成事件時候,處理程序的每一個實例將依次調用(按照它們在腳本中出現的順序),因此,不一樣的模塊能夠自定義zeek_init處理程序用於初始化,這將大大簡化建立模塊化事件處理程序集的任務。
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
,表示忽略這些類型的行爲,將其記錄到異常文件,記錄或者實施通知異常,記錄到文件(僅僅在第一次發生時候)
全部的複製文件造成了當天的流量檔案記錄,能夠無期限的保留這些文件,當發現過去幾周或者幾個月存在中斷時候,檔案啓動重要做用,此外,一旦肯定了一個攻擊站點行爲,能夠在檔案中運行它,找到它可能攻擊的、監視沒有檢測到的其餘主機(例如,攻擊者使用密碼嗅探器獲取密碼列表)
最後,能夠利用離線分析生成一個流量摘要,突出顯示負載最大的服務器,給出該服務器上的不一樣應用程序佔用帶寬狀況(鏈接數和傳輸的字節數)