@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=[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)碧浊。