2020-11-16 Zeek Script

@load

Zeek可以從其他文件中l(wèi)oad腳本, 用@load就可以
@load misc/dump-events 用于輸出所有日志, 用于debug

zeek默認(rèn)路徑, 可以直接load?
<prefix>/share/zeek
<prefix>/share/zeek/policy
<prefix>/share/zeek/site

@load操作最常用的腳本為/site/local.zeek, 此腳本包含部分zeek的配置信息, 可以添加一些不被默認(rèn)裝載的腳本.
目錄<PREFIX>/share/zeek 中/base下的腳本全部會(huì)自動(dòng)被裝載.
目錄<PREFIX>/share/zeek 中/policy 需要手動(dòng)裝載.

函數(shù)

function emphasize(s: string, p:string &default="*"): string{
    return p+s+p;
}

event zeek_init(){
    print emphasize("yes");
    print emphasize("no","-");
}

*yes*
-no-

function 函數(shù)名(變量名:類(lèi)型 &屬性): 函數(shù)返回值類(lèi)型{
}

變量

變量的聲明是弱類(lèi)型
常用的有l(wèi)ocal/global/const等接變量名
也可local/global/const 變量名:變量類(lèi)型
ps: 沒(méi)有i++只有++i

原生數(shù)據(jù)類(lèi)型

https://docs.zeek.org/en/current/script-reference/types.html

    print /one|two|three/ == "two";  # T
    print /one|two|three/ == "ones"; # F (exact matching)
    print /one|two|three/ in "ones"; # T (embedded matching)
    print /[123].*/ == "2 two";  # T
    print /[123].*/ == "4 four"; # F

features:
count, pattern(正則), time, interval, port, addr, subnet

Zeek 數(shù)據(jù)類(lèi)型

  • time
    network_time()得到當(dāng)前時(shí)間點(diǎn), 如果輸入為報(bào)文則為報(bào)文時(shí)間. 通過(guò)double_to_time(d)函數(shù)將double格式的d轉(zhuǎn)為time格式

  • strftime("%D %H:%M", c$start_time)
    將標(biāo)準(zhǔn)時(shí)間格式轉(zhuǎn)換成可讀時(shí)間

  • interval
    時(shí)間間隔類(lèi)型, usec, msec, sec, min, hr, or day.

  • 端口
    port, 帶傳輸層協(xié)議, 例53/udp, 80/tcp

  • 地址
    addr, 例 1.2.3.4, [2001:db8::1]

  • 網(wǎng)段
    subnet, 192.168.0.0/16

event zeek_init()
    {
    print network_time();
    local d:double=111111111.123456789;
    print double_to_time(d);
    
    local t:time=double_to_time(d);
    
    local i:interval=network_time()-t;
    print i;
    
    local p:port=53/tcp;
    print p;
    
    if (127.0.0.1 in 127.0.0.0/31)
        print 222222222222;
    }

type casting

http://mailman.icsi.berkeley.edu/pipermail/zeek/2017-January/011178.html

  • cat()
    local a:addr=127.0.0.1;
    print cat("foo", 3, T, a);
    foo3T127.0.0.1
  • as
function example(a: any)
    {
    local s: string;

    if ( a is string )
        s = (a as string);
    }

for if 格式化print

event zeek_init() 
    { 
    local x = "3";

    for ( c in "12345" )
        {
        if ( c == x )
            {
            print "Found it.";
            # A preview of functions: fmt() does substitutions, outputs result.
            print fmt("And by 'it', I mean %s.", x);
            }
        else
            {
            # A quick way to print multiple things on one line.
            print "I'm looking for", x, "not", c;
            }
        }
    }

for 和 while

  • for (i in set()) print i;
    輸出set()中元素
  • for (i in vector("a","b","c")) print i;
    輸出為vector下標(biāo)
  • for ( i in table([0]="a",[1]="b",[2]="c") ) print i;
    輸出為table下標(biāo)
  • break是break液样,continue變?yōu)閚ext

switch語(yǔ)句接多個(gè)條件

