Use Zeek to Monitor Connections to Specific Countries
Hi !
I have spoken about “Zeek” many times in the past. One of its strengths (if not one of the most interesting in my opinion) is its scripting language, which allows it to be programmed to perform the tasks one wants. Let’s imagine we want to identify all network connections to and from “friendly” countries. Since I don’t want to ostracize anyone, I’m going to include everyone in a somewhat mixed group. What interests me most is seeing who, from my information assets, initiates outbound communications to the “trusted relationships” mentioned above. Please note, and I repeat, I have deliberately included both the “not-so-bright” ones, as a friend would say, and those considered more trustworthy.
Here are a few additional important points:
- To identify who is who, Zeek will need the configuration that allows it to use geolocation (I’ve already discussed this here: https://www.openmon.org/blog/posts/post-2022090502/)
- The countries to monitor are identified by the ISO 3166 standard (https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes)
- Only connections with the “SF” flag are kept, otherwise we would have all the “noise” of the Internet (port scans, etc.)
Here is the Zeek script (“CountryMonitor.zeek”).
module CountryMonitor;
@load base/utils/site
@load base/frameworks/logging
export {
# List of countries of interest
const monitored_countries: set[string] = { "FR", "CN", "US", "RU" } &redef;
redef enum Log::ID += { LOG_COUNTRY };
type Info: record {
ts: time &log;
uid: string &log;
id: conn_id &log;
orig_cc: string &log &optional;
resp_cc: string &log &optional;
state: string &log &optional;
};
}
event zeek_init() {
Log::create_stream(CountryMonitor::LOG_COUNTRY, [
$columns=Info,
$path="country_watch"
]);
}
event connection_state_remove(c: connection) {
# Only connections with the "SF" flag are kept
if ( ! c$conn?$conn_state || c$conn$conn_state != "SF" )
return;
# Retrieving geolocation (universal syntax)
local orig_loc = lookup_location(c$id$orig_h);
local resp_loc = lookup_location(c$id$resp_h);
local o_cc = orig_loc?$country_code ? orig_loc$country_code : "XX";
local r_cc = resp_loc?$country_code ? resp_loc$country_code : "XX";
# Verification of target countries
if (o_cc in monitored_countries || r_cc in monitored_countries) {
local info: CountryMonitor::Info = [
$ts=network_time(),
$uid=c$uid,
$id=c$id,
$orig_cc=o_cc,
$resp_cc=r_cc,
$state=c$conn$conn_state
];
Log::write(CountryMonitor::LOG_COUNTRY, info);
}
}
Load it the usual way in ’local.zeek’ (for exemple).
@load CountryMonitor
Exemple output file:
1773696344.038141 CuerNs4HSdevW2LqQ8 5.143.254.86 59160 192.168.117.12 53 RU CA SF
1773696345.446331 CE6XC92s1wTA2DW5c 46.44.32.144 50246 192.168.246.8 587 RU CA SF
1773696346.236201 CQsrsU7DgxstAG7Ce 192.168.82.155 55432 180.113.220.173 20089 CA CN SF
1773696347.631179 CDybfq1ofPbMd1H5ad 172.16.24.32 41897 122.112.208.177 53 XX CN SF
1773696599.082492 CDcmn42kwB7EEbShOg 192.168.34.27 8567 220.197.69.156 15708 CA CN SF
1773696598.804429 C6dujrhpMAK5WMXU8 192.168.25.57 8567 220.197.69.156 15787 CA CN SF
1773696599.600217 C4jbL24cPRRYzuDbJe 192.168.117.12 55173 192.168.117.12 443 CN CA SF
I hope this gives you some ideas on how to use Zeek’s scripting language.
Cheers.