diff --git a/modules/parsers/MACO/AgentTesla.py b/modules/parsers/MACO/AgentTesla.py new file mode 100644 index 00000000..cf19350d --- /dev/null +++ b/modules/parsers/MACO/AgentTesla.py @@ -0,0 +1,63 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.AgentTesla import extract_config + + +def convert_to_MACO(raw_config: dict) -> MACOModel: + if not raw_config: + return + + protocol = raw_config.get("Protocol") + if not protocol: + return + + parsed_result = MACOModel(family="AgentTesla", other=raw_config) + if protocol == "Telegram": + parsed_result.http.append(MACOModel.Http(uri=raw_config["C2"], password=raw_config["Password"], usage="c2")) + + elif protocol in ["HTTP(S)", "Discord"]: + parsed_result.http.append(MACOModel.Http(uri=raw_config["C2"], usage="c2")) + + elif protocol == "FTP": + parsed_result.ftp.append( + MACOModel.FTP( + username=raw_config["Username"], + password=raw_config["Password"], + hostname=raw_config["C2"].replace("ftp://", ""), + usage="c2", + ) + ) + + elif protocol == "SMTP": + smtp = dict( + username=raw_config["Username"], + password=raw_config["Password"], + hostname=raw_config["C2"], + mail_to=[raw_config["EmailTo"]], + usage="c2", + ) + if "Port" in raw_config: + smtp["port"] = raw_config["Port"] + parsed_result.smtp.append(MACOModel.SMTP(**smtp)) + + if "Persistence_Filename" in raw_config: + parsed_result.paths.append(MACOModel.Path(path=raw_config["Persistence_Filename"], usage="storage")) + + if "ExternalIPCheckServices" in raw_config: + for service in raw_config["ExternalIPCheckServices"]: + parsed_result.http.append(MACOModel.Http(uri=service, usage="other")) + + return parsed_result + + +class AgentTesla(Extractor): + author = "kevoreilly" + family = "AgentTesla" + last_modified = "2024-10-20" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/AsyncRAT.py b/modules/parsers/MACO/AsyncRAT.py new file mode 100644 index 00000000..73c47158 --- /dev/null +++ b/modules/parsers/MACO/AsyncRAT.py @@ -0,0 +1,50 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.AsyncRAT import extract_config + + +def convert_to_MACO(raw_config: dict) -> MACOModel: + if not raw_config: + return + + parsed_result = MACOModel(family="AsyncRAT", other=raw_config) + + # Mutex + parsed_result.mutex.append(raw_config["Mutex"]) + + # Version + parsed_result.version = raw_config["Version"] + + # Was persistence enabled? + if raw_config["Install"] == "true": + parsed_result.capability_enabled.append("persistence") + else: + parsed_result.capability_disabled.append("persistence") + + # Installation Path + if raw_config.get("Folder"): + parsed_result.paths.append(MACOModel.Path(path=os.path.join(raw_config["Folder"], raw_config["Filename"]), usage="install")) + + # C2s + for i in range(len(raw_config.get("C2s", []))): + parsed_result.http.append(MACOModel.Http(hostname=raw_config["C2s"][i], port=int(raw_config["Ports"][i]), usage="c2")) + # Pastebin + if raw_config.get("Pastebin") not in ["null", None]: + # TODO: Is it used to download the C2 information if not embedded? + # Ref: https://www.netskope.com/blog/asyncrat-using-fully-undetected-downloader + parsed_result.http.append(MACOModel.Http(uri=raw_config["Pastebin"], usage="download")) + + return parsed_result + + +class AsyncRAT(Extractor): + author = "kevoreilly" + family = "AsyncRAT" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/AuroraStealer.py b/modules/parsers/MACO/AuroraStealer.py new file mode 100644 index 00000000..68fd9da5 --- /dev/null +++ b/modules/parsers/MACO/AuroraStealer.py @@ -0,0 +1,28 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.AuroraStealer import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="AuroraStealer", other=raw_config) + if raw_config.get("C2"): + # IP related to C2 + parsed_result.http.append(MACOModel.Http(hostname=raw_config["C2"], usage="c2")) + + return parsed_result + + +class AuroraStealer(Extractor): + author = "kevoreilly" + family = "AuroraStealer" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Azorult.py b/modules/parsers/MACO/Azorult.py new file mode 100644 index 00000000..17ed6d69 --- /dev/null +++ b/modules/parsers/MACO/Azorult.py @@ -0,0 +1,21 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Azorult import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + return MACOModel(family="Azorult", http=[MACOModel.Http(hostname=raw_config["address"])], other=raw_config) + + +class Azorult(Extractor): + author = "kevoreilly" + family = "Azorult" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/BackOffLoader.py b/modules/parsers/MACO/BackOffLoader.py new file mode 100644 index 00000000..4e75ec8e --- /dev/null +++ b/modules/parsers/MACO/BackOffLoader.py @@ -0,0 +1,32 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.BackOffLoader import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="BackOffLoader", other=raw_config) + + # Version + parsed_result.version = raw_config["Version"] + + # Encryption details + parsed_result.encryption.append( + MACOModel.Encryption(algorithm="rc4", key=raw_config["EncryptionKey"], seed=raw_config["RC4Seed"]) + ) + for url in raw_config["URLs"]: + parsed_result.http.append(MACOModel.Http(url=url)) + + return parsed_result + + +class BackOffLoader(Extractor): + author = "kevoreilly" + family = "BackOffLoader" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/BackOffPOS.py b/modules/parsers/MACO/BackOffPOS.py new file mode 100644 index 00000000..14452d0e --- /dev/null +++ b/modules/parsers/MACO/BackOffPOS.py @@ -0,0 +1,32 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.BackOffPOS import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="BackOffPOS", other=raw_config) + + # Version + parsed_result.version = raw_config["Version"] + + # Encryption details + parsed_result.encryption.append( + MACOModel.Encryption(algorithm="rc4", key=raw_config["EncryptionKey"], seed=raw_config["RC4Seed"]) + ) + for url in raw_config["URLs"]: + parsed_result.http.append(MACOModel.Http(url=url)) + + return parsed_result + + +class BackOffPOS(Extractor): + author = "kevoreilly" + family = "BackOffPOS" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/BitPaymer.py b/modules/parsers/MACO/BitPaymer.py new file mode 100644 index 00000000..70a6f8be --- /dev/null +++ b/modules/parsers/MACO/BitPaymer.py @@ -0,0 +1,28 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.BitPaymer import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="BitPaymer", other=raw_config) + + # Extracted strings + parsed_result.decoded_strings = raw_config["strings"] + + # Encryption details + parsed_result.encryption.append(MACOModel.Encryption(algorithm="rsa", public_key=raw_config["RSA public key"])) + return parsed_result + + +class BitPaymer(Extractor): + author = "kevoreilly" + family = "BitPaymer" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/BlackDropper.py b/modules/parsers/MACO/BlackDropper.py new file mode 100644 index 00000000..61575408 --- /dev/null +++ b/modules/parsers/MACO/BlackDropper.py @@ -0,0 +1,31 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.BlackDropper import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="BlackDropper", campaign_id=[raw_config["campaign"]], other=raw_config) + + for dir in raw_config.get("directories", []): + parsed_result.paths.append(MACOModel.Path(path=dir)) + + for url in raw_config.get("urls", []): + parsed_result.http.append(MACOModel.Http(uri=url)) + + return parsed_result + + +class BlackDropper(Extractor): + author = "kevoreilly" + family = "BlackDropper" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/BlackNix.py b/modules/parsers/MACO/BlackNix.py new file mode 100644 index 00000000..877fa6fa --- /dev/null +++ b/modules/parsers/MACO/BlackNix.py @@ -0,0 +1,65 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.BlackNix import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="BlackNix", other=raw_config) + + # Mutex + parsed_result.mutex.append(raw_config["Mutex"]) + + # Capabilities that are enabled/disabled + # TODO: Review if these are all capabilities set by a boolean flag + for capa in [ + "Anti Sandboxie", + "Kernel Mode Unhooking", + "User Mode Unhooking", + "Melt Server", + "Offline Screen Capture", + "Offline Keylogger", + "Copy to ADS", + "Safe Mode Startup", + "Inject winlogon.exe", + "Active X Run", + "Registry Run", + ]: + if raw_config[capa].lower() == "true": + parsed_result.capability_enabled.append(capa) + else: + parsed_result.capability_disabled.append(capa) + + # Delay Time + parsed_result.sleep_delay = raw_config["Delay Time"] + + # Password + parsed_result.password.append(raw_config["Password"]) + + # C2 Domain + parsed_result.http.append(MACOModel.Http(hostname=raw_config["Domain"], usage="c2")) + # Registry + parsed_result.registry.append(MACOModel.Registry(key=raw_config["Registry Key"])) + + # Install Path + parsed_result.paths.append( + MACOModel.Path(path=os.path.join(raw_config["Install Path"], raw_config["Install Name"]), usage="install") + ) + + # Campaign Group/Name + parsed_result.campaign_id = [raw_config["Campaign Name"], raw_config["Campaign Group"]] + return parsed_result + + +class BlackNix(Extractor): + author = "kevoreilly" + family = "BlackNix" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Blister.py b/modules/parsers/MACO/Blister.py new file mode 100644 index 00000000..f38ca7df --- /dev/null +++ b/modules/parsers/MACO/Blister.py @@ -0,0 +1,35 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Blister import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Blister", other=raw_config) + + for capa in ["Persistence", "Sleep after injection"]: + if raw_config[capa]: + parsed_result.capability_enabled.append(capa) + else: + parsed_result.capability_disabled.append(capa) + + # Rabbit encryption + parsed_result.encryption.append( + MACOModel.Encryption(algorithm="rabbit", key=raw_config["Rabbit key"], iv=raw_config["Rabbit IV"]) + ) + return parsed_result + + +class Blister(Extractor): + author = "kevoreilly" + family = "Blister" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/BruteRatel.py b/modules/parsers/MACO/BruteRatel.py new file mode 100644 index 00000000..bfc2c392 --- /dev/null +++ b/modules/parsers/MACO/BruteRatel.py @@ -0,0 +1,31 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.BruteRatel import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="BruteRatel", other=raw_config) + + for url in raw_config["C2"]: + for path in raw_config["URI"]: + parsed_result.http.append( + MACOModel.Http(uri=url, user_agent=raw_config["User Agent"], port=raw_config["Port"], path=path, usage="c2") + ) + + return parsed_result + + +class BruteRatel(Extractor): + author = "kevoreilly" + family = "BruteRatel" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/BuerLoader.py b/modules/parsers/MACO/BuerLoader.py new file mode 100644 index 00000000..e10ea070 --- /dev/null +++ b/modules/parsers/MACO/BuerLoader.py @@ -0,0 +1,27 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.BuerLoader import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="BuerLoader", other=raw_config) + + for c2 in raw_config["address"]: + parsed_result.http.append(MACOModel.Http(hostname=c2, usage="c2")) + return parsed_result + + +class BuerLoader(Extractor): + author = "kevoreilly" + family = "BuerLoader" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/BumbleBee.py b/modules/parsers/MACO/BumbleBee.py new file mode 100644 index 00000000..553523d2 --- /dev/null +++ b/modules/parsers/MACO/BumbleBee.py @@ -0,0 +1,45 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.BumbleBee import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="BumbleBee", other=raw_config) + + # Campaign ID + if raw_config.get("Campaign ID"): + parsed_result.campaign_id.append(raw_config["Campaign ID"]) + + # Botnet ID + if raw_config.get("Botnet ID"): + parsed_result.identifier.append(raw_config["Botnet ID"]) + + # C2s + for c2 in raw_config.get("C2s", []): + parsed_result.http.append(MACOModel.Http(hostname=c2, usage="c2")) + + # Data + if raw_config.get("Data"): + parsed_result.binaries.append(MACOModel.Binary(data=raw_config["Data"])) + + # RC4 Key + if raw_config.get("RC4 Key"): + parsed_result.encryption.append(MACOModel.Encryption(algorithm="rc4", key=raw_config["RC4 Key"])) + + return parsed_result + + +class BumbleBee(Extractor): + author = "kevoreilly" + family = "BumbleBee" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Carbanak.py b/modules/parsers/MACO/Carbanak.py new file mode 100644 index 00000000..bd267cdc --- /dev/null +++ b/modules/parsers/MACO/Carbanak.py @@ -0,0 +1,44 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Carbanak import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Carbanak", other=raw_config) + + # Version + if raw_config.get("Version"): + parsed_result.version = raw_config["Version"] + + # Unknown strings + for i in [1, 2]: + if raw_config.get(f"Unknown {i}"): + parsed_result.decoded_strings.append(raw_config[f"Unknown {i}"]) + + # C2 + if raw_config.get("C2"): + if isinstance(raw_config["C2"], str): + parsed_result.http.append(MACOModel.Http(hostname=raw_config["C2"], usage="c2")) + else: + for c2 in raw_config["C2"]: + parsed_result.http.append(MACOModel.Http(hostname=c2, usage="c2")) + + # Campaign Id + if raw_config.get("Campaign Id"): + parsed_result.campaign_id.append(raw_config["Campaign Id"]) + + return parsed_result + + +class Carbanak(Extractor): + author = "kevoreilly" + family = "Carbanak" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/ChChes.py b/modules/parsers/MACO/ChChes.py new file mode 100644 index 00000000..a2711bd6 --- /dev/null +++ b/modules/parsers/MACO/ChChes.py @@ -0,0 +1,27 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.ChChes import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="ChChes", other=raw_config) + + # C2 URLs + for c2_url in raw_config.get("c2_url", []): + parsed_result.http.append(MACOModel.Http(uri=c2_url, usage="c2")) + + return parsed_result + + +class ChChes(Extractor): + author = "kevoreilly" + family = "ChChes" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/CobaltStrikeBeacon.py b/modules/parsers/MACO/CobaltStrikeBeacon.py new file mode 100644 index 00000000..79a93aa4 --- /dev/null +++ b/modules/parsers/MACO/CobaltStrikeBeacon.py @@ -0,0 +1,49 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.CobaltStrikeBeacon import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="CobaltStrikeBeacon", other=raw_config) + + clean_config = {k: v for k, v in raw_config.items() if v != "Not Found"} + capabilities = {k[1:]: clean_config.pop(k) for k in list(clean_config.keys()) if clean_config[k] in ["True", "False"]} + + for capability, enabled in capabilities.items(): + if enabled.lower() == "true": + parsed_result.capability_enabled.append(capability) + else: + parsed_result.capability_disabled.append(capability) + + if "C2Server" in clean_config: + host, get_path = clean_config.pop("C2Server").split(",") + port = clean_config.pop("Port") + parsed_result.http.append(MACOModel.Http(hostname=host, port=port, method="GET", path=get_path, usage="c2")) + parsed_result.http.append( + MACOModel.Http(hostname=host, port=port, method="POST", path=clean_config.pop("HttpPostUri"), usage="c2") + ) + + parsed_result.sleep_delay = clean_config.pop("SleepTime") + parsed_result.sleep_delay_jitter = clean_config.pop("Jitter") + + for path_key in ["Spawnto_x86", "Spawnto_x64"]: + if path_key in clean_config: + parsed_result.paths.append(MACOModel.Path(path=clean_config.pop(path_key))) + + return parsed_result + + +class CobaltStrikeBeacon(Extractor): + author = "kevoreilly" + family = "CobaltStrikeBeacon" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/CobaltStrikeStager.py b/modules/parsers/MACO/CobaltStrikeStager.py new file mode 100644 index 00000000..08dc3142 --- /dev/null +++ b/modules/parsers/MACO/CobaltStrikeStager.py @@ -0,0 +1,25 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.CobaltStrikeStager import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="CobaltStrikeStager", other=raw_config) + + return parsed_result + + +class CobaltStrikeStager(Extractor): + author = "kevoreilly" + family = "CobaltStrikeStager" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/DCRat.py b/modules/parsers/MACO/DCRat.py new file mode 100644 index 00000000..f10db01d --- /dev/null +++ b/modules/parsers/MACO/DCRat.py @@ -0,0 +1,26 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.DCRat import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + # TODO: Assign fields to MACO model + parsed_result = MACOModel(family="DCRat", other=raw_config) + + return parsed_result + + +class DCRat(Extractor): + author = "kevoreilly" + family = "DCRat" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/DarkGate.py b/modules/parsers/MACO/DarkGate.py new file mode 100644 index 00000000..9bd6ae91 --- /dev/null +++ b/modules/parsers/MACO/DarkGate.py @@ -0,0 +1,51 @@ +import os +from copy import deepcopy + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.DarkGate import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="DarkGate", other=raw_config) + + # Create a copy of the raw configuration for parsing + config = deepcopy(raw_config) + + # Go through capabilities/settings that are boolean in nature + for k, v in list(config.items()): + if v not in ["Yes", "No"]: + continue + + if v == "Yes": + parsed_result.capability_enabled.append(k) + else: + parsed_result.capability_disabled.append(k) + + # Remove key from raw config + config.pop(k) + + # C2 + c2_port = config.pop("c2_port", None) + for c2_url in config.pop("C2", []): + parsed_result.http.append(MACOModel.Http(uri=c2_url, port=c2_port, usage="c2")) + + # Mutex + if config.get("internal_mutex"): + parsed_result.mutex.append(config.pop("internal_mutex")) + + return parsed_result + + +class DarkGate(Extractor): + author = "kevoreilly" + family = "DarkGate" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/DoppelPaymer.py b/modules/parsers/MACO/DoppelPaymer.py new file mode 100644 index 00000000..a4548d4c --- /dev/null +++ b/modules/parsers/MACO/DoppelPaymer.py @@ -0,0 +1,29 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.DoppelPaymer import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="DoppelPaymer") + + if "strings" in raw_config: + parsed_result.decoded_strings = raw_config["strings"] + + if "RSA public key" in raw_config: + parsed_result.encryption.append(MACOModel.Encryption(algorithm="RSA", public_key=raw_config["RSA public key"])) + + return parsed_result + + +class DoppelPaymer(Extractor): + author = "kevoreilly" + family = "DoppelPaymer" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/DridexLoader.py b/modules/parsers/MACO/DridexLoader.py new file mode 100644 index 00000000..90318d44 --- /dev/null +++ b/modules/parsers/MACO/DridexLoader.py @@ -0,0 +1,32 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.DridexLoader import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="DridexLoader", other=raw_config) + + for c2_address in raw_config.get("address", []): + parsed_result.http.append(MACOModel.Http(uri=c2_address, usage="c2")) + + if "RC4 key" in raw_config: + parsed_result.encryption.append(MACOModel.Encryption(algorithm="RC4", key=raw_config["RC4 key"])) + + if "Botnet ID" in raw_config: + parsed_result.identifier.append(raw_config["Botnet ID"]) + + return parsed_result + + +class DridexLoader(Extractor): + author = "kevoreilly" + family = "DridexLoader" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Emotet.py b/modules/parsers/MACO/Emotet.py new file mode 100644 index 00000000..c5fd49ff --- /dev/null +++ b/modules/parsers/MACO/Emotet.py @@ -0,0 +1,31 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Emotet import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Emotet", other=raw_config) + + for c2_address in raw_config.get("address", []): + parsed_result.http.append(MACOModel.Http(uri=c2_address, usage="c2")) + + if "RC4 public key" in raw_config: + parsed_result.encryption.append(MACOModel.Encryption(algorithm="RC4", public_key=raw_config["RSA public key"])) + + parsed_result.other = {k: raw_config[k] for k in raw_config.keys() if k not in ["address", "RSA public key"]} + + return parsed_result + + +class Emotet(Extractor): + author = "kevoreilly" + family = "Emotet" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Enfal.py b/modules/parsers/MACO/Enfal.py new file mode 100644 index 00000000..f79d6878 --- /dev/null +++ b/modules/parsers/MACO/Enfal.py @@ -0,0 +1,24 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Enfal import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + # TODO: Assign fields to MACO model + parsed_result = MACOModel(family="Enfal", other=raw_config) + + return parsed_result + + +class Enfal(Extractor): + author = "kevoreilly" + family = "Enfal" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/EvilGrab.py b/modules/parsers/MACO/EvilGrab.py new file mode 100644 index 00000000..4addf1f0 --- /dev/null +++ b/modules/parsers/MACO/EvilGrab.py @@ -0,0 +1,37 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.EvilGrab import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="EvilGrab", other=raw_config) + + if "mutex" in raw_config: + parsed_result.mutex.append(raw_config["mutex"]) + + if "missionid" in raw_config: + parsed_result.campaign_id.append(raw_config["missionid"]) + + if "version" in raw_config: + parsed_result.version = raw_config["version"] + + if "c2_address" in raw_config: + parsed_result.http.append( + parsed_result.Http(uri=raw_config["c2_address"], port=raw_config["port"][0] if "port" in raw_config else None) + ) + + return parsed_result + + +class EvilGrab(Extractor): + author = "kevoreilly" + family = "EvilGrab" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Fareit.py b/modules/parsers/MACO/Fareit.py new file mode 100644 index 00000000..bc7a4df9 --- /dev/null +++ b/modules/parsers/MACO/Fareit.py @@ -0,0 +1,26 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Fareit import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + # TODO: Assign fields to MACO model + parsed_result = MACOModel(family="Fareit", other=raw_config) + + return parsed_result + + +class Fareit(Extractor): + author = "kevoreilly" + family = "Fareit" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Formbook.py b/modules/parsers/MACO/Formbook.py new file mode 100644 index 00000000..aa9d9d52 --- /dev/null +++ b/modules/parsers/MACO/Formbook.py @@ -0,0 +1,31 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Formbook import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Formbook", other=raw_config) + + if "C2" in raw_config: + parsed_result.http.append(MACOModel.Http(uri=raw_config["C2"], usage="c2")) + + for decoy in raw_config.get("Decoys", []): + parsed_result.http.append(MACOModel.Http(uri=decoy, usage="decoy")) + + return parsed_result + + +class Formbook(Extractor): + author = "kevoreilly" + family = "Formbook" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Greame.py b/modules/parsers/MACO/Greame.py new file mode 100644 index 00000000..310e429c --- /dev/null +++ b/modules/parsers/MACO/Greame.py @@ -0,0 +1,22 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Greame import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Greame", other=raw_config) + + return parsed_result + + +class Greame(Extractor): + author = "kevoreilly" + family = "Greame" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/GuLoader.py b/modules/parsers/MACO/GuLoader.py new file mode 100644 index 00000000..3628097f --- /dev/null +++ b/modules/parsers/MACO/GuLoader.py @@ -0,0 +1,28 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.GuLoader import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="GuLoader", other=raw_config) + + for url in raw_config.get("URLs", []): + parsed_result.http.append(MACOModel.Http(uri=url, usage="download")) + + return parsed_result + + +class GuLoader(Extractor): + author = "kevoreilly" + family = "GuLoader" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], "data/yara/CAPE/Guloader.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Hancitor.py_deprecated.py b/modules/parsers/MACO/Hancitor.py_deprecated.py new file mode 100644 index 00000000..0d7f37f5 --- /dev/null +++ b/modules/parsers/MACO/Hancitor.py_deprecated.py @@ -0,0 +1,31 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.Hancitor import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Hancitor", other=raw_config) + + for url in raw_config.get("address", []): + parsed_result.http.append(MACOModel.Http(uri=url, usage="c2")) + + if "Build ID" in raw_config: + parsed_result.identifier.append(raw_config["Build ID"]) + + return parsed_result + + +class Hancitor(Extractor): + author = "kevoreilly" + family = "Hancitor" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/HttpBrowser.py b/modules/parsers/MACO/HttpBrowser.py new file mode 100644 index 00000000..32bac0b1 --- /dev/null +++ b/modules/parsers/MACO/HttpBrowser.py @@ -0,0 +1,34 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.HttpBrowser import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="HttpBrowser", other=raw_config) + + port = raw_config["port"][0] if "port" in raw_config else None + + if "c2_address" in raw_config: + parsed_result.http.append(MACOModel.Http(uri=raw_config["c2_address"], port=port, usage="c2")) + + if "filepath" in raw_config: + parsed_result.paths.append(MACOModel.Path(path=raw_config["filepath"])) + + if "injectionprocess" in raw_config: + parsed_result["injectionprocess"] = raw_config["injectionprocess"] + + return parsed_result + + +class HttpBrowser(Extractor): + author = "kevoreilly" + family = "HttpBrowser" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/IcedID.py b/modules/parsers/MACO/IcedID.py new file mode 100644 index 00000000..8fdf5da7 --- /dev/null +++ b/modules/parsers/MACO/IcedID.py @@ -0,0 +1,23 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.IcedID import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + return MACOModel(**raw_config) + + +class IcedID(Extractor): + author = "kevoreilly" + family = "IcedID" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/IcedIDLoader.py b/modules/parsers/MACO/IcedIDLoader.py new file mode 100644 index 00000000..66be8692 --- /dev/null +++ b/modules/parsers/MACO/IcedIDLoader.py @@ -0,0 +1,31 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.IcedIDLoader import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="IcedIDLoader", other=raw_config) + + if "C2" in raw_config: + parsed_result.http.append(MACOModel.Http(hostname=raw_config["C2"], usage="c2")) + + if "Campaign" in raw_config: + parsed_result.campaign_id.append(str(raw_config["Campaign"])) + + return parsed_result + + +class IcedIDLoader(Extractor): + author = "kevoreilly" + family = "IcedIDLoader" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/KoiLoader.py b/modules/parsers/MACO/KoiLoader.py new file mode 100644 index 00000000..c1ab0051 --- /dev/null +++ b/modules/parsers/MACO/KoiLoader.py @@ -0,0 +1,26 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.KoiLoader import RULE_SOURCE, extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="KoiLoader", other=raw_config) + + for c2_url in raw_config.get("C2", []): + parsed_result.http.append(MACOModel.Http(uri=c2_url, usage="c2")) + + return parsed_result + + +class KoiLoader(Extractor): + author = "kevoreilly" + family = "KoiLoader" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = RULE_SOURCE + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Latrodectus.py b/modules/parsers/MACO/Latrodectus.py new file mode 100644 index 00000000..7bee949b --- /dev/null +++ b/modules/parsers/MACO/Latrodectus.py @@ -0,0 +1,43 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Latrodectus import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Latrodectus", other=raw_config) + + for c2_url in raw_config.get("C2", []): + parsed_result.http.append(MACOModel.Http(uri=c2_url, usage="c2")) + + if "Group name" in raw_config: + parsed_result.identifier.append(raw_config["Group name"]) + + if "Campaign ID" in raw_config: + parsed_result.campaign_id.append(str(raw_config["Campaign ID"])) + + if "Version" in raw_config: + parsed_result.version = raw_config["Version"] + + if "RC4 key" in raw_config: + parsed_result.encryption.append(MACOModel.Encryption(algorithm="RC4", key=raw_config["RC4 key"])) + + if "Strings" in raw_config: + parsed_result.decoded_strings = raw_config["Strings"] + + return parsed_result + + +class Latrodectus(Extractor): + author = "kevoreilly" + family = "Latrodectus" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/LokiBot.py b/modules/parsers/MACO/LokiBot.py new file mode 100644 index 00000000..0332c47c --- /dev/null +++ b/modules/parsers/MACO/LokiBot.py @@ -0,0 +1,28 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.LokiBot import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="LokiBot", other=raw_config) + + for address in raw_config.get("address", []): + parsed_result.http.append(MACOModel.Http(uri=address)) + + return parsed_result + + +class LokiBot(Extractor): + author = "kevoreilly" + family = "LokiBot" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Lumma.py b/modules/parsers/MACO/Lumma.py new file mode 100644 index 00000000..d6d806d7 --- /dev/null +++ b/modules/parsers/MACO/Lumma.py @@ -0,0 +1,28 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Lumma import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Lumma", other=raw_config) + + for address in raw_config.get("C2", []): + parsed_result.http.append(MACOModel.Http(hostname=address, usage="c2")) + + return parsed_result + + +class Lumma(Extractor): + author = "kevoreilly" + family = "Lumma" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/NanoCore.py b/modules/parsers/MACO/NanoCore.py new file mode 100644 index 00000000..0fe6e321 --- /dev/null +++ b/modules/parsers/MACO/NanoCore.py @@ -0,0 +1,43 @@ +from copy import deepcopy + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.NanoCore import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="NanoCore", other=raw_config) + + config_copy = deepcopy(raw_config) + capabilities = {k: config_copy.pop(k) for k in list(config_copy.keys()) if config_copy[k] in ["True", "False"]} + + if "Version" in config_copy: + parsed_result.version = config_copy.pop("Version") + + if "Mutex" in config_copy: + parsed_result.mutex.append(config_copy.pop("Mutex")) + + for capability, enabled in capabilities.items(): + if enabled.lower() == "true": + parsed_result.capability_enabled.append(capability) + else: + parsed_result.capability_disabled.append(capability) + + for address in config_copy.pop("cncs", []): + host, port = address.split(":") + parsed_result.http.append(MACOModel.Http(hostname=host, port=port, usage="c2")) + + return parsed_result + + +class NanoCore(Extractor): + author = "kevoreilly" + family = "NanoCore" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Nighthawk.py b/modules/parsers/MACO/Nighthawk.py new file mode 100644 index 00000000..11d231e9 --- /dev/null +++ b/modules/parsers/MACO/Nighthawk.py @@ -0,0 +1,25 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Nighthawk import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Nighthawk", other=raw_config) + + return parsed_result + + +class Nighthawk(Extractor): + author = "kevoreilly" + family = "Nighthawk" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Njrat.py b/modules/parsers/MACO/Njrat.py new file mode 100644 index 00000000..e91a9138 --- /dev/null +++ b/modules/parsers/MACO/Njrat.py @@ -0,0 +1,32 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Njrat import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Njrat", other=raw_config) + + if "version" in raw_config: + parsed_result.version = raw_config["version"] + + if "campaign_id" in raw_config: + parsed_result.campaign_id.append(raw_config["campaign_id"]) + + for c2 in raw_config.get("cncs", []): + host, port = c2.split(":") + parsed_result.http.append(MACOModel.Http(hostname=host, port=port, usage="c2")) + + return parsed_result + + +class Njrat(Extractor): + author = "kevoreilly" + family = "Njrat" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Oyster.py b/modules/parsers/MACO/Oyster.py new file mode 100644 index 00000000..c26b9e36 --- /dev/null +++ b/modules/parsers/MACO/Oyster.py @@ -0,0 +1,34 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Oyster import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Oyster", other=raw_config) + + for address in raw_config.get("C2", []): + parsed_result.http.append(MACOModel.Http(uri=address, usage="c2")) + + if "Dll Version" in raw_config: + parsed_result.version = raw_config["Dll Version"] + + if "Strings" in raw_config: + parsed_result.decoded_strings = raw_config["Strings"] + + return parsed_result + + +class Oyster(Extractor): + author = "kevoreilly" + family = "Oyster" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Pandora.py b/modules/parsers/MACO/Pandora.py new file mode 100644 index 00000000..7e5983c8 --- /dev/null +++ b/modules/parsers/MACO/Pandora.py @@ -0,0 +1,49 @@ +import os +from copy import deepcopy + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Pandora import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + config_copy = deepcopy(raw_config) + parsed_result = MACOModel( + family="Pandora", + mutex=[config_copy.pop("Mutex")], + campaign_id=[config_copy.pop("Campaign ID")], + version=config_copy.pop("Version"), + http=[dict(hostname=config_copy.pop("Domain"), port=config_copy.pop("Port"), password=config_copy.pop("Password"))], + other=raw_config, + ) + + parsed_result.paths.append( + MACOModel.Path(path=os.path.join(config_copy.pop("Install Path"), config_copy.pop("Install Name")), usage="install") + ) + + parsed_result.registry.append(MACOModel.Registry(key=config_copy.pop("HKCU Key"))) + parsed_result.registry.append(MACOModel.Registry(key=config_copy.pop("ActiveX Key"))) + + for field in list(config_copy.keys()): + # TODO: Unsure what's the value of the remaining fields + if config_copy[field].lower() in ["true", "false"]: + enabled = config_copy.pop(field).lower() == "true" + if enabled: + parsed_result.capability_enabled.append(field) + else: + parsed_result.capability_disabled.append(field) + + return parsed_result + + +class Pandora(Extractor): + author = "kevoreilly" + family = "Pandora" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/PhemedroneStealer.py b/modules/parsers/MACO/PhemedroneStealer.py new file mode 100644 index 00000000..24448a71 --- /dev/null +++ b/modules/parsers/MACO/PhemedroneStealer.py @@ -0,0 +1,22 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.PhemedroneStealer import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="PhemedroneStealer", other=raw_config) + + return parsed_result + + +class PhemedroneStealer(Extractor): + author = "kevoreilly" + family = "PhemedroneStealer" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/PikaBot.py b/modules/parsers/MACO/PikaBot.py new file mode 100644 index 00000000..e9d7e3fa --- /dev/null +++ b/modules/parsers/MACO/PikaBot.py @@ -0,0 +1,34 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.PikaBot import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="PikaBot", other=raw_config) + + if "C2" in raw_config: + [parsed_result.http.append(MACOModel.Http(uri=c2, usage="c2")) for c2 in raw_config["C2"]] + parsed_result.binaries.append(MACOModel.Binary(datatype="payload", data=raw_config["Powershell"])) + elif "C2s" in raw_config: + parsed_result.version = raw_config["Version"] + parsed_result.campaign_id.append(raw_config["Campaign Name"]) + parsed_result.registry.append(MACOModel.Registry(key=raw_config["Registry Key"])) + for c2 in raw_config["C2s"]: + host, port = c2.split(":") + parsed_result.http.append(MACOModel.Http(hostname=host, port=port, user_agent=raw_config["User Agent"])) + + return parsed_result + + +class PikaBot(Extractor): + author = "kevoreilly" + family = "PikaBot" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/PlugX.py b/modules/parsers/MACO/PlugX.py new file mode 100644 index 00000000..afc77d25 --- /dev/null +++ b/modules/parsers/MACO/PlugX.py @@ -0,0 +1,22 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.PlugX import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="PlugX", other=raw_config) + + return parsed_result + + +class PlugX(Extractor): + author = "kevoreilly" + family = "PlugX" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/PoisonIvy.py b/modules/parsers/MACO/PoisonIvy.py new file mode 100644 index 00000000..d5ef2a40 --- /dev/null +++ b/modules/parsers/MACO/PoisonIvy.py @@ -0,0 +1,44 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.PoisonIvy import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="PoisonIvy", other=raw_config) + + if "Campaign ID" in raw_config: + parsed_result.campaign_id.append(raw_config["Campaign ID"]) + if "Group ID" in raw_config: + parsed_result.identifier.append(raw_config["Group ID"]) + if "Domains" in raw_config: + for domain_port in raw_config["Domains"].split("|"): + host, port = domain_port.split(":") + parsed_result.http.append(MACOModel.Http(hostname=host, port=port)) + if "Password" in raw_config: + parsed_result.password.append(raw_config["Password"]) + if "Mutex" in raw_config: + parsed_result.mutex.append(raw_config["Mutex"]) + + for field in list(raw_config.keys()): + value = raw_config[field] + if value.lower() == "true": + parsed_result.capability_enabled.append(field) + elif value.lower() == "false": + parsed_result.capability_disabled.append(field) + + return parsed_result + + +class PoisonIvy(Extractor): + author = "kevoreilly" + family = "PoisonIvy" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + output = extract_config(stream.read()) + if output: + return convert_to_MACO(output[0]) diff --git a/modules/parsers/MACO/Punisher.py b/modules/parsers/MACO/Punisher.py new file mode 100644 index 00000000..23040462 --- /dev/null +++ b/modules/parsers/MACO/Punisher.py @@ -0,0 +1,45 @@ +import os +from copy import deepcopy + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Punisher import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + config_copy = deepcopy(raw_config) + parsed_result = MACOModel( + family="Punisher", + campaign_id=config_copy["Campaign Name"], + password=[config_copy["Password"]], + registry=[MACOModel.Registry(key=config_copy["Registry Key"])], + paths=[MACOModel.Path(path=os.path.join(config_copy["Install Path"], config_copy["Install Name"]))], + http=[MACOModel.Http(hostname=config_copy["Domain"], port=config_copy["Port"])], + other=raw_config, + ) + + for field in raw_config.keys(): + value = raw_config[field] + if value.lower() == "true": + parsed_result.capability_enabled.append(field) + elif value.lower() == "false": + parsed_result.capability_disabled.append(field) + else: + parsed_result.other[field] = value + + return parsed_result + + +class Punisher(Extractor): + author = "kevoreilly" + family = "Punisher" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + output = extract_config(stream.read()) + if output: + return convert_to_MACO(output[0]) diff --git a/modules/parsers/MACO/QakBot.py b/modules/parsers/MACO/QakBot.py new file mode 100644 index 00000000..d4d8009a --- /dev/null +++ b/modules/parsers/MACO/QakBot.py @@ -0,0 +1,27 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.QakBot import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="QakBot", other=raw_config) + + for address in raw_config.get("address", []) + raw_config.get("C2s", []): + host, port = address.split(":") + parsed_result.http.append(MACOModel.Http(hostname=host, port=port, usage="c2")) + + return parsed_result + + +class QakBot(Extractor): + author = "kevoreilly" + family = "QakBot" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/QuasarRAT.py b/modules/parsers/MACO/QuasarRAT.py new file mode 100644 index 00000000..4ec5bd5b --- /dev/null +++ b/modules/parsers/MACO/QuasarRAT.py @@ -0,0 +1,25 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.QuasarRAT import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="QuasarRAT", other=raw_config) + + return parsed_result + + +class QuasarRAT(Extractor): + author = "kevoreilly" + family = "QuasarRAT" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Quickbind.py b/modules/parsers/MACO/Quickbind.py new file mode 100644 index 00000000..69531920 --- /dev/null +++ b/modules/parsers/MACO/Quickbind.py @@ -0,0 +1,34 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Quickbind import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Quickbind", other=raw_config) + + if "Mutex" in raw_config: + parsed_result.mutex = raw_config["Mutex"] + + for c2 in raw_config.get("C2", []): + parsed_result.http.append(MACOModel.Http(hostname=c2, usage="c2")) + + if "Encryption Key" in raw_config: + parsed_result.encryption.append(MACOModel.Encryption(key=raw_config["Encryption Key"])) + + return parsed_result + + +class Quickbind(Extractor): + author = "kevoreilly" + family = "Quickbind" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/RCSession.py b/modules/parsers/MACO/RCSession.py new file mode 100644 index 00000000..68565c25 --- /dev/null +++ b/modules/parsers/MACO/RCSession.py @@ -0,0 +1,43 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.RCSession import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="RCSession", other=raw_config) + + for address in raw_config.get("c2_address", []): + parsed_result.http.append(MACOModel.Http(hostname=address, usage="c2")) + + if "directory" in raw_config: + parsed_result.paths.append(MACOModel.Path(path=raw_config["directory"], usage="install")) + + service = {} + + if "servicename" in raw_config: + service["name"] = raw_config["servicename"] + if "servicedisplayname" in raw_config: + service["display_name"] = raw_config["servicedisplayname"] + if "servicedescription" in raw_config: + service["description"] = raw_config["servicedescription"] + if "filename" in raw_config: + service["dll"] = raw_config["filename"] + + if service: + parsed_result.service.append(MACOModel.Service(**service)) + + return parsed_result + + +class RCSession(Extractor): + author = "kevoreilly" + family = "RCSession" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/REvil.py b/modules/parsers/MACO/REvil.py new file mode 100644 index 00000000..7c935c29 --- /dev/null +++ b/modules/parsers/MACO/REvil.py @@ -0,0 +1,22 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.REvil import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="REvil", other=raw_config) + + return parsed_result + + +class REvil(Extractor): + author = "kevoreilly" + family = "REvil" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/RedLeaf.py b/modules/parsers/MACO/RedLeaf.py new file mode 100644 index 00000000..6101140a --- /dev/null +++ b/modules/parsers/MACO/RedLeaf.py @@ -0,0 +1,35 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.RedLeaf import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="RedLeaf", other=raw_config) + + for address in raw_config.get("c2_address", []): + parsed_result.http.append(MACOModel.Http(hostname=address, usage="c2")) + + if "missionid" in raw_config: + parsed_result.campaign_id.append(raw_config["missionid"]) + + if "mutex" in raw_config: + parsed_result.mutex.append(raw_config["mutex"]) + + if "key" in raw_config: + parsed_result.other["key"] = raw_config["key"] + + return parsed_result + + +class RedLeaf(Extractor): + author = "kevoreilly" + family = "RedLeaf" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/RedLine.py b/modules/parsers/MACO/RedLine.py new file mode 100644 index 00000000..2b15bb4d --- /dev/null +++ b/modules/parsers/MACO/RedLine.py @@ -0,0 +1,26 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.RedLine import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="RedLine", other=raw_config) + + if "C2" in raw_config: + host, port = raw_config["C2"].split(":") + parsed_result.http.append(MACOModel.Http(hostname=host, port=port, usage="c2")) + + return parsed_result + + +class RedLine(Extractor): + author = "kevoreilly" + family = "RedLine" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Remcos.py b/modules/parsers/MACO/Remcos.py new file mode 100644 index 00000000..7bc5eb15 --- /dev/null +++ b/modules/parsers/MACO/Remcos.py @@ -0,0 +1,25 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Remcos import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Remcos", other=raw_config) + + return parsed_result + + +class Remcos(Extractor): + author = "kevoreilly" + family = "Remcos" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Retefe.py b/modules/parsers/MACO/Retefe.py new file mode 100644 index 00000000..b0107414 --- /dev/null +++ b/modules/parsers/MACO/Retefe.py @@ -0,0 +1,23 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Retefe import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Retefe", other=raw_config) + + return parsed_result + + +class Retefe(Extractor): + author = "kevoreilly" + family = "Retefe" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Rhadamanthys.py b/modules/parsers/MACO/Rhadamanthys.py new file mode 100644 index 00000000..6dc37fca --- /dev/null +++ b/modules/parsers/MACO/Rhadamanthys.py @@ -0,0 +1,26 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Rhadamanthys import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Rhadamanthys", other=raw_config) + parsed_result.http = [MACOModel.Http(hostname=raw_config["C2"], usage="c2")] + + return parsed_result + + +class Rhadamanthys(Extractor): + author = "kevoreilly" + family = "Rhadamanthys" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Rozena.py b/modules/parsers/MACO/Rozena.py new file mode 100644 index 00000000..8577265f --- /dev/null +++ b/modules/parsers/MACO/Rozena.py @@ -0,0 +1,26 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Rozena import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Rozena", other=raw_config) + parsed_result.http = [MACOModel.Http(hostname=raw_config["C2"], port=raw_config["Port"], usage="c2")] + + return parsed_result + + +class Rozena(Extractor): + author = "kevoreilly" + family = "Rozena" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/SmallNet.py b/modules/parsers/MACO/SmallNet.py new file mode 100644 index 00000000..be331cf7 --- /dev/null +++ b/modules/parsers/MACO/SmallNet.py @@ -0,0 +1,25 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.SmallNet import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="SmallNet", other=raw_config) + + return parsed_result + + +class SmallNet(Extractor): + author = "kevoreilly" + family = "SmallNet" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + output = extract_config(stream.read()) + if output: + config = output if isinstance(output, dict) else output[0] + return convert_to_MACO(config) diff --git a/modules/parsers/MACO/SmokeLoader.py b/modules/parsers/MACO/SmokeLoader.py new file mode 100644 index 00000000..4d0884c6 --- /dev/null +++ b/modules/parsers/MACO/SmokeLoader.py @@ -0,0 +1,25 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.SmokeLoader import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel( + family="SmokeLoader", other=raw_config, http=[MACOModel.Http(uri=c2, usage="c2") for c2 in raw_config["C2s"]] + ) + + return parsed_result + + +class SmokeLoader(Extractor): + author = "kevoreilly" + family = "SmokeLoader" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Socks5Systemz.py b/modules/parsers/MACO/Socks5Systemz.py new file mode 100644 index 00000000..0cf7b24f --- /dev/null +++ b/modules/parsers/MACO/Socks5Systemz.py @@ -0,0 +1,30 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Socks5Systemz import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel( + family="Socks5Systemz", + other=raw_config, + http=[MACOModel.Http(hostname=c2, usage="c2") for c2 in raw_config.get("C2s", [])] + + [MACOModel.Http(hostname=decoy, usage="decoy") for decoy in raw_config.get("Dummy domain", [])], + ) + + return parsed_result + + +class Socks5Systemz(Extractor): + author = "kevoreilly" + family = "Socks5Systemz" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/SparkRAT.py b/modules/parsers/MACO/SparkRAT.py new file mode 100644 index 00000000..44862656 --- /dev/null +++ b/modules/parsers/MACO/SparkRAT.py @@ -0,0 +1,33 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.SparkRAT import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="SparkRAT", other=raw_config) + + url = f"http{'s' if raw_config['secure'] else ''}://{raw_config['host']}:{raw_config['port']}{raw_config['path']}" + + parsed_result.http.append( + MACOModel.Http(uri=url, hostname=raw_config["host"], port=raw_config["port"], path=raw_config["path"]) + ) + + parsed_result.identifier.append(raw_config["uuid"]) + + return parsed_result + + +class SparkRAT(Extractor): + author = "kevoreilly" + family = "SparkRAT" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/SquirrelWaffle.py b/modules/parsers/MACO/SquirrelWaffle.py new file mode 100644 index 00000000..59d3bf57 --- /dev/null +++ b/modules/parsers/MACO/SquirrelWaffle.py @@ -0,0 +1,25 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.SquirrelWaffle import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel( + family="SquirrelWaffle", other=raw_config, http=[MACOModel.Http(uri=c2, usage="c2") for c2 in raw_config["URLs"]] + ) + + return parsed_result + + +class SquirrelWaffle(Extractor): + author = "kevoreilly" + family = "SquirrelWaffle" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Stealc.py b/modules/parsers/MACO/Stealc.py new file mode 100644 index 00000000..62c9667e --- /dev/null +++ b/modules/parsers/MACO/Stealc.py @@ -0,0 +1,25 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.Stealc import RULE_SOURCE, extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel( + family="Stealc", other=raw_config, http=[MACOModel.Http(uri=c2, usage="c2") for c2 in raw_config["C2"]] + ) + + return parsed_result + + +class Stealc(Extractor): + author = "kevoreilly" + family = "Stealc" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = RULE_SOURCE + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Strrat.py b/modules/parsers/MACO/Strrat.py new file mode 100644 index 00000000..0b696c4d --- /dev/null +++ b/modules/parsers/MACO/Strrat.py @@ -0,0 +1,22 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Strrat import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Strrat", other=raw_config) + + return parsed_result + + +class Strrat(Extractor): + author = "kevoreilly" + family = "Strrat" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/TSCookie.py b/modules/parsers/MACO/TSCookie.py new file mode 100644 index 00000000..a3dc3f7b --- /dev/null +++ b/modules/parsers/MACO/TSCookie.py @@ -0,0 +1,25 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.TSCookie import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="TSCookie", other=raw_config) + + return parsed_result + + +class TSCookie(Extractor): + author = "kevoreilly" + family = "TSCookie" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/TrickBot.py b/modules/parsers/MACO/TrickBot.py new file mode 100644 index 00000000..fad48b29 --- /dev/null +++ b/modules/parsers/MACO/TrickBot.py @@ -0,0 +1,23 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.TrickBot import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="TrickBot", other=raw_config) + + return parsed_result + + +class TrickBot(Extractor): + author = "kevoreilly" + family = "TrickBot" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/UrsnifV3.py b/modules/parsers/MACO/UrsnifV3.py new file mode 100644 index 00000000..b631caff --- /dev/null +++ b/modules/parsers/MACO/UrsnifV3.py @@ -0,0 +1,25 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.UrsnifV3 import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="UrsnifV3", other=raw_config) + + return parsed_result + + +class UrsnifV3(Extractor): + author = "kevoreilly" + family = "UrsnifV3" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/VenomRat.py b/modules/parsers/MACO/VenomRat.py new file mode 100644 index 00000000..53914ff2 --- /dev/null +++ b/modules/parsers/MACO/VenomRat.py @@ -0,0 +1,22 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.VenomRAT import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="VenomRAT", other=raw_config) + + return parsed_result + + +class VenomRAT(Extractor): + author = "kevoreilly" + family = "VenomRAT" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/WarzoneRAT.py b/modules/parsers/MACO/WarzoneRAT.py new file mode 100644 index 00000000..6d7972c5 --- /dev/null +++ b/modules/parsers/MACO/WarzoneRAT.py @@ -0,0 +1,26 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.WarzoneRAT import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="WarzoneRAT", other=raw_config) + + if "C2" in raw_config: + host, port = raw_config["C2"].split(":") + parsed_result.http.append(MACOModel.Http(hostname=host, port=port, usage="c2")) + + return parsed_result + + +class WarzoneRAT(Extractor): + author = "kevoreilly" + family = "WarzoneRAT" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/XWorm.py b/modules/parsers/MACO/XWorm.py new file mode 100644 index 00000000..3293c4bb --- /dev/null +++ b/modules/parsers/MACO/XWorm.py @@ -0,0 +1,25 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.XWorm import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="XWorm", other=raw_config) + + return parsed_result + + +class XWorm(Extractor): + author = "kevoreilly" + family = "XWorm" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/XenoRAT.py b/modules/parsers/MACO/XenoRAT.py new file mode 100644 index 00000000..a4120943 --- /dev/null +++ b/modules/parsers/MACO/XenoRAT.py @@ -0,0 +1,25 @@ +import os + +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.community.XenoRAT import extract_config + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="XenoRAT", other=raw_config) + + return parsed_result + + +class XenoRAT(Extractor): + author = "kevoreilly" + family = "XenoRAT" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = open(os.path.join(os.path.dirname(__file__).split("/modules", 1)[0], f"data/yara/CAPE/{family}.yar")).read() + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/Zloader.py b/modules/parsers/MACO/Zloader.py new file mode 100644 index 00000000..53e62736 --- /dev/null +++ b/modules/parsers/MACO/Zloader.py @@ -0,0 +1,32 @@ +from maco.extractor import Extractor +from maco.model import ExtractorModel as MACOModel +from cape_parsers.CAPE.core.Zloader import extract_config, rule_source + + +def convert_to_MACO(raw_config: dict): + if not raw_config: + return None + + parsed_result = MACOModel(family="Zloader", other=raw_config) + + if "Campaign ID" in raw_config: + parsed_result.campaign_id = [raw_config["Campaign ID"]] + + if "RC4 key" in raw_config: + parsed_result.encryption = [MACOModel.Encryption(algorithm="RC4", key=raw_config[:"RC4 key"])] + + for address in raw_config.get("address", []): + parsed_result.http.append(MACOModel.Http(uri=address)) + + return parsed_result + + +class Zloader(Extractor): + author = "kevoreilly" + family = "Zloader" + last_modified = "2024-10-26" + sharing = "TLP:CLEAR" + yara_rule = rule_source + + def run(self, stream, matches): + return convert_to_MACO(extract_config(stream.read())) diff --git a/modules/parsers/MACO/__init__.py b/modules/parsers/MACO/__init__.py new file mode 100644 index 00000000..f39e5e8d --- /dev/null +++ b/modules/parsers/MACO/__init__.py @@ -0,0 +1 @@ +# Init diff --git a/modules/parsers/MACO/test_maco.py b/modules/parsers/MACO/test_maco.py new file mode 100644 index 00000000..d502c95b --- /dev/null +++ b/modules/parsers/MACO/test_maco.py @@ -0,0 +1,10 @@ +from maco.extractor import Extractor + + +class Test(Extractor): + author = "test" + family = "test" + last_modified = "2024-10-20" + + def run(self, stream, matches): + pass