event zeek_init() 
    { 
    local result = 0;
    local input = "The Zeek Network Security Monitor";
    for ( c in input )
        {
        switch ( c ) 
            {
            case "a", "e", "i", "o", "u":
                ++result;
                break;
            }
        }
    print result;
    }

event

  • 可以自定義事件
  • 沒(méi)有返回值
  • 可以有多個(gè)定義, 被調(diào)用時(shí)按照&priority順序執(zhí)行

一些與協(xié)議無(wú)關(guān)的事件, 即不通過(guò)協(xié)議狀態(tài)觸發(fā)的事件
https://docs.zeek.org/en/current/scripts/base/bif/event.bif.zeek.html

global myevent: event(s: string);

global n = 0;

event myevent(s: string) &priority = -10
    {
    ++n;
    }

event myevent(s: string) &priority = 10
    {
    print "myevent", s, n;
    }

event zeek_init()
    {
    print "zeek_init()";
    event myevent("hi");
    schedule 5 sec { myevent("bye") };
    }

event zeek_done()
    {
    print "zeek_done()";
    }

zeek_init()
myevent, hi, 0
myevent, bye, 1
zeek_done()

schedule 5 sec{}, 五秒后執(zhí)行或在zeek進(jìn)程結(jié)束前執(zhí)行

hook

hook 是另一種形式的函數(shù),和event的相同點(diǎn)是可以多次定義, 被調(diào)用時(shí)按照&priority順序執(zhí)行巧还。
和event的不同點(diǎn)為:

  • 有返回值
  • 被調(diào)用立即執(zhí)行
  • 如果執(zhí)行到末尾或return語(yǔ)句鞭莽,則繼續(xù)執(zhí)行priority低的hook,但如遇到break語(yǔ)句麸祷,則不會(huì)執(zhí)行其他的同名hook
global myhook: hook(s: string);

hook myhook(s: string) &priority = 10
    {
    print "priority 10 myhook handler", s;
    s = "bye";
    }

hook myhook(s: string)
    {
    print "break out of myhook handling", s;
    break;
    }

hook myhook(s: string) &priority = -5
    {
    print "not going to happen", s;
    }

event zeek_init() 
    {
    local ret: bool = hook myhook("hi");
    if ( ret )
        {
        print "all handlers ran";
        }
    }

priority 10 myhook handler, hi
break out of myhook handling, hi

Set

集合, 通過(guò)add和delete操作.
local x: set[string]={"one","two","three"};

event zeek_init()
    {
    local x: set[string] = { "one", "two", "three" };
    add x["four"];
    print "four" in x; # T
    delete x["two"];
    print "two" !in x; # T
    add x["one"]; # x is unmodified since 1 is already a member.

    for ( e in x )
        {
        print e;
        }
    }

T
T
one
three
four

Table

  • 表項(xiàng)(索引)唯一
  • 添加直接賦值即可
  • 通過(guò)delete刪除
  • 相當(dāng)于其他語(yǔ)言的數(shù)組, 哈希表, map等
event zeek_init() 
    { 
    local x: table[count] of string = { [1] = "one", 
                                        [3] = "three",
                                        [5] = "five" };
    x[7] = "seven";
    print 7 in x; # T
    print x[7]; # seven
    delete x[3];
    print 3 !in x; # T
    x[1] = "1"; # changed the value at index 1

    for ( key in x ) 
        {
        print key,x[key];
        }
    }

T
seven
T
1, 1
5, five
7, seven

Vector

  • 區(qū)別于集合的點(diǎn)為可以存相同的元素
event zeek_init() 
    { 
    local x: vector of string = { "one", "two", "three" };
    print x; # [one, two, three]
    print x[1]; # two
    x[|x|] = "one";
    print x; # [one, two, three, one]

    for ( i in x ) 
        {
        print i;  # Iterates over indices.
        }
    }
[one, two, three]
two
[one, two, three, one]
0
1
2
3

其中第6行為向容器中添加一個(gè)新元素, |x|表示容器的長(zhǎng)度.

Record

