Zeek0x02_Zeek文件结构&日志&脚本基础

admin 2024年11月24日21:36:04评论9 views字数 17779阅读59分15秒阅读模式

文件结构

文件结构如下:

Zeek0x02_Zeek文件结构&日志&脚本基础

logs文件夹下是log文件相关信息,注意,只有在zeek运行时才会有这些文件:

Zeek0x02_Zeek文件结构&日志&脚本基础

并且在2024-11-04(当天日期)文件夹下,有各类log文件的压缩包。

Zeek0x02_Zeek文件结构&日志&脚本基础

bin目录下为二进制文件,而share目录下包含zeek附带的所有脚本。

Zeek 附带了一个用于本地自定义的脚本local.zeek。后续升级不会覆盖此文件,要使用该脚本,只需将其添加到命令行(zeek -i en0 local)或通过 @load加载它。如果是 ZeekControl,会自动加载。

日志

日志格式

TSV

生成默认日志:

Zeek0x02_Zeek文件结构&日志&脚本基础

Zeek0x02_Zeek文件结构&日志&脚本基础

通过awk处理日志:

Zeek0x02_Zeek文件结构&日志&脚本基础

通过zeek-cut处理日志:

Zeek0x02_Zeek文件结构&日志&脚本基础

JSON

生成json日志:

Zeek0x02_Zeek文件结构&日志&脚本基础

Zeek0x02_Zeek文件结构&日志&脚本基础

jq处理日志:

Zeek0x02_Zeek文件结构&日志&脚本基础

日志内容

以下面的conn.log为例:

{"ts":1729659418.809245,"uid":"ChNfYL1dVVR827J2z1","id.orig_h":"10.21.9.50","id.orig_p":50256,"id.resp_h":"10.21.15.21","id.resp_p":8300,"proto":"tcp","duration":10.472655773162842,"orig_bytes":208211,"resp_bytes":252951,"conn_state":"SF","local_orig":true,"local_resp":true,"missed_bytes":0,"history":"DdAaFf","orig_pkts":110,"orig_ip_bytes":213931,"resp_pkts":98,"resp_ip_bytes":258047,"tunnel_parents":["CAcDHN3mvd29FiHQY7"]}

ts为时间戳,解析结果如下:

Zeek0x02_Zeek文件结构&日志&脚本基础

id.orig_h,id.orig_p,id.resp_h,id.resp_p分别代表源ip,port和目的ip,port。

duration表示会话持续时间,为10.472655773162842秒。

10.21.9.50在其应用层发送了208211字节的数据,在IP层发送了213931字节数据。

可通过如下命令查看具体内容:

tshark -V -r log.pcap http and ip.src==ip //显示指定源IP的HTTP数据包的详细解析信息tshark -x -r log.pcap http and ip.src==ip //以十六进制和ASCII格式输出指定源IP的HTTP数据包内容

Zeek0x02_Zeek文件结构&日志&脚本基础

更多日志信息见:https://docs.zeek.org/en/master/logs/index.html

脚本

脚本基础

##! Detect file downloads that have hash values matching files in Team##! Cymru's Malware Hash Registry (http://www.team-cymru.org/Services/MHR/).@loadbase/frameworks/files@loadbase/frameworks/notice@load frameworks/files/hash-all-filesmoduleTeamCymruMalwareHashRegistry;export{    redef enumNotice::Type+={## The hash value of a file transferred over HTTP matched in the## malware hash registry.Match};## File types to attempt matching against the Malware Hash Registry.    option match_file_types =/application/x-dosexec/|/application/vnd.ms-cab-compressed/|/application/pdf/|/application/x-shockwave-flash/|/application/x-java-applet/|/application/jar/|/video/mp4/;## The Match notice has a sub message with a URL where you can get more## information about the file. The %s will be replaced with the SHA-1## hash of the file.    option match_sub_url ="https://www.virustotal.com/en/search/?query=%s";## The malware hash registry runs each malware sample through several## A/V engines.  Team Cymru returns a percentage to indicate how## many A/V engines flagged the sample as malicious. This threshold## allows you to require a minimum detection rate.    option notice_threshold =10;}function do_mhr_lookup(hash:string,fi:Notice::FileInfo){local hash_domain = fmt("%s.malware.hash.cymru.com", hash);when(local MHR_result = lookup_hostname_txt(hash_domain)){# Data is returned as "<dateFirstDetected> <detectionRate>"local MHR_answer = split_string1(MHR_result,/ /);if(|MHR_answer|==2){local mhr_detect_rate = to_count(MHR_answer[1]);if( mhr_detect_rate >= notice_threshold ){local mhr_first_detected = double_to_time(to_double(MHR_answer[0]));local readable_first_detected = strftime("%Y-%m-%d %H:%M:%S", mhr_first_detected);local message = fmt("Malware Hash Registry Detection rate: %d%%  Last seen: %s", mhr_detect_rate, readable_first_detected);local virustotal_url = fmt(match_sub_url, hash);# We don't have the full fa_file record here in order to# avoid the "when" statement cloning it (expensive!).local n:Notice::Info=Notice::Info($note=Match, $msg=message, $sub=virustotal_url);Notice::populate_file_info2(fi, n);                NOTICE(n);}}}}event file_hash(f: fa_file, kind:string, hash:string){if( kind =="sha1"&& f?$info && f$info?$mime_type &&         match_file_types in f$info$mime_type )        do_mhr_lookup(hash,Notice::create_file_info(f));}

借助zeek自带的脚本detect-MHR.zeek开始介绍。

该脚本可以分为3部分:

模块和导入语句配置选项函数和事件处理

模块和导入语句

这部分包括:

@loadbase/frameworks/files@loadbase/frameworks/notice@load frameworks/files/hash-all-files

使用 @load 指令导入所需的框架和模块,处理正在加载的相应目录中的 __load__.zeek 脚本,允许脚本使用 Zeek 的基本功能和特定的库。

配置选项

这部分包括:

export{    redef enumNotice::Type+={## The hash value of a file transferred over HTTP matched in the## malware hash registry.Match};## File types to attempt matching against the Malware Hash Registry.    option match_file_types =/application/x-dosexec/|/application/vnd.ms-cab-compressed/|/application/pdf/|/application/x-shockwave-flash/|/application/x-java-applet/|/application/jar/|/video/mp4/;## The Match notice has a sub message with a URL where you can get more## information about the file. The %s will be replaced with the SHA-1## hash of the file.    option match_sub_url ="https://www.virustotal.com/en/search/?query=%s";## The malware hash registry runs each malware sample through several## A/V engines.  Team Cymru returns a percentage to indicate how## many A/V engines flagged the sample as malicious. This threshold## allows you to require a minimum detection rate.    option notice_threshold =10;}

这里首先重定义了 Notice::Type,增加了一个新的通知类型 Match。这表示当通过 HTTP 传输的文件哈希值与恶意软件哈希注册表中的某个条目匹配时,将触发一个新的通知。

match_file_types处定义了部分文件类型,脚本将重点检查这些特定类型的文件,以查找潜在的恶意内容。

当生成匹配的通知时,match_sub_url定义了一个包含文件 SHA-1 哈希值的 URL。

notice_threshold选项定义了一个阈值,表示在判断恶意软件时需要满足的最低检测率。

函数和事件处理

这部分包括:

function do_mhr_lookup(hash:string,fi:Notice::FileInfo){local hash_domain = fmt("%s.malware.hash.cymru.com", hash);when(local MHR_result = lookup_hostname_txt(hash_domain)){# Data is returned as "<dateFirstDetected> <detectionRate>"local MHR_answer = split_string1(MHR_result,/ /);if(|MHR_answer|==2){local mhr_detect_rate = to_count(MHR_answer[1]);if( mhr_detect_rate >= notice_threshold ){local mhr_first_detected = double_to_time(to_double(MHR_answer[0]));local readable_first_detected = strftime("%Y-%m-%d %H:%M:%S", mhr_first_detected);local message = fmt("Malware Hash Registry Detection rate: %d%%  Last seen: %s", mhr_detect_rate, readable_first_detected);local virustotal_url = fmt(match_sub_url, hash);# We don't have the full fa_file record here in order to# avoid the "when" statement cloning it (expensive!).local n:Notice::Info=Notice::Info($note=Match, $msg=message, $sub=virustotal_url);Notice::populate_file_info2(fi, n);                NOTICE(n);}}}}event file_hash(f: fa_file, kind:string, hash:string){if( kind =="sha1"&& f?$info && f$info?$mime_type &&         match_file_types in f$info$mime_type )        do_mhr_lookup(hash,Notice::create_file_info(f));}

这段代码定义了一个用于检测和生成恶意软件通知的功能块。当 Zeek 的文件分析框架生成一个文件哈希时,会触发 file_hash 事件处理器。这个处理器将会检查生成的哈希是否是 SHA1,并且判断该文件的 MIME 类型是否符合已定义的特定类型。如果都满足,将调用 do_mhr_lookup 函数,查询 Team Cymru 的恶意软件哈希注册表,获取关于这个哈希的检测信息。如果该哈希在恶意软件数据库中被检测到,并且检测率达到了预设的阈值,则生成一条通知,包括检测率和最后检测时间。

语法

$是分隔符,例如,源主机通过 c$id$orig_h 进行引用,其中 id 是一个成员,而 orig_h 是 id 的一个成员。这个 id 是传递给事件处理程序 c 的数据结构的一部分。

scope

变量声明可以带SCOPE name或者不带,形式如下:

SCOPE name: TYPESCOPE name = EXPRESSION

event zeek_init(){local a:int;        a =10;local b =10;if(a == b)print fmt("A: %d, B: %d", a, b);}

全局变量

全局变量在没有命名空间时可被所有脚本访问。而在命名空间中声明的全局变量仅对该命名空间内的脚本可用。如果在 export { ... } 块中声明,则须通过 <module name>::<variable name> 形式来访问。

常量

在 Zeek 中,常量通过 const 关键字来定义。常量与全局变量不同,它们只能在解析时(parse time)被设置或修改,除非使用了 &redef 属性。在运行时,常量是不可改变的。常见情况下,重定义常量用于 Zeek 脚本中的配置选项,例如控制日志记录、指定选项和其他参数。 常量可以声明如下:

module HTTP;export{## 此设置决定是否捕获 Basic-Auth 中使用的密码。const default_capture_password = F &redef;}

这里,default_capture_password 是一个配置选项,默认值为 F(假)。其后,如果需要全局开启捕获密码选项,可以在 site/local.zeek 文件中添加以下行:

redef HTTP::default_capture_password = T;

局部变量

局部变量在函数处理完毕后便会被删除,从而释放占用的内存。

以下是一个使用局部变量的示例:

function add_two(i: count): count{local added_two = i +2;# 定义局部变量 added_twoprint fmt("i + 2 = %d", added_two);# 打印结果return added_two;# 返回 added_two}event zeek_init(){local test = add_two(10);# 在此处调用 add_two 函数}

数据结构

Data Type Description
int 64 bit signed integer
count 64 bit unsigned integer
double double precision floating precision
bool boolean (T/F)
addr IP address, IPv4 and IPv6
port transport layer port
subnet CIDR subnet mask
time absolute epoch time
interval a time interval
pattern regular expression

sets

event zeek_init(){local ssl_ports:set[port];local non_ssl_ports =set(23/tcp,80/tcp,143/tcp,25/tcp );# SSH    add ssl_ports[22/tcp];# HTTPS    add ssl_ports[443/tcp];# IMAPS    add ssl_ports[993/tcp];# Check for SMTPS if(587/tcp !in ssl_ports )        add ssl_ports[587/tcp];for( i in ssl_ports )print fmt("SSL Port: %s", i);for( i in non_ssl_ports )print fmt("Non-SSL Port: %s", i);}

Zeek0x02_Zeek文件结构&日志&脚本基础

tables

event zeek_init(){local samurai_flicks: table[string,string, count,string] of string;    samurai_flicks["Kihachi Okamoto","Toho",1968,"Tatsuya Nakadai"]="Kiru";    samurai_flicks["Hideo Gosha","Fuji",1969,"Tatsuya Nakadai"]="Goyokin";    samurai_flicks["Masaki Kobayashi","Shochiku Eiga",1962,"Tatsuya Nakadai"]="Harakiri";    samurai_flicks["Yoji Yamada","Eisei Gekijo",2002,"Hiroyuki Sanada"]="Tasogare Seibei";for([d, s, y, a]in samurai_flicks )print fmt("%s was released in %d by %s studios, directed by %s and starring %s", samurai_flicks[d, s, y, a], y, s, d, a);}

Zeek0x02_Zeek文件结构&日志&脚本基础

vectors

event zeek_init(){local v1: vector of count;local v2 = vector(1,2,3,4);    v1 +=1;    v1 +=2;    v1 +=3;    v1 +=4;print fmt("contents of v1: %s", v1);print fmt("length of v1: %d",|v1|);print fmt("contents of v2: %s", v2);print fmt("length of v2: %d",|v2|);}

Zeek0x02_Zeek文件结构&日志&脚本基础

数据类型

addr

在 Zeek 中,addr 数据类型用于表示网络地址,包括 IPv4、IPv6 和主机名常量。IPv4 地址采用点分十进制格式,而 IPv6 地址使用 RFC 2373 的表示法,并用方括号括起来。对于主机名常量,Zeek 会自动执行 DNS 查询,将其转换为一组地址(set[addr])。

event zeek_init(){local google:set[addr];    google = www.google.com;print(google);}

Zeek0x02_Zeek文件结构&日志&脚本基础

port

port的格式为:<unsigned ingeger>/<protocal name>,例如22/tcp

subnet

event zeek_init(){local subnets = vector(172.16.0.0/20,172.16.16.0/20,172.16.32.0/20,[2001:db8:b120::]/64);local addresses = vector(172.16.4.56,172.16.47.254,172.16.1.1,[2001:db8:b120::1]);for( a in addresses ){for( s in subnets ){if( addresses[a]in subnets[s])print fmt("%s belongs to subnet %s", addresses[a], subnets[s]);}}}

Zeek0x02_Zeek文件结构&日志&脚本基础

time

在 Zeek 中,current_time 和 network_time 是两个内置函数,用于处理时间数据类型。current_time 返回操作系统的时间,而 network_time 返回最后处理的数据包的时间戳。

interval

在 Zeek 中,interval 数据类型表示相对时间,通过一个数字常量和一个时间单位表示,如 2.2sec 或 31daysinterval 还可以执行各种数学运算,如加法、减法和比较操作。

pattern

event zeek_init(){local test_string ="The quick brown fox jumps over the lazy dog.";local test_pattern =/quick|lazy/;if( test_pattern in test_string ){local results = split_string(test_string, test_pattern);print results[0];print results[1];print results[2];}}

Zeek0x02_Zeek文件结构&日志&脚本基础

record

record 类型和 type 关键字,类似于在 C 语言中使用 typedef 和 struct 关键字,来定义新的数据结构。

当与 type 关键字结合使用时,record 可以生成一个复合类型。例如下面的 Conn::Info:

moduleConn;export{## The record type which contains column fields of the connection log.    type Info: record {        ts:           time            &log;        uid:string&log;        id:           conn_id         &log;        proto:        transport_proto &log;        service:string&log &optional;        duration:     interval        &log &optional;        orig_bytes:   count           &log &optional;        resp_bytes:   count           &log &optional;        conn_state:string&log &optional;        local_orig:bool&log &optional;        local_resp:bool&log &optional;        missed_bytes: count           &log &default=0;        history:string&log &optional;        orig_pkts:     count      &log &optional;        orig_ip_bytes: count      &log &optional;        resp_pkts:     count      &log &optional;        resp_ip_bytes: count      &log &optional;        tunnel_parents:set[string]&log;};}

由于这个类型定义在一个 export 块内,因此实际上定义的是 Conn::Info。

在 Zeek 中,record类型声明的格式包括:(被定义类型的描述性名称)+(构成该记录的各个字段)。

构成新记录的单个字段在类型或数量上没有限制,只要每个字段的名称是唯一的。

type Service: record {    name:string;    ports:set[port];    rfc: count;};function print_service(serv:Service){print fmt("Service: %s(RFC%d)",serv$name, serv$rfc);for( p in serv$ports )print fmt("  port: %s", p);}event zeek_init(){local dns:Service=[$name="dns", $ports=set(53/udp,53/tcp), $rfc=1035];local http:Service=[$name="http", $ports=set(80/tcp,8080/tcp), $rfc=2616];    print_service(dns);    print_service(http);}

Zeek0x02_Zeek文件结构&日志&脚本基础

record还可以作为另一个record中的字段。例如:

type Service: record {    name:string;    ports:set[port];    rfc: count;};type System: record {    name:string;    services:set[Service];};function print_service(serv:Service){print fmt("  Service: %s(RFC%d)",serv$name, serv$rfc);for( p in serv$ports )print fmt("    port: %s", p);}function print_system(sys:System){print fmt("System: %s", sys$name);for( s in sys$services )        print_service(s);}event zeek_init(){local server01:System;    server01$name ="morlock";    add server01$services[[ $name="dns", $ports=set(53/udp,53/tcp), $rfc=1035]];    add server01$services[[ $name="http", $ports=set(80/tcp,8080/tcp), $rfc=2616]];    print_system(server01);}

Zeek0x02_Zeek文件结构&日志&脚本基础

一般还使用record为数据结构创建一个更具描述性的名称,例如:

type string_array: table[count] of string;type string_set:set[string];type addr_set:set[addr];

自定义日志

用到最多的就是日志框架(Logging Framework),其中包括了日志流(Log Streams)、过滤器(Filters)和写入器(Writers)。

数据基于 Zeek 脚本中的决策过程写入日志流,然后,这些数据可以通过日志过滤器过滤、修改或重定向。过滤器可用于将日志文件拆分为子集或将该信息复制到另一个输出。数据的最终输出由写入器定义。Zeek 的默认写入器是简单的以制表符分隔的 ASCII 文件,但 Zeek 还支持 DataSeries 和 Elasticsearch 输出。

下面的例子将记录数字 1 到 10 及其对应的阶乘到默认的 ASCII 日志写入器中。

这是希望得到的结果:

moduleFactor;function factorial(n: count): count{if( n ==0)return1;elsereturn( n * factorial(n -1));}event zeek_init(){local numbers: vector of count = vector(1,2,3,4,5,6,7,8,9,10);for( n in numbers )print fmt("%d", factorial(numbers[n]));}

Zeek0x02_Zeek文件结构&日志&脚本基础

整合日志框架:

moduleFactor;export{    redef enumLog::ID +={ LOG };    type Info: record {        num:           count &log;        factorial_num: count &log;};}function factorial(n: count): count{if( n ==0)return1;elsereturn( n * factorial(n -1));}event zeek_init(){Log::create_stream(LOG,[$columns=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=factorial(numbers[n])]);}

首先定义了一个模块 Factor,用于计算并记录数字的阶乘。接着通过 redef enum Log::ID += { LOG }; 创建了一个新的日志标识符 LOG,用于识别即将创建的日志流。然后,定义了一个记录类型 Factor::Info,包含输入的数字和其阶乘,两个字段均带有 &log 属性,代表它们能够被 Logging Framework 所记录。脚本在初始化事件中调用 Log::create_stream(LOG, [$columns=Info, $path="factor"]); 创建一个名为 factor 的日志流,并在zeek_done中遍历 1 到 10 的数字,计算其阶乘并通过 Log::write(Factor::LOG, ...) 方法将结果写入日志,从而实现了数字及其相应阶乘的记录功能。

Zeek0x02_Zeek文件结构&日志&脚本基础

如图所示,最终执行脚本没没有标准输出,而是将结果写入了factor日志。

过滤器为 Zeek 脚本提供了自定义的能力。过滤器可以选择性地包含或排除日志字段,甚至修改日志文件的保存路径。每个日志流在创建时都会默认有一个名为 default 的过滤器。当使用该默认过滤器时,所有带有 &log 属性的键值对都会被写入到同一个文件中。

下面尝试将阶乘为5的因子的日志写入另一个文件:

moduleFactor;export{    redef enumLog::ID +={ LOG };    type Info: record {        num:           count &log;        factorial_num: count &log;};}function factorial(n: count): count{if( n ==0)return1;elsereturn(n * factorial(n -1));}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=factorial(numbers[n])]);}function mod5(id:Log::ID, path:string, rec:Factor::Info):string{if( rec$factorial_num %5==0)return"factor-mod5";elsereturn"factor-non5";}event zeek_init(){Log::create_stream(LOG,[$columns=Info, $path="factor"]);local filter:Log::Filter=[$name="split-mod5s", $path_func=mod5];Log::add_filter(Factor::LOG, filter);Log::remove_filter(Factor::LOG,"default");}

跟前一个脚本相比,差异主要在于func mod5和event zeek_init:

mod5:该方法根据factorial_num的不同结果,返回两个路径。zeek_init:同样是先创建一个日志流,但是随后定义了一个过滤器,名为split-mod5s,路径函数为mod5,并在最后移除了默认过滤器并添加了自定义过滤器。

Zeek0x02_Zeek文件结构&日志&脚本基础

再次执行脚本,日志的输出符合预期。

在下面的脚本中,相较于前一个版本,新增了自定义事件 log_factor,用于实时处理日志记录。通过在导出模块中定义该事件,并在调用 Log::create_stream 时将其设置为 $ev=log_factor,脚本能够在每次将数字及其阶乘写入日志时触发该事件。这种设计允许用户在日志生成的同时实施进一步的数据处理,增强了日志系统的灵活性和可扩展性。而之前的脚本仅通过 Log::write 将数据写入日志文件,缺乏动态交互和实时处理能力。

moduleFactor;export{    redef enumLog::ID +={ LOG };    type Info: record {        num:           count &log;        factorial_num: count &log;};global log_factor:event(rec:Info);}function factorial(n: count): count{if( n ==0)return1;elsereturn(n * factorial(n -1));}event log_factor(rec:Info){print fmt("log_factor triggered: num=%d, factorial_num=%d", rec$num, rec$factorial_num);}event zeek_init(){Log::create_stream(LOG,[$columns=Info, $ev=log_factor, $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=factorial(numbers[n])]);}function mod5(id:Log::ID, path:string, rec:Factor::Info):string{if( rec$factorial_num %5==0)return"factor-mod5";elsereturn"factor-non5";}event zeek_init(){local filter:Log::Filter=[$name="split-mod5s", $path_func=mod5];Log::add_filter(Factor::LOG, filter);Log::remove_filter(Factor::LOG,"default");}

Zeek0x02_Zeek文件结构&日志&脚本基础

创建通知

Zeek的通知框架(Notice Framework)允许脚本编写者以明确的方式来发出通知。只需要通过export声明一个特定的通知类型(Notice::Type),然后调用 NOTICE 函数,并提供适当的 Notice::Info 记录来提拉起通知即可。

在 Notice::Info 中,唯一的必需字段是 note 字段,但是最好在 msg 中包含简单的描述。如果提供了 $conn 变量,通知框架还会自动填充 $id 和 $src 字段。

核心概念

1.NOTICE 事件: Zeek 中的通知都是通过 NOTICE 事件实现的。用户可以调用 NOTICE 事件来生成一条告警日志。2.告警类型 (Notice::Type): 每条 NOTICE 都有一个类型,表示这条通知的具体含义。Zeek 默认提供了很多类型,也可以自定义。3.通知处理NOTICE 事件触发后,会生成记录到 notice.log根据配置,可以执行额外操作,如发送电子邮件或触发外部系统响应。

基础结构

触发 NOTICE 事件的基本代码如下:

NOTICE([$note=Notice::SSH_Brute_Force,        $msg="Detected possible SSH brute force attack",        $sub=fmt("Attacker IP: %s", c$id$orig_h),        $conn=c]);

其中:

$note:告警的类型,通常用枚举值(Notice::Type)表示。$msg:告警的主要信息,用于描述事件的内容。$sub:告警的附加信息,可以提供更多细节。$conn:连接信息,方便关联流量日志。$peer_descr:来源描述(可选,用于标识产生事件的 Zeek 节点)。

Notice 类型 (Notice::Type)

1. 内置类型

Zeek 提供了一些常见的告警类型,例如:

Notice::SSH_Brute_Force:SSH 暴力破解。Notice::DNS_Spoofed:DNS 被篡改。Notice::HTTP_XSS_Attack:HTTP XSS 攻击。

2. 自定义类型

用户可以根据需求定义自己的告警类型:

redef enumNotice::Type+={MyCustomNotice::Malicious_Activity};

Zeek 的 Notice 生命周期

1.生成: 脚本中通过 NOTICE 函数生成告警。2.记录: 告警会被写入 notice.log 文件。3.响应: 根据 policy/frameworks/notice/main.zeek 的配置,可以触发进一步的响应操作,如电子邮件通知。

下面的脚本在检测特定行为的同时,会通过notice进行通知:

1、捕获 SSH 登录行为,并生成通知告警:

@loadbase/frameworks/noticemoduleSSHDetection;export{    redef enumNotice::Type+={SSHDetection::SSH_Login };}event SSH::log_ssh(rec: SSH::Info){    NOTICE([$note=SSHDetection::SSH_Login,            $msg=fmt("SSH login detected from %s to %s", rec$id$orig_h, rec$id$resp_h),            $conn=rec$id]);}

SSH::log_ssh 是 Zeek 的一个事件,用于捕获 SSH 连接日志。每次触发该事件,脚本会调用 NOTICE,生成一条包含来源 IP 和目标 IP 的告警。

2. 检测 HTTP 中的恶意 User-Agent

检测 HTTP 请求中包含特定恶意标识的 User-Agent 字段。

@loadbase/frameworks/noticemoduleHTTPDetection;export{    redef enumNotice::Type+={HTTPDetection::Malicious_User_Agent};}event http_header(c: connection, is_orig:bool, name:string, value:string){if(name =="user-agent"&&/EvilBot/i in value){        NOTICE([$note=HTTPDetection::Malicious_User_Agent,                $msg=fmt("Malicious User-Agent detected: %s", value),                $conn=c]);}}

使用 http_header 事件捕获 HTTP 请求头信息。如果 User-Agent 字段中包含 "EvilBot",则生成告警。

3. 检测 DNS 中的可疑域名

检测 DNS 查询中是否包含某些可疑域名。

@loadbase/frameworks/noticemoduleDNSDetection;export{    redef enumNotice::Type+={DNSDetection::Suspicious_Domain_Query};}event dns_request(c: connection, msg: dns_msg, query:string){if(query in{"malicious.com","badguy.org"}){        NOTICE([$note=DNSDetection::Suspicious_Domain_Query,                $msg=fmt("Suspicious domain query detected: %s", query),                $conn=c]);}}

使用 dns_request 事件捕获 DNS 查询。如果域名在可疑列表中,生成通知。

事件组

基于属性

当事件或者hook具有&group属性时,就会有基于属性的事件。基于属性的事件组有一个全局命名空间,不同文件或模块中的事件处理程序,如果有相同的group属性值,就属于同一组。事件和hook可以有多个group属性。

event http_request(c: connection, method:string, original_URI:string, unescaped_URI:string, version:string)&group="http-print-debugging"{print fmt("HTTP request: %s %s (%s->%s)", method, original_URI, c$id$orig_h, c$id$resp_h);}event http_header(c: connection, is_orig:bool, original_name:string, name:string, value:string)&group="http-print-debugging"{if( name !="USER-AGENT"&& name !="SERVER")return;local snd = is_orig ? c$id$orig_h : c$id$resp_h;local rcv = is_orig ? c$id$resp_h : c$id$orig_h;print fmt("HTTP header : %s=%s (%s->%s)", original_name, value, snd, rcv);}event http_reply(c: connection, version:string, code: count, reason:string)&group="http-print-debugging"{print fmt("HTTP reply: %s/%s version %s (%s->%s)", code, reason, version, c$id$resp_h, c$id$orig_h);}

基于模块

除了基于属性的事件组外,Zeek 还支持基于模块的事件组。内置函数 disable_module_events 和 enable_module_events 可用于禁用和启用模块中的所有事件和hook处理程序。

原文始发于微信公众号(Crush Sec):Zeek0x02_Zeek文件结构&日志&脚本基础

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月24日21:36:04
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Zeek0x02_Zeek文件结构&日志&脚本基础https://cn-sec.com/archives/3431781.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息