Skip to content

Commit

Permalink
Merge pull request kevoreilly#3 from klingerko/cents_dateinrule
Browse files Browse the repository at this point in the history
add date of the analysis run of the sample to rules
  • Loading branch information
zoomequipd authored Oct 15, 2021
2 parents 5c446ef + 1c79bed commit a47e9d0
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 25 deletions.
10 changes: 7 additions & 3 deletions lib/cuckoo/common/cents/cents_azorult.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
log = logging.getLogger(__name__)


def cents_azorult(config_dict, sid_counter, md5):
def cents_azorult(config_dict, sid_counter, md5, date):
"""Creates Suricata rules from extracted Azorult malware configuration.
:param config_dict: Dictionary with the extracted Azorult configuration.
Expand All @@ -15,9 +15,12 @@ def cents_azorult(config_dict, sid_counter, md5):
:param md5: MD5 hash of the source sample.
:type md5: `int`
:param date: Timestamp of the analysis run of the source sample.
:type date: `str`
:return List of Suricata rules (`str`) or empty list if no rule has been created.
"""
if not config_dict or not sid_counter or not md5:
if not config_dict or not sid_counter or not md5 or not date:
return []

next_sid = sid_counter
Expand All @@ -26,7 +29,8 @@ def cents_azorult(config_dict, sid_counter, md5):
for address in address_list:
rule = f"alert http $HOME_NET any -> $EXTERNAL_NET any (msg:\"ET MALWARE Azorult Beacon " \
f"C2 Communication - CAPE sandbox config extraction\"; flow:established,to_server; " \
f"http.host; content:\"{address}\"; fast_pattern; reference:md5,{md5}; sid:{next_sid}; rev:1;)"
f"http.host; content:\"{address}\"; fast_pattern; reference:md5,{md5}; sid:{next_sid}; rev:1; " \
f"metadata:created_at {date};)"
next_sid += 1
rule_list.append(rule)

Expand Down
10 changes: 7 additions & 3 deletions lib/cuckoo/common/cents/cents_cobaltstrikebeacon.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
log = logging.getLogger(__name__)


def cents_cobaltstrikebeacon(config_dict, sid_counter, md5):
def cents_cobaltstrikebeacon(config_dict, sid_counter, md5, date):
"""Creates Suricata rules from extracted CobaltStrikeBeacon malware configuration.
:param config_dict: Dictionary with the extracted CobaltStrikeBeacon configuration.
Expand All @@ -15,9 +15,12 @@ def cents_cobaltstrikebeacon(config_dict, sid_counter, md5):
:param md5: MD5 hash of the source sample.
:type md5: `int`
:param date: Timestamp of the analysis run of the source sample.
:type date: `str`
:return List of Suricata rules (`str`) or empty list if no rule has been created.
"""
if not config_dict or not sid_counter or not md5:
if not config_dict or not sid_counter or not md5 or not date:
return []

next_sid = sid_counter
Expand All @@ -33,7 +36,8 @@ def cents_cobaltstrikebeacon(config_dict, sid_counter, md5):
# TODO make this better and differentiate between HTTP and HTTPS beacon types
rule = f"alert http $HOME_NET any -> $EXTERNAL_NET {port} (msg:\"ET MALWARE CobaltStrike Beacon " \
f"C2 Communication - CAPE sandbox config extraction\"; flow:established,to_server; " \
f"content:\"{c2}\"; fast_pattern; reference:md5,{md5}; sid:{next_sid}; rev:1;)"
f"content:\"{c2}\"; fast_pattern; reference:md5,{md5}; sid:{next_sid}; rev:1; " \
f"metadata:created_at {date};)"
next_sid += 1
rule_list.append(rule)

Expand Down
16 changes: 11 additions & 5 deletions lib/cuckoo/common/cents/cents_remcos.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def _parse_ratdecoders(remcos_config):
return remcos_config_list


def cents_remcos(config_dict, sid_counter, md5):
def cents_remcos(config_dict, sid_counter, md5, date):
"""Creates Suricata rules from extracted Remcos malware configuration.
:param config_dict: Dictionary with the extracted Remcos configuration.
Expand All @@ -90,9 +90,12 @@ def cents_remcos(config_dict, sid_counter, md5):
:param md5: MD5 hash of the source sample.
:type md5: `int`
:param date: Timestamp of the analysis run of the source sample.
:type date: `str`
:return List of Suricata rules (`str`) or empty list if no rule has been created.
"""
if not config_dict or not sid_counter or not md5:
if not config_dict or not sid_counter or not md5 or not date:
return []