類(lèi)似結(jié)構(gòu)體, 通過(guò)$符號(hào)來(lái)索引結(jié)構(gòu)體中成員(用.會(huì)跟ip地址沖突)
通過(guò)?$判斷成員是否存在.
type代表用戶(hù)自定義類(lèi)型

type MyRecord: record {
    a: string;
    b: count;
    c: bool &default = T;
    d: int &optional;
};

event zeek_init() 
    { 
    local x = MyRecord($a = "vvvvvv", $b = 6, $c = F, $d = -13);
    if ( x?$d )
        {
        print x$d;
        }
    
    x = MyRecord($a = "abc", $b = 3);
    print x$c;  # T (default value of the field)
    print x?$d; # F (optional field was not set)
    }

Record 的重定義

record和enum這種用戶(hù)自定義類(lèi)型可以拓展

type MyRecord: record {
    a: string &default="hi";
    b: count  &default=7;
} &redef;

redef record MyRecord += {
    c: bool &optional;
    d: bool &default=F;
    #e: bool; # Not allowed, must be &optional or &default.
};


event zeek_init() 
    {
    print MyRecord();
    print MyRecord($c=T);
    }

[a=hi, b=7, c=<uninitialized>, d=F]
[a=hi, b=7, c=T, d=F]

e.g.

  • event new_connection()
  • strftime("%D %H:%M", c$start_time)
    將標(biāo)準(zhǔn)時(shí)間格式轉(zhuǎn)換成可讀時(shí)間
  • connection_state_remove()
    在連接從內(nèi)存中移除時(shí)觸發(fā)的事件, 可以方便地記錄一個(gè)連接的interval類(lèi)型的持續(xù)時(shí)間.
  • 內(nèi)網(wǎng)ip在實(shí)際環(huán)境中在network.cfg文件中配置
global local_subnets: set[subnet] = { 192.168.1.0/24, 192.68.2.0/24, 172.16.0.0/20, 172.16.16.0/20, 172.16.32.0/20, 172.16.48.0/20 };
global my_count = 0;
global inside_networks: set[addr];
global outside_networks: set[addr];

event new_connection(c: connection)
    {
    ++my_count;
    if ( my_count <= 10 )
    {
        print fmt("The connection %s from %s on port %s to %s on port %s started at %s.", c$uid, c$id$orig_h, c$id$orig_p, c$id$resp_h, c$id$resp_p, strftime("%D %H:%M", c$start_time)); 
        #print c$start_time;
    }
    if ( c$id$orig_h in local_subnets)
        {
    add inside_networks[c$id$orig_h];
        }
    else
        add outside_networks[c$id$orig_h];
        
    if ( c$id$resp_h in local_subnets)
        {
        add inside_networks[c$id$resp_h];
        }
    else
        add outside_networks[c$id$resp_h];
    }

event connection_state_remove(c: connection)
    {
    if ( my_count <= 10 )
        {
        print fmt("Connection %s took %s seconds", c$uid, c$duration);  
        }
    }

event zeek_done() 
    {
    print fmt("Saw %d new connections", my_count);
    print "These IPs are considered local";
    for (a in inside_networks)
        {
        print a;
        }
    print "These IPs are considered external";
    for (a in outside_networks)
        {
        print a;
        }
    }

The connection Cy1Vgd4iLmOBM7JTD3 from 192.168.1.1 on port 626/udp to 224.0.0.1 on port 626/udp started at 11/18/09 08:00.
Connection CJj7Bl4if8upezvIAk took 163.0 msecs 820.028305 usecs seconds

Module

  • Module定義一個(gè)新的命名空間, 通過(guò)export塊來(lái)使module外的文件調(diào)用module內(nèi)的內(nèi)容和函數(shù)

  • 用戶(hù)不能定義新的協(xié)議相關(guān)事件, 但可以通過(guò)Module實(shí)現(xiàn)新的解析器或?qū)崿F(xiàn)特定功能的協(xié)議無(wú)關(guān)事件

  • export中的元素聲明為global或type(user-defined type)才能被外部文件調(diào)用

  • Zeek packages中每個(gè)Module都包含最少兩個(gè)文件澎怒,其中有load.zeek,且load.zeek須列出Module中其他所有文件阶牍。包含所有Module文件的目錄名稱(chēng)即為Module的名字喷面?因?yàn)楫?dāng)Zeek加載一個(gè)Module時(shí)星瘾,會(huì)找到這個(gè)目錄并讀取load.zeek文件。
    main.zeek

