173 lines
5.4 KiB
Plaintext
173 lines
5.4 KiB
Plaintext
@load base/protocols/smb
|
|
|
|
module Cobaltstrike;
|
|
|
|
|
|
export {
|
|
# Number of times to match pattern before logging
|
|
const beacon_iterations: count = 4 &redef;
|
|
# Max size of read/write to track
|
|
const hb_len_limit = 25;
|
|
}
|
|
|
|
export {
|
|
redef enum Notice::Type +=
|
|
{
|
|
C1::Beacon_Activity
|
|
};
|
|
}
|
|
|
|
|
|
# start with buffer overflow response
|
|
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) {
|
|
if (c$smb_state$current_cmd$command == "IOCTL" && c$smb_state$current_cmd?$status) {
|
|
if (c$smb_state$current_cmd$status == "BUFFER_OVERFLOW") {
|
|
SumStats::observe("cs_smb",
|
|
SumStats::Key($host=c$id$orig_h,
|
|
$str=cat_sep($sep="|",
|
|
$def="NULL",
|
|
c$id$orig_p,
|
|
c$id$resp_h,
|
|
c$id$resp_p)),
|
|
SumStats::Observation($num=0,
|
|
$str=cat_sep($sep="|", $def="NULL", "buffer_overflow")));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
event smb2_read_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) {
|
|
# if tagged, mark size
|
|
SumStats::observe("cs_smb",
|
|
SumStats::Key($host=c$id$orig_h,
|
|
$str=cat_sep($sep="|",
|
|
$def="NULL",
|
|
c$id$orig_p,
|
|
c$id$resp_h,
|
|
c$id$resp_p)),
|
|
SumStats::Observation($num=length,
|
|
$str=cat_sep($sep="|", $def="NULL", "read", length)));
|
|
}
|
|
|
|
event smb2_write_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) {
|
|
|
|
if (length < hb_len_limit) {
|
|
SumStats::observe("cs_smb",
|
|
SumStats::Key($host=c$id$orig_h,
|
|
$str=cat_sep($sep="|",
|
|
$def="NULL",
|
|
c$id$orig_p,
|
|
c$id$resp_h,
|
|
c$id$resp_p)),
|
|
SumStats::Observation($num=length,
|
|
$str=cat_sep($sep="|", $def="NULL", "write", length)));
|
|
}
|
|
}
|
|
|
|
function thresh_crossed(key:SumStats::Key, result:SumStats::Result)
|
|
{
|
|
|
|
local r = result["cs_smb"];
|
|
#unpack our key 0: orig_p, 1: resp_h, 2: resp_p
|
|
local keys = split_string($str = key$str, $re=/\|/);
|
|
|
|
# note data_len is a str now because of pack/unpack
|
|
#local s = fmt("Possible SMB Beacon traffic: observered %.0f writes of data length %s", r$sum, keys[2]);
|
|
local s = fmt("Potential Cobalt Strike SMB Beacon Activity.");
|
|
print fmt(s);
|
|
local cid: conn_id = [$orig_h = key$host,
|
|
$orig_p = to_port(keys[0]),
|
|
$resp_h = to_addr(keys[1]),
|
|
$resp_p = to_port(keys[2])];
|
|
|
|
# TODO: error check this
|
|
local conn = lookup_connection(cid);
|
|
# Raise Notice
|
|
NOTICE([$note=C1::Beacon_Activity,
|
|
$msg=s,
|
|
#$src=key$host,
|
|
#$dst=to_addr(keys[0]),
|
|
#$identifier=key$str,
|
|
$conn = conn,
|
|
$suppress_for=30min]);
|
|
|
|
SumStats::next_epoch("CS_SMB_Beacon");
|
|
}
|
|
|
|
function check_beacon(key: SumStats::Key, result: SumStats::Result): double {
|
|
local cmds = vector("read" ,"write", "buffer_overflow");
|
|
local mark: int = -1;
|
|
local len: count = 0;
|
|
local r_vec = SumStats::get_last(result["cs_smb"]);
|
|
for (i in r_vec)
|
|
{
|
|
local keys = split_string($str = r_vec[i]$str, $re=/\|/);
|
|
if (mark == -1)
|
|
{
|
|
for (j in cmds)
|
|
{
|
|
if (keys[0] == cmds[j])
|
|
{
|
|
# keep track of where we are in the sequence
|
|
mark = j;
|
|
# get out of this loop
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
# mark is set if first cmd write or read
|
|
if (len == 0 && r_vec[i]$num != 0)
|
|
{
|
|
len = r_vec[i]$num;
|
|
}
|
|
# len is set
|
|
if (keys[0] == cmds[mark])
|
|
{
|
|
if(cmds[mark] == "read" || cmds[mark] == "write")
|
|
{
|
|
if(len != r_vec[i]$num)
|
|
{
|
|
return 1.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cmds[mark] != "buffer_overflow")
|
|
{
|
|
#print fmt("cmd: %s\t\tvec: %s",cmds[mark], keys[0]);
|
|
return 1.0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
# move mark up
|
|
mark = (mark + 1) % 3;
|
|
|
|
}
|
|
|
|
# winner, fit the pattern all the same length
|
|
return 3.0;
|
|
}
|
|
|
|
event zeek_init()
|
|
{
|
|
|
|
local r1 = SumStats::Reducer($stream="cs_smb",
|
|
$apply=set(SumStats::LAST),
|
|
$num_last_elements = beacon_iterations * 3
|
|
);
|
|
|
|
|
|
SumStats::create([$name = "CS_SMB_Beacon",
|
|
$epoch = 0secs, #manual epochs
|
|
$reducers = set(r1),
|
|
$threshold = 2.0,
|
|
$threshold_val = check_beacon,
|
|
$threshold_crossed = thresh_crossed
|
|
]);
|
|
} |