next_sid = sid_counter
Expand Down Expand Up @@ -152,7 +155,8 @@ def cents_remcos(config_dict, sid_counter, md5):
for ip_group in _chunk_stuff(list(ip_list)):
rule = f"alert tcp $HOME_NET any -> {ip_group} any (msg:\"ET CENTS Remcos RAT (C2 IP Address) " \
f"C2 Communication - CAPE sandbox config extraction\"; flow:established,to_server; " \
f"reference:md5,{md5}; sid:{next_sid}; rev:1;)"
f"reference:md5,{md5}; sid:{next_sid}; rev:1; " \
f"metadata:created_at {date};)"
rule_list.append(rule)
next_sid += 1

Expand All @@ -161,7 +165,8 @@ def cents_remcos(config_dict, sid_counter, md5):
rule = f"alert dns $HOME_NET any -> any any (msg:\"ET CENTS Remcos RAT (C2 Domain) " \
f"C2 Communication - CAPE sandbox config extraction\"; flow:established,to_server; " \
f"dns.query; content:\"{c2_domain}\"; " \
f"reference:md5,{md5}; sid:{next_sid}; rev:1;)"
f"reference:md5,{md5}; sid:{next_sid}; rev:1; " \
f"metadata:created_at {date};)"
rule_list.append(rule)
next_sid += 1

Expand All @@ -174,7 +179,8 @@ def cents_remcos(config_dict, sid_counter, md5):
f"(passphrase {parsed_config.get('Password')}) " \
f"C2 Communication - CAPE sandbox config extraction\"; flow:established,to_server; " \
f"content:\"|{first}|\"; startswith; fast_pattern; content:\"|{second}|\"; distance:2; within:2; " \
f"reference:md5,{md5}; sid:{next_sid}; rev:1;)"
f"reference:md5,{md5}; sid:{next_sid}; rev:1; " \
f"metadata:created_at {date};)"
rule_list.append(rule)
next_sid += 1

Expand Down
13 changes: 9 additions & 4 deletions lib/cuckoo/common/cents/cents_squirrelwaffle.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
log = logging.getLogger(__name__)


def cents_squirrelwaffle(config_dict, sid_counter, md5):
def cents_squirrelwaffle(config_dict, sid_counter, md5, date):
"""Creates Suricata rules from extracted SquirrelWaffle malware configuration.
:param config_dict: Dictionary with the extracted SquirrelWaffle configuration.
Expand All @@ -16,9 +16,12 @@ def cents_squirrelwaffle(config_dict, sid_counter, md5):
:param md5: MD5 hash of the source sample.
:type md5: `int`
:param date: Timestamp of the analysis run of the source sample.
:type date: `str`
:return List of Suricata rules (`str`) or empty list if no rule has been created.
"""
if not config_dict or not sid_counter or not md5:
if not config_dict or not sid_counter or not md5 or not date:
return []

next_sid = sid_counter
Expand All @@ -38,14 +41,16 @@ def cents_squirrelwaffle(config_dict, sid_counter, md5):
http_rule = f"alert http $HOME_NET any -> $EXTERNAL_NET any (msg:\"ET CENTS SquirrelWaffle CnC " \
f"Activity\"; flow:established,to_server; http.method; content:\"POST\"; http.host; " \
f"content:\"{c2.hostname}\"; fast_pattern; reference:md5,{md5}; http.uri; " \
f"content:\"{c2.path}\"; bsize:{len(c2.path)}; sid:{next_sid}; rev:1;)"
f"content:\"{c2.path}\"; bsize:{len(c2.path)}; sid:{next_sid}; rev:1; " \
f"metadata:created_at {date};)"

rule_list.append(http_rule)
next_sid += 1

dns_rule = f"alert dns $HOME_NET any -> any any (msg:\"ET CENTS SquirrelWaffle CnC Domain in DNS Query\"; " \
f"dns.query; content:\"{c2.hostname}\"; fast_pattern; reference:md5,{md5}; sid:{next_sid}; rev" \
f":1;)"
f":1; " \
f"metadata:created_at {date};)"