@load factorial

event zeek_done()
    {
    local numbers: vector of count = vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);    
    for ( n in numbers )
        print fmt("%d", Factor::factorial(numbers[n]));
    }

factorial.zeek

module Factor;

export {
    global factorial: function(n: count): count;
    }
    
function factorial(n: count): count
    {
    if ( n == 0 )
        return 1;
    
    else
        return ( n * factorial(n - 1) );
    }

Manipulating Logs

  • redef enum Log::ID += { LOG }惧辈;
    給Log::ID枚舉(Zeek logging framework的一部分)添加一個(gè)新id死相,名字可以換,每個(gè)ID代表唯一的日志流咬像。

  • &redef屬性可以使所指global算撮,const對(duì)象,亦或是type用戶(hù)定義對(duì)象進(jìn)行重定義

  • Log::create_stream需要在zeek_init()事件中創(chuàng)建日志流

main.zeek

@load factorial

event zeek_init()
    {
    # Create the logging stream.
    Log::create_stream(Factor::LOG, [$columns=Factor::Info, $path="factor"]);
    }

event zeek_done()
    {
    local numbers: vector of count = vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);    
    for ( n in numbers )
        Log::write( Factor::LOG, [$num=numbers[n],
                                  $factorial_num=Factor::factorial(numbers[n])]);
    }

factorial.zeek

module Factor;

export {
    # Append the value LOG to the Log::ID enumerable.
    redef enum Log::ID += { LOG };

    # Define a new type called Factor::Info.
    type Info: record {
        num:           count &log;
        factorial_num: count &log;
        };
    global factorial: function(n: count): count;
    }
    
function factorial(n: count): count
    {
    if ( n == 0 )
        return 1;
    
    else
        return ( n * factorial(n - 1) );
    }

Filtering Logs

  • 聲明filter函數(shù)和名稱(chēng)
    local ttt: Log::Filter=[name="test",path_func=Module_name::filter_name]]
  • 添加filter
    Log::add_filter(Module_name::LOG, ttt);
  • 移除默認(rèn)filter
    Log::remove_filter(Module_name::LOG, "default");
  • 例中filter函數(shù)
    function mod5(id: Log::ID, path: string, rec: Factor::Info) : string
    其中rec為column的record數(shù)據(jù)結(jié)構(gòu)县昂,返回值為

main.zeek

@load factorial

event zeek_init()
    {
    Log::create_stream(Factor::LOG, [$columns=Factor::Info, $path="factor"]);
    
    local filter: Log::Filter = [$name="split-mod5s", $path_func=Factor::mod5];
    Log::add_filter(Factor::LOG, filter);
    Log::remove_filter(Factor::LOG, "default");
    }

event zeek_done()
    {
    local numbers: vector of count = vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);    
    for ( n in numbers )
        Log::write( Factor::LOG, [$num=numbers[n],
                                  $factorial_num=Factor::factorial(numbers[n])]);
    }

factorial.zeek

module Factor;

export {
    # Append the value LOG to the Log::ID enumerable.
    redef enum Log::ID += { LOG };

    # Define a new type called Factor::Info.
    type Info: record {
        num:           count &log;
        factorial_num: count &log;
        };
    global factorial: function(n: count): count;
    global mod5: function(id: Log::ID, path: string, rec: Factor::Info) : string;
    }
    
function factorial(n: count): count
    {
    if ( n == 0 )
        return 1;
    
    else
        return ( n * factorial(n - 1) );
    }
    
function mod5(id: Log::ID, path: string, rec: Factor::Info) : string    
    {
    if ( rec$factorial_num % 5 == 0 )
        return "factor-mod5";
    
    else
        return "factor-non5";
    }

