安装
今天我们又要来接触一种新的IDS
首先,准备好虚拟机(我使用的是Ubuntu20.04)
进入zeek官网(zeek-lts from security:zeek project)查看安装过程
选择适配的操作系统,点击 " 添加软件源并手动安装 "
对于 xUbuntu 20.04,请运行以下命令:
Keep in mind that the owner of the key may distribute updates, packages and repositories that your system will trust (more information).
echo 'deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_20.04/ /' | sudo tee /etc/apt/sources.list.d/security:zeek.list
curl -fsSL https://download.opensuse.org/repositories/security:zeek/xUbuntu_20.04/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/security_zeek.gpg > /dev/null
sudo apt update
sudo apt install zeek-lts
在terminal依次输入以上命令即可
如果在安装过程中遇到无法解决的问题,我们还可以用线上编译平台解决燃眉之急
实验
Detection Requirement
- check http sessions and if a source IP is related to three different user-agents or more
- output “xxx.xxx.xxx.xxx is a proxy” where xxx.xxx.xxx.xxx is the source IP
You Need
- a global variable to store the relationship of sourceIP to user-agent
- write a event which can return you the http header user_agent information
- you may need to study the datatype of Table, Set, String,
- to_lower(str) return a lowercase version string of the original one
- you may use print to output the alert
确定数据结构
首先我们需要确定存储IP和agent对应关系的数据结构
最简单最直接的想法:二维数组,第一维是IP,第二维存储Agent
在根据提示简单查看了Set,Table等数据类型后
我决定使用Set这种数据结构
但是当我开始申请变量的时候,遇到了一点麻烦
set[addr]
表示:集合中存储的变量是addr类型,[]
里面定义的是集合里面元素的类型
然而我们需要的是set有一个索引,索引是addr类型,set里面存储的是agent(string)
于是我又考虑使用Table类型
table [ type^+ ] of type
的定义格式
启发了我结合Table和Set两种类型:
global addrToAgent : table[addr] of set[string] = table();
处理http头部信息
这里我们选择http_header
事件
(实际上我觉得其他事件也是可以的,看下去你就会发现,我们只用到了connection这个参数)
可以看到提供的参数有
Parameters | Meaning |
---|---|
C | The connection. |
Is_orig | True if the header was sent by the originator of the TCP connection. |
Original_name | The name of the header (unaltered). |
Name | The name of the header (converted to all uppercase). |
Value | The value of the header. |
在我寻找了一万年之后,终于发现了解决方法:
connection实际上是一个record(有一点类似c里面的结构体)
有以下两个操作符
至于connection里面有什么,我们可以去Zeek Script Index: base/init-bare.zeek: Types: connection看一看
然后我们找到了一个叫做http的成员
在这里,我们终于找到了和agent相关的信息
我们需要判断connection的http成员中是否存在agent内容
如果存在,则需要提取该agent记录(为了避免大小写的影响,我们需要用to_lower
统一格式)
至于如何提取source IP
我们可以找到connection的id成员的orig_h内容
if (c$http?$user_agent) {
local src_ip=c$id$orig_h;
local user_agent=to_lower(c$http$user_agent);
}
接下来我们要把agent加入到集合中
注意需要分情况处理,因为table在第一次与set建立对应关系的时候,需要特别的初试化
if (src_ip in addrToAgent){
add (addrToAgent[src_ip])[user_agent]; //set insert
}else{
addrToAgent[src_ip]=set(user_agent); //table insert
}
输出答案
有了之前的经验,这一部分就很简单了
遍历所有ip地址(可以使用index in table
的方法)
如果对应的集合大小大于等于3,则输出针对该ip的警报
需要强调一下addr的类型转换
Zeek Types - addr
我们可以使用第二种方法(类似c语言比较好理解)把ip地址转换为字符串输出
event zeek_done() {
for (addr_ip in addrToAgent) {
if (|addrToAgent[addr_ip]| >= 3) {
print fmt("%s is a proxy", addr_ip);
}
}
}
完整代码
global addrToAgent : table[addr] of set[string] = table();
event http_header (c: connection, is_orig: bool, name: string, value: string){
if(c$http?$user_agent){
local src_ip=c$id$orig_h;
local user_agent=to_lower(c$http$user_agent);
if(src_ip in addrToAgent){
add (addrToAgent[src_ip])[user_agent];
}else{
addrToAgent[src_ip]=set(user_agent);
}
}
}
event zeek_done() {
for (addr_ip in addrToAgent) {
if (|addrToAgent[addr_ip]| >= 3) {
print fmt("%s is a proxy", addr_ip);
}
}
}