rule_list.append(dns_rule)
next_sid += 1
Expand Down
10 changes: 7 additions & 3 deletions lib/cuckoo/common/cents/cents_trickbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
log = logging.getLogger(__name__)


def cents_trickbot(config_dict, sid_counter, md5):
def cents_trickbot(config_dict, sid_counter, md5, date):
"""Creates Suricata rules from extracted TrickBot malware configuration.
:param config_dict: Dictionary with the extracted TrickBot configuration.
Expand All @@ -15,9 +15,12 @@ def cents_trickbot(config_dict, sid_counter, md5):
:param md5: MD5 hash of the source sample.
:type md5: `int`
:param date: Timestamp of the analysis run of the source sample.
:type date: `str`
:return List of Suricata rules (`str`) or empty list if no rule has been created.
"""
if not config_dict or not sid_counter or not md5:
if not config_dict or not sid_counter or not md5 or not date:
return []

next_sid = sid_counter
Expand All @@ -30,7 +33,8 @@ def cents_trickbot(config_dict, sid_counter, md5):
port = s.split(":", 1)[1]
rule = f"alert ip $HOME_NET any -> {ip} {port} (msg:\"ET MALWARE TrickBot Beacon (gtag {gtag}, version {ver})" \
f" C2 Communication - CAPE sandbox config extraction\"; flow:established,to_server; " \
f"reference:md5,{md5}; sid:{next_sid}; rev:1;)"
f"reference:md5,{md5}; sid:{next_sid}; rev:1; " \
f"metadata:created_at {date};)"
next_sid += 1
rule_list.append(rule)

Expand Down
16 changes: 9 additions & 7 deletions modules/reporting/cents.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from lib.cuckoo.common.config import Config
from lib.cuckoo.common.abstracts import Report
from lib.cuckoo.common.exceptions import CuckooReportError
from lib.cuckoo.common.utils import datetime_to_iso

from lib.cuckoo.common.cents.cents_azorult import cents_azorult
from lib.cuckoo.common.cents.cents_cobaltstrikebeacon import cents_cobaltstrikebeacon
Expand Down Expand Up @@ -38,7 +39,8 @@ def run(self, results):
:raise CuckooReportError: if fails to write rules file.
"""
rule_list = []
md5 = results.get("target", {}).get("file", {}).get("md5", "")
md5 = results.get("target", {}).get("file", {}).get("md5", "") # md5 of the sample
date = datetime_to_iso(results.get("info", {}).get("started", "")).split("T", 1)[0].replace("-", "_") # timestamp of the sample run
configs = results.get("CAPE", {}).get("configs", [])
results["info"]["has_cents_rules"] = False
if not configs:
Expand All @@ -58,15 +60,15 @@ def run(self, results):
for config_name, config_dict in config.items():
rules = None
if config_name == "Azorult":
rules = cents_azorult(config_dict, self.sid_counter, md5)
rules = cents_azorult(config_dict, self.sid_counter, md5, date)
elif config_name == "CobaltStrikeBeacon":
rules = cents_cobaltstrikebeacon(config_dict, self.sid_counter, md5)
rules = cents_cobaltstrikebeacon(config_dict, self.sid_counter, md5, date)
elif config_name == "Remcos":
rules = cents_remcos(config_dict, self.sid_counter, md5)
rules = cents_remcos(config_dict, self.sid_counter, md5, date)
elif config_name == "SquirrelWaffle":
rules = cents_squirrelwaffle(config_dict, self.sid_counter, md5)
rules = cents_squirrelwaffle(config_dict, self.sid_counter, md5, date)
elif config_name == "TrickBot":
rules = cents_trickbot(config_dict, self.sid_counter, md5)
rules = cents_trickbot(config_dict, self.sid_counter, md5, date)
else:
# config for this family not implemented yet
log.debug(f"[CENTS] Config for family {config_name} not implemented yet")
Expand All @@ -80,7 +82,7 @@ def run(self, results):
log.warning(f"[CENTS] Found config for {config_name}, but couldn't create rules")

if not rule_list:
# no rules ahve been created
# no rules have been created
return

try:
Expand Down

0 comments on commit a47e9d0

Please sign in to comment.