Notice

https://docs.zeek.org/en/current/frameworks/notice.html
redef enum Notice::Type += { Interestring_Result}肮柜;

NOTICE([$note=Factor::Interesting_Result, $msg = "Something happened!", $sub = fmt("result = %d", result)]);

SumStats Framework

  • Outputs
    192.168.1.102 did 17 total and 6 unique DNS requests in the last 6 hours.
    192.168.1.104 did 20 total and 10 unique DNS requests in the last 6 hours.
    192.168.1.103 did 18 total and 6 unique DNS requests in the last 6 hours.

  • DNS Scan e.g.

@load base/frameworks/sumstats
event zeek_init()
    {
    local r1 = SumStats::Reducer($stream="dns.lookup", $apply=set(SumStats::UNIQUE));
    SumStats::create([$name="dns.requests.unique",
                      $epoch=6hrs,
                      $reducers=set(r1),
                      $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) =
                        {
                        local r = result["dns.lookup"];
                        print fmt("%s did %d total and %d unique DNS requests in the last 6 hours.", 
                                    key$host, r$num, r$unique);
                        }]);
    }

event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count)
    {
    if ( c$id$resp_p == 53/udp && query != "" )
        SumStats::observe("dns.lookup", [$host=c$id$orig_h], [$str=query]);
    }
  • 首先SumStats通過(guò)dns_request事件來(lái)統(tǒng)計(jì)獨(dú)立的DNS請(qǐng)求,通過(guò)SumStats的observe()建立一個(gè)observer來(lái)統(tǒng)計(jì)流中IP為第二個(gè)參數(shù)值倒彰,內(nèi)含字符串第三個(gè)參數(shù)的request數(shù)量审洞,并存進(jìn)observation stream,命名此流為dns.lookup

  • 第二步為統(tǒng)計(jì)i.e. "reduce" 流名為"dns.lookup"的observation stream. 首先聲明一個(gè)Reducer待讳,將流指向dns.lookup芒澜,后面的參數(shù)為統(tǒng)計(jì)i.e. "reduce"函數(shù)的集合,至少規(guī)定一種統(tǒng)計(jì)方法创淡,這里用了UNIQUE痴晦,可以參閱文檔sumstats reference

  • 第三步為將前面的Reducer連接到Sumstats。SumStats本體也需要名字琳彩,為了被引用誊酌,這里命名為"dns.requests.unique". epoch設(shè)置了持續(xù)時(shí)間,類(lèi)型為interval.
    SumStats::create()的參數(shù)為一個(gè)用戶(hù)自定義的record SumStats::SumStat露乏,其中name, interval, reducers為必選項(xiàng)碧浊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瘟仿,隨后出現(xiàn)的幾起案子箱锐,更是在濱河造成了極大的恐慌,老刑警劉巖劳较,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驹止,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡兴想,警方通過(guò)查閱死者的電腦和手機(jī)幢哨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嫂便,“玉大人捞镰,你說(shuō)我怎么就攤上這事。” “怎么了岸售?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵践樱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我凸丸,道長(zhǎng)拷邢,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任屎慢,我火速辦了婚禮瞭稼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腻惠。我一直安慰自己环肘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布集灌。 她就那樣靜靜地躺著悔雹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪欣喧。 梳的紋絲不亂的頭發(fā)上腌零,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音唆阿,去河邊找鬼益涧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛酷鸦,可吹牛的內(nèi)容都是我干的饰躲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼臼隔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了妄壶?” 一聲冷哼從身側(cè)響起摔握,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丁寄,沒(méi)想到半個(gè)月后氨淌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伊磺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年盛正,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屑埋。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡豪筝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情续崖,我是刑警寧澤敲街,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站严望,受9級(jí)特大地震影響多艇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜像吻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一峻黍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拨匆,春花似錦奸披、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至洪鸭,卻和暖如春样刷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背览爵。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工置鼻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜓竹。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓箕母,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親俱济。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嘶是,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容