CAPE-parsers 0.1.47__py3-none-any.whl → 0.1.49__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cape_parsers/CAPE/community/Amadey.py +213 -0
- cape_parsers/CAPE/community/CobaltStrikeBeacon.py +3 -3
- cape_parsers/CAPE/community/LokiBot.py +11 -8
- cape_parsers/CAPE/community/Lumma.py +3 -3
- cape_parsers/CAPE/community/Snake.py +2 -2
- cape_parsers/CAPE/community/Stealc.py +10 -0
- cape_parsers/CAPE/core/AuraStealer.py +93 -0
- cape_parsers/CAPE/core/BumbleBee.py +5 -5
- cape_parsers/CAPE/core/Remcos.py +1 -1
- cape_parsers/__init__.py +1 -1
- {cape_parsers-0.1.47.dist-info → cape_parsers-0.1.49.dist-info}/METADATA +1 -1
- {cape_parsers-0.1.47.dist-info → cape_parsers-0.1.49.dist-info}/RECORD +14 -12
- {cape_parsers-0.1.47.dist-info → cape_parsers-0.1.49.dist-info}/LICENSE +0 -0
- {cape_parsers-0.1.47.dist-info → cape_parsers-0.1.49.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import yara
|
|
3
|
+
import pefile
|
|
4
|
+
import json
|
|
5
|
+
import struct
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
RULE_SOURCE_KEY = """
|
|
10
|
+
rule Amadey_Key_String
|
|
11
|
+
{
|
|
12
|
+
meta:
|
|
13
|
+
author = "YungBinary"
|
|
14
|
+
description = "Find decryption key in Amadey."
|
|
15
|
+
strings:
|
|
16
|
+
$chunk_1 = {
|
|
17
|
+
6A 20
|
|
18
|
+
68 ?? ?? ?? ??
|
|
19
|
+
B9 ?? ?? ?? ??
|
|
20
|
+
E8 ?? ?? ?? ??
|
|
21
|
+
68 ?? ?? ?? ??
|
|
22
|
+
E8 ?? ?? ?? ??
|
|
23
|
+
59
|
|
24
|
+
C3
|
|
25
|
+
}
|
|
26
|
+
condition:
|
|
27
|
+
$chunk_1
|
|
28
|
+
}
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
RULE_SOURCE_ENCODED_STRINGS = """
|
|
32
|
+
rule Amadey_Encoded_Strings
|
|
33
|
+
{
|
|
34
|
+
meta:
|
|
35
|
+
author = "YungBinary"
|
|
36
|
+
description = "Find encoded strings in Amadey."
|
|
37
|
+
strings:
|
|
38
|
+
$chunk_1 = {
|
|
39
|
+
6A ??
|
|
40
|
+
68 ?? ?? ?? ??
|
|
41
|
+
B9 ?? ?? ?? ??
|
|
42
|
+
E8 ?? ?? ?? ??
|
|
43
|
+
68 ?? ?? ?? ??
|
|
44
|
+
E8 ?? ?? ?? ??
|
|
45
|
+
59
|
|
46
|
+
C3
|
|
47
|
+
}
|
|
48
|
+
condition:
|
|
49
|
+
$chunk_1
|
|
50
|
+
}
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def contains_non_printable(byte_array):
|
|
55
|
+
for byte in byte_array:
|
|
56
|
+
if not chr(byte).isprintable():
|
|
57
|
+
return True
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def yara_scan_generator(raw_data, rule_source):
|
|
62
|
+
yara_rules = yara.compile(source=rule_source)
|
|
63
|
+
matches = yara_rules.match(data=raw_data)
|
|
64
|
+
|
|
65
|
+
for match in matches:
|
|
66
|
+
for block in match.strings:
|
|
67
|
+
for instance in block.instances:
|
|
68
|
+
yield instance.offset, block.identifier
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_keys(pe, data):
|
|
72
|
+
image_base = pe.OPTIONAL_HEADER.ImageBase
|
|
73
|
+
keys = []
|
|
74
|
+
for offset, _ in yara_scan_generator(data, RULE_SOURCE_KEY):
|
|
75
|
+
try:
|
|
76
|
+
key_string_rva = struct.unpack('i', data[offset + 3 : offset + 7])[0]
|
|
77
|
+
key_string_dword_offset = pe.get_offset_from_rva(key_string_rva - image_base)
|
|
78
|
+
key_string = pe.get_string_from_data(key_string_dword_offset, data)
|
|
79
|
+
|
|
80
|
+
if b"=" not in key_string:
|
|
81
|
+
keys.append(key_string.decode())
|
|
82
|
+
|
|
83
|
+
if len(keys) == 2:
|
|
84
|
+
return keys
|
|
85
|
+
|
|
86
|
+
except Exception:
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_encoded_strings(pe, data):
|
|
93
|
+
encoded_strings = []
|
|
94
|
+
image_base = pe.OPTIONAL_HEADER.ImageBase
|
|
95
|
+
for offset, _ in yara_scan_generator(data, RULE_SOURCE_ENCODED_STRINGS):
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
encoded_string_size = data[offset + 1]
|
|
99
|
+
encoded_string_rva = struct.unpack('i', data[offset + 3 : offset + 7])[0]
|
|
100
|
+
encoded_string_dword_offset = pe.get_offset_from_rva(encoded_string_rva - image_base)
|
|
101
|
+
encoded_string = pe.get_string_from_data(encoded_string_dword_offset, data)
|
|
102
|
+
|
|
103
|
+
# Make sure the string matches length from operand
|
|
104
|
+
if encoded_string_size != len(encoded_string):
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
encoded_strings.append(encoded_string.decode())
|
|
108
|
+
|
|
109
|
+
except Exception:
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
return encoded_strings
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def decode_amadey_string(key: str, encoded_str: str) -> bytes:
|
|
116
|
+
"""
|
|
117
|
+
Decode Amadey encoded strings that look like base64
|
|
118
|
+
"""
|
|
119
|
+
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "
|
|
120
|
+
|
|
121
|
+
decoded = ""
|
|
122
|
+
for i in range(len(encoded_str)):
|
|
123
|
+
if encoded_str[i] == "=":
|
|
124
|
+
decoded += "="
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
index_1 = alphabet.index(encoded_str[i % len(encoded_str)])
|
|
128
|
+
index_2 = alphabet.index(key[i % len(key)])
|
|
129
|
+
|
|
130
|
+
index_result = (index_1 + (0x3F - index_2) + 0x3F) % 0x3F
|
|
131
|
+
|
|
132
|
+
decoded += alphabet[index_result]
|
|
133
|
+
|
|
134
|
+
decoded = base64.b64decode(decoded)
|
|
135
|
+
|
|
136
|
+
return decoded
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def find_campaign_id(data):
|
|
140
|
+
pattern = br'\x00\x00\x00([0-9a-f]{6})\x00\x00'
|
|
141
|
+
matches = re.findall(pattern, data)
|
|
142
|
+
if matches:
|
|
143
|
+
return matches[0]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def extract_config(data):
|
|
147
|
+
pe = pefile.PE(data=data, fast_load=True)
|
|
148
|
+
# image_base = pe.OPTIONAL_HEADER.ImageBase
|
|
149
|
+
|
|
150
|
+
keys = get_keys(pe, data)
|
|
151
|
+
if not keys:
|
|
152
|
+
return {}
|
|
153
|
+
|
|
154
|
+
decode_key = keys[0]
|
|
155
|
+
rc4_key = keys[1]
|
|
156
|
+
encoded_strings = get_encoded_strings(pe, data)
|
|
157
|
+
|
|
158
|
+
decoded_strings = []
|
|
159
|
+
for encoded_string in encoded_strings:
|
|
160
|
+
try:
|
|
161
|
+
decoded_string = decode_amadey_string(decode_key, encoded_string)
|
|
162
|
+
if not decoded_string or contains_non_printable(decoded_string):
|
|
163
|
+
continue
|
|
164
|
+
decoded_strings.append(decoded_string.decode())
|
|
165
|
+
except Exception:
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
if not decoded_strings:
|
|
169
|
+
return {}
|
|
170
|
+
|
|
171
|
+
decoded_strings = decoded_strings[:10]
|
|
172
|
+
final_config = {}
|
|
173
|
+
version = ""
|
|
174
|
+
install_dir = ""
|
|
175
|
+
install_file = ""
|
|
176
|
+
version_pattern = r"^\d+\.\d{1,2}$"
|
|
177
|
+
install_dir_pattern = r"^[0-9a-f]{10}$"
|
|
178
|
+
|
|
179
|
+
for i in range(len(decoded_strings)):
|
|
180
|
+
s = decoded_strings[i]
|
|
181
|
+
if s.endswith(".php"):
|
|
182
|
+
c2 = decoded_strings[i-1]
|
|
183
|
+
final_config.setdefault("CNCs", []).append(f"http://{c2}{s}")
|
|
184
|
+
elif re.match(version_pattern, s):
|
|
185
|
+
version = s
|
|
186
|
+
elif re.match(install_dir_pattern, s):
|
|
187
|
+
install_dir = s
|
|
188
|
+
elif s.endswith(".exe"):
|
|
189
|
+
install_file = s
|
|
190
|
+
|
|
191
|
+
if version:
|
|
192
|
+
final_config["version"] = version
|
|
193
|
+
if install_dir:
|
|
194
|
+
final_config.setdefault("raw", {})["install_dir"] = install_dir
|
|
195
|
+
if install_file:
|
|
196
|
+
final_config.setdefault("raw", {})["install_file"] = install_file
|
|
197
|
+
|
|
198
|
+
final_config["cryptokey"] = rc4_key
|
|
199
|
+
final_config["cryptokey_type"] = "RC4"
|
|
200
|
+
|
|
201
|
+
campaign_id = find_campaign_id(data)
|
|
202
|
+
if campaign_id:
|
|
203
|
+
final_config["campaign_id"] = campaign_id.decode()
|
|
204
|
+
|
|
205
|
+
return final_config
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
if __name__ == "__main__":
|
|
210
|
+
import sys
|
|
211
|
+
|
|
212
|
+
with open(sys.argv[1], "rb") as f:
|
|
213
|
+
print(json.dumps(extract_config(f.read()), indent=4))
|
|
@@ -360,11 +360,11 @@ class cobaltstrikeConfig:
|
|
|
360
360
|
if parsed_setting == "Not Found" and quiet:
|
|
361
361
|
continue
|
|
362
362
|
if not isinstance(parsed_setting, list):
|
|
363
|
-
log.debug("{: <{width}} - {val}".format(conf_name, width=COLUMN_WIDTH - 3, val=parsed_setting))
|
|
363
|
+
log.debug("{: <{width}} - {val}".format(conf_name, width=COLUMN_WIDTH - 3, val=parsed_setting)) # noqa: G001
|
|
364
364
|
elif parsed_setting == []:
|
|
365
|
-
log.debug("{: <{width}} - {val}".format(conf_name, width=COLUMN_WIDTH - 3, val="Empty"))
|
|
365
|
+
log.debug("{: <{width}} - {val}".format(conf_name, width=COLUMN_WIDTH - 3, val="Empty")) # noqa: G001
|
|
366
366
|
else:
|
|
367
|
-
log.debug("{: <{width}} - {val}".format(conf_name, width=COLUMN_WIDTH - 3, val=parsed_setting[0]))
|
|
367
|
+
log.debug("{: <{width}} - {val}".format(conf_name, width=COLUMN_WIDTH - 3, val=parsed_setting[0])) # noqa: G001
|
|
368
368
|
for val in parsed_setting[1:]:
|
|
369
369
|
log.debug(" " * COLUMN_WIDTH, end="")
|
|
370
370
|
print(val)
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import re
|
|
24
24
|
import struct
|
|
25
25
|
import sys
|
|
26
|
-
|
|
26
|
+
from contextlib import suppress
|
|
27
27
|
import pefile
|
|
28
28
|
from Cryptodome.Cipher import DES3
|
|
29
29
|
from Cryptodome.Util.Padding import unpad
|
|
@@ -128,7 +128,8 @@ def decoder(data):
|
|
|
128
128
|
else:
|
|
129
129
|
x = bytearray(img)
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
# ToDo add \.php
|
|
132
|
+
url_re = rb"https?:\/\/[a-zA-Z0-9\/\.:?\-_]+\.php"
|
|
132
133
|
urls = re.findall(url_re, x)
|
|
133
134
|
if not urls:
|
|
134
135
|
for i in range(len(x)):
|
|
@@ -144,21 +145,23 @@ def decoder(data):
|
|
|
144
145
|
confs = find_conf(img)
|
|
145
146
|
if iv and iv not in (b"", -1) and confs != []:
|
|
146
147
|
for conf in confs:
|
|
147
|
-
|
|
148
|
+
with suppress(ValueError):
|
|
148
149
|
dec = DES3.new(key, DES3.MODE_CBC, iv)
|
|
149
150
|
temp = dec.decrypt(conf)
|
|
150
151
|
temp = unpad(temp, 8)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
pass
|
|
152
|
+
if not temp.endswith(b".php"):
|
|
153
|
+
continue
|
|
154
|
+
urls.append("http://" + temp.decod())
|
|
155
155
|
return urls
|
|
156
156
|
|
|
157
157
|
|
|
158
158
|
def extract_config(filebuf):
|
|
159
159
|
|
|
160
160
|
urls = decoder(filebuf)
|
|
161
|
-
|
|
161
|
+
if urls:
|
|
162
|
+
return {"CNCs": urls}
|
|
163
|
+
else:
|
|
164
|
+
return {}
|
|
162
165
|
|
|
163
166
|
|
|
164
167
|
if __name__ == "__main__":
|
|
@@ -318,7 +318,7 @@ def extract_config(data):
|
|
|
318
318
|
decoded_c2 = chacha20_xor(encrypted_string, key, nonce, counter).split(b"\x00", 1)[0]
|
|
319
319
|
if contains_non_printable(decoded_c2):
|
|
320
320
|
break
|
|
321
|
-
config.setdefault("CNCs", []).append(decoded_c2.decode())
|
|
321
|
+
config.setdefault("CNCs", []).append("https://" + decoded_c2.decode())
|
|
322
322
|
encrypted_strings_offset = encrypted_strings_offset + step_size
|
|
323
323
|
counter += 2
|
|
324
324
|
|
|
@@ -351,7 +351,7 @@ def extract_config(data):
|
|
|
351
351
|
decrypted = chacha20_xor(c2_encrypted, key, nonce, counter)
|
|
352
352
|
c2 = extract_c2_domain(decrypted)
|
|
353
353
|
if c2 is not None and len(c2) > 10:
|
|
354
|
-
config["CNCs"].append(c2.decode())
|
|
354
|
+
config["CNCs"].append("https://" + c2.decode())
|
|
355
355
|
break
|
|
356
356
|
|
|
357
357
|
except Exception:
|
|
@@ -384,7 +384,7 @@ def extract_config(data):
|
|
|
384
384
|
decoded_c2 = xor_data(encoded_c2, xor_key)
|
|
385
385
|
|
|
386
386
|
if not contains_non_printable(decoded_c2):
|
|
387
|
-
config.setdefault("CNCs", []).append(decoded_c2.decode())
|
|
387
|
+
config.setdefault("CNCs", []).append("https://" + decoded_c2.decode())
|
|
388
388
|
except Exception as e:
|
|
389
389
|
print(e)
|
|
390
390
|
continue
|
|
@@ -133,7 +133,7 @@ def extract_config(data):
|
|
|
133
133
|
try:
|
|
134
134
|
dotnet_file = dnfile.dnPE(data=data)
|
|
135
135
|
except Exception as e:
|
|
136
|
-
log.debug(
|
|
136
|
+
log.debug("Exception when attempting to parse .NET file: %s", str(e))
|
|
137
137
|
log.debug(traceback.format_exc())
|
|
138
138
|
|
|
139
139
|
# ldstr, stsfld
|
|
@@ -165,7 +165,7 @@ def extract_config(data):
|
|
|
165
165
|
else:
|
|
166
166
|
user_strings[field_name] = string_index
|
|
167
167
|
except Exception as e:
|
|
168
|
-
log.debug(
|
|
168
|
+
log.debug("There was an exception parsing user strings: %s", str(e))
|
|
169
169
|
log.debug(traceback.format_exc())
|
|
170
170
|
|
|
171
171
|
if c2_type is None:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import struct
|
|
2
2
|
import pefile
|
|
3
3
|
import yara
|
|
4
|
+
import ipaddress
|
|
4
5
|
from contextlib import suppress
|
|
5
6
|
|
|
6
7
|
|
|
@@ -42,6 +43,13 @@ def yara_scan(raw_data):
|
|
|
42
43
|
yield block.identifier, instance.offset
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
def _is_ip(ip):
|
|
47
|
+
try:
|
|
48
|
+
ipaddress.ip_address(ip)
|
|
49
|
+
return True
|
|
50
|
+
except Exception:
|
|
51
|
+
return False
|
|
52
|
+
|
|
45
53
|
def xor_data(data, key):
|
|
46
54
|
decoded = bytearray()
|
|
47
55
|
for i in range(len(data)):
|
|
@@ -67,6 +75,8 @@ def parse_text(data):
|
|
|
67
75
|
for line in lines:
|
|
68
76
|
if line.startswith("http") and "://" in line:
|
|
69
77
|
domain = line
|
|
78
|
+
elif _is_ip(line):
|
|
79
|
+
domain = line
|
|
70
80
|
if line.startswith("/") and line[-4] == ".":
|
|
71
81
|
uri = line
|
|
72
82
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import struct
|
|
3
|
+
from contextlib import suppress
|
|
4
|
+
from typing import Any, Dict, Tuple
|
|
5
|
+
|
|
6
|
+
import pefile
|
|
7
|
+
from Cryptodome.Cipher import AES
|
|
8
|
+
from Cryptodome.Util.Padding import unpad
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_blob(data: bytes):
|
|
12
|
+
"""
|
|
13
|
+
Parse the blob according to the scheme:
|
|
14
|
+
- 32 bytes = AES key
|
|
15
|
+
- Next 16 bytes = IV
|
|
16
|
+
- Next 2 DWORDs (8 bytes total) = XOR to get cipher data size
|
|
17
|
+
- Remaining bytes = cipher data of that size
|
|
18
|
+
"""
|
|
19
|
+
offset = 0
|
|
20
|
+
aes_key = data[offset:offset + 32]
|
|
21
|
+
offset += 32
|
|
22
|
+
iv = data[offset:offset + 16]
|
|
23
|
+
offset += 16
|
|
24
|
+
dword1, dword2 = struct.unpack_from("<II", data, offset)
|
|
25
|
+
cipher_size = dword1 ^ dword2
|
|
26
|
+
offset += 8
|
|
27
|
+
cipher_data = data[offset:offset + cipher_size]
|
|
28
|
+
return aes_key, iv, cipher_data
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def decrypt(data: bytes) -> Tuple[bytes, bytes, bytes]:
|
|
32
|
+
aes_key, iv, cipher_data = parse_blob(data)
|
|
33
|
+
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
|
|
34
|
+
plaintext_padded = cipher.decrypt(cipher_data)
|
|
35
|
+
return aes_key, iv, unpad(plaintext_padded, AES.block_size)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def extract_config(data: bytes) -> Dict[str, Any]:
|
|
39
|
+
cfg: Dict[str, Any] = {}
|
|
40
|
+
plaintext = ""
|
|
41
|
+
pe = pefile.PE(data=data, fast_load=True)
|
|
42
|
+
try:
|
|
43
|
+
data_section = [s for s in pe.sections if s.Name.find(b".data") != -1][0]
|
|
44
|
+
except IndexError:
|
|
45
|
+
return cfg
|
|
46
|
+
|
|
47
|
+
if not data_section:
|
|
48
|
+
return cfg
|
|
49
|
+
|
|
50
|
+
data = data_section.get_data()
|
|
51
|
+
block_size = 4096
|
|
52
|
+
zeros = b"\x00" * block_size
|
|
53
|
+
offset = data.find(zeros)
|
|
54
|
+
if offset == -1:
|
|
55
|
+
return cfg
|
|
56
|
+
|
|
57
|
+
while offset > 0:
|
|
58
|
+
with suppress(Exception):
|
|
59
|
+
aes_key, iv, plaintext = decrypt(data[offset : offset + block_size])
|
|
60
|
+
if plaintext and b"conf" in plaintext:
|
|
61
|
+
break
|
|
62
|
+
|
|
63
|
+
offset -= 1
|
|
64
|
+
|
|
65
|
+
if plaintext:
|
|
66
|
+
parsed = json.loads(plaintext.decode("utf-8", errors="ignore").rstrip("\x00"))
|
|
67
|
+
conf = parsed.get("conf", {})
|
|
68
|
+
build = parsed.get("build", {})
|
|
69
|
+
if conf:
|
|
70
|
+
cfg = {
|
|
71
|
+
"CNCs": conf.get("hosts"),
|
|
72
|
+
"user_agent": conf.get("useragents"),
|
|
73
|
+
"version": build.get("ver"),
|
|
74
|
+
"build": build.get("build_id"),
|
|
75
|
+
"cryptokey": aes_key.hex(),
|
|
76
|
+
"cryptokey_type": "AES",
|
|
77
|
+
"raw": {
|
|
78
|
+
"iv": iv.hex(),
|
|
79
|
+
"anti_vm": conf.get("anti_vm"),
|
|
80
|
+
"anti_dbg": conf.get("anti_dbg"),
|
|
81
|
+
"self_del": conf.get("self_del"),
|
|
82
|
+
"run_delay": conf.get("run_delay"),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return cfg
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
import sys
|
|
91
|
+
|
|
92
|
+
with open(sys.argv[1], "rb") as f:
|
|
93
|
+
print(extract_config(f.read()))
|
|
@@ -50,7 +50,7 @@ def extract_key_data(data, pe, key_match):
|
|
|
50
50
|
# Read arbitrary number of byes from key offset and split on null bytes to extract key
|
|
51
51
|
key = data[key_offset : key_offset + 0x40].split(b"\x00")[0]
|
|
52
52
|
except Exception as e:
|
|
53
|
-
log.debug(
|
|
53
|
+
log.debug("There was an exception extracting the key: %s", str(e))
|
|
54
54
|
log.debug(traceback.format_exc())
|
|
55
55
|
return False
|
|
56
56
|
return key
|
|
@@ -70,7 +70,7 @@ def extract_config_data(data, pe, config_match):
|
|
|
70
70
|
)
|
|
71
71
|
campaign_id_ct = data[campaign_id_offset : campaign_id_offset + 0x10]
|
|
72
72
|
except Exception as e:
|
|
73
|
-
log.debug(
|
|
73
|
+
log.debug("There was an exception extracting the campaign id: %s", str(e))
|
|
74
74
|
log.debug(traceback.format_exc())
|
|
75
75
|
return False, False, False
|
|
76
76
|
|
|
@@ -84,7 +84,7 @@ def extract_config_data(data, pe, config_match):
|
|
|
84
84
|
)
|
|
85
85
|
botnet_id_ct = data[botnet_id_offset : botnet_id_offset + 0x10]
|
|
86
86
|
except Exception as e:
|
|
87
|
-
log.debug(
|
|
87
|
+
log.debug("There was an exception extracting the botnet id: %s", str(e))
|
|
88
88
|
log.debug(traceback.format_exc())
|
|
89
89
|
return False, False, False
|
|
90
90
|
|
|
@@ -99,7 +99,7 @@ def extract_config_data(data, pe, config_match):
|
|
|
99
99
|
c2s_offset = pe.get_offset_from_rva(c2s_rva + int.from_bytes(config_match.group("c2s"), byteorder="little"))
|
|
100
100
|
c2s_ct = data[c2s_offset : c2s_offset + 0x400]
|
|
101
101
|
except Exception as e:
|
|
102
|
-
log.debug(
|
|
102
|
+
log.debug("There was an exception extracting the C2s: %s", str(e))
|
|
103
103
|
log.debug(traceback.format_exc())
|
|
104
104
|
return False, False, False
|
|
105
105
|
|
|
@@ -243,7 +243,7 @@ def extract_config(data):
|
|
|
243
243
|
if c2s:
|
|
244
244
|
cfg["CNCs"] = list(ARC4.new(key).decrypt(c2s).split(b"\x00")[0].decode().split(","))
|
|
245
245
|
except Exception as e:
|
|
246
|
-
log.
|
|
246
|
+
log.exception("This is broken: %s", str(e))
|
|
247
247
|
|
|
248
248
|
if not cfg:
|
|
249
249
|
cfg = extract_2024(pe, data)
|
cape_parsers/CAPE/core/Remcos.py
CHANGED
cape_parsers/__init__.py
CHANGED
|
@@ -129,7 +129,7 @@ def load_malwareconfig_parsers() -> Tuple[bool, dict, ModuleType]:
|
|
|
129
129
|
except ImportError:
|
|
130
130
|
log.info("Missed RATDecoders -> poetry run pip install malwareconfig")
|
|
131
131
|
except Exception as e:
|
|
132
|
-
log.
|
|
132
|
+
log.exception(e)
|
|
133
133
|
return False, False, False
|
|
134
134
|
|
|
135
135
|
|
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
cape_parsers/CAPE/__init__.py,sha256=JcY8WPKzUFYgexwV1eyKIuT1JyNZzMJjBynlPSzxY_I,7
|
|
2
2
|
cape_parsers/CAPE/community/AgentTesla.py,sha256=rHhTmINQ0bGZEiJ5NhCKPhGobcifq3FDWZItgHTpBC8,3796
|
|
3
|
+
cape_parsers/CAPE/community/Amadey.py,sha256=IUyt909q9IDQPPip6UW9uD16rJMD_gvkwvNZ8NHTW-k,5577
|
|
3
4
|
cape_parsers/CAPE/community/Arkei.py,sha256=k36qHxdo5yPa9V1cg7EImSWP06kMog0rBda4KXqLKCY,3783
|
|
4
5
|
cape_parsers/CAPE/community/AsyncRAT.py,sha256=0-FRT3d2x63KQ_cs1xmKFj7x0JRf7ID6QDc_DvBa0PM,1003
|
|
5
6
|
cape_parsers/CAPE/community/AuroraStealer.py,sha256=C0j9SZDJRi107PbfYZ9G168MCyqYItrI-XK5k0Bp4tE,2632
|
|
6
7
|
cape_parsers/CAPE/community/Carbanak.py,sha256=Smi_vTWDfWxYBQa661ZIy0624IYJA22LMHAJEQbstpk,5607
|
|
7
|
-
cape_parsers/CAPE/community/CobaltStrikeBeacon.py,sha256=
|
|
8
|
+
cape_parsers/CAPE/community/CobaltStrikeBeacon.py,sha256=U4Q0ObCrPRpiO5B5fBmkgr63jXdizujNth8v6kUPnEQ,19466
|
|
8
9
|
cape_parsers/CAPE/community/CobaltStrikeStager.py,sha256=HLxROBjz453uHNq1bPz0VSAhtyWDfz79ZacTPdjuWmY,7535
|
|
9
10
|
cape_parsers/CAPE/community/DCRat.py,sha256=0-FRT3d2x63KQ_cs1xmKFj7x0JRf7ID6QDc_DvBa0PM,1003
|
|
10
11
|
cape_parsers/CAPE/community/Fareit.py,sha256=OyKeZdcvyAhjxZgJqkDPJHP4Npv1ArvTHJZ5F0C1Iac,1875
|
|
11
12
|
cape_parsers/CAPE/community/KoiLoader.py,sha256=F2gsgCvrVuwxY1bg8rlexsjCjikAP5HIGGOqU8zhT8E,4008
|
|
12
|
-
cape_parsers/CAPE/community/LokiBot.py,sha256=
|
|
13
|
-
cape_parsers/CAPE/community/Lumma.py,sha256=
|
|
13
|
+
cape_parsers/CAPE/community/LokiBot.py,sha256=355kqLx0LNMr8XcGfPL7cxG8QZalcmE7ttVBqoWtTWE,5754
|
|
14
|
+
cape_parsers/CAPE/community/Lumma.py,sha256=Iqd9yvt3g0FeV_bYRmL1RKp4C1H92qeGg4fXivVDSxw,12206
|
|
14
15
|
cape_parsers/CAPE/community/NanoCore.py,sha256=8QZnf1AcY9481kSfsf3SHQShwPLn97peGAf8_xEasQc,6230
|
|
15
16
|
cape_parsers/CAPE/community/Nighthawk.py,sha256=8ss8yvslrwUt53zV6U0xuwGKU3hgYfOt13S5lkOVpNo,12105
|
|
16
17
|
cape_parsers/CAPE/community/Njrat.py,sha256=GiwSENBB43RUqyJ7zT7ZPkPUYqo8Ew4kd5MJUj0jzdc,4702
|
|
17
18
|
cape_parsers/CAPE/community/PhemedroneStealer.py,sha256=Z7_PdxC8bmd6P3AqOm7AHVRrbEVuREwMWbyLVHaAhK0,7095
|
|
18
19
|
cape_parsers/CAPE/community/QuasarRAT.py,sha256=dzVInOc-BPVRdArk92oEY4PKq1AEW04NUToL8UV-UGk,146
|
|
19
20
|
cape_parsers/CAPE/community/README.md,sha256=SHgVQraCdp033IQjM4Cm6t70U4kULn1MfSwTq3rsZv8,22
|
|
20
|
-
cape_parsers/CAPE/community/Snake.py,sha256
|
|
21
|
+
cape_parsers/CAPE/community/Snake.py,sha256=v_MAPmg86ZdgGOkzc9GVHbi-lu4nLa1_0Lp90qiCg8s,6650
|
|
21
22
|
cape_parsers/CAPE/community/SparkRAT.py,sha256=OVDty_1i9PTGuEumT0BHoDn0bD2UtdhHVNjThah80pg,2140
|
|
22
|
-
cape_parsers/CAPE/community/Stealc.py,sha256=
|
|
23
|
+
cape_parsers/CAPE/community/Stealc.py,sha256=A00EEMSubZGLhgRWhL_HWDZBu-EsLWrpBv_-JR3-yZE,5302
|
|
23
24
|
cape_parsers/CAPE/community/VenomRAT.py,sha256=0-FRT3d2x63KQ_cs1xmKFj7x0JRf7ID6QDc_DvBa0PM,1003
|
|
24
25
|
cape_parsers/CAPE/community/XWorm.py,sha256=0-FRT3d2x63KQ_cs1xmKFj7x0JRf7ID6QDc_DvBa0PM,1003
|
|
25
26
|
cape_parsers/CAPE/community/XenoRAT.py,sha256=0-FRT3d2x63KQ_cs1xmKFj7x0JRf7ID6QDc_DvBa0PM,1003
|
|
26
27
|
cape_parsers/CAPE/community/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
28
|
cape_parsers/CAPE/community/monsterv2.py,sha256=eVEs4VIeS3PiZtRjNb69itmDq2Zkbrpn5k3M68GujiI,2995
|
|
28
29
|
cape_parsers/CAPE/core/AdaptixBeacon.py,sha256=40wMfrXt-7UG30WsLC5GxUtG6tSUaaP1OT-ntWzPZn0,2956
|
|
30
|
+
cape_parsers/CAPE/core/AuraStealer.py,sha256=6mbS1MNsaULpTAlh2vNeZPQy6faYm8BMu0OQh5Vb0eI,2702
|
|
29
31
|
cape_parsers/CAPE/core/Azorult.py,sha256=YkMIhC6zRTxEkLVMUdr2MMsbV9iAnZ8hUS8be9GZ5N4,2150
|
|
30
32
|
cape_parsers/CAPE/core/BitPaymer.py,sha256=HQwoE0o7HMiXItxE08vBenf2ZWMxZp84-Hf_1eZ8QdE,3050
|
|
31
33
|
cape_parsers/CAPE/core/BlackDropper.py,sha256=sCSu2T5oPvcFHlSAzSsLj_gCv2Tldl0UPguwy0MVg6A,3282
|
|
32
34
|
cape_parsers/CAPE/core/Blister.py,sha256=wprcJMHixv4JHGqBjQeu26BJ6HgXeBMobh10Y-H6-Xg,18173
|
|
33
35
|
cape_parsers/CAPE/core/BruteRatel.py,sha256=_hFAYLbOsHdekWPOMXRmIYNXTNeNQSs3LZqh7xAVI2U,1147
|
|
34
|
-
cape_parsers/CAPE/core/BumbleBee.py,sha256=
|
|
36
|
+
cape_parsers/CAPE/core/BumbleBee.py,sha256=8a15OLAoNKs377ehdgx8R6kCAsJeG-_itt8xc_EMQes,10075
|
|
35
37
|
cape_parsers/CAPE/core/DarkGate.py,sha256=ppSRDfw-u2NltzQlrVvRwqxGaprShuv5CrwbNbnSvaw,3477
|
|
36
38
|
cape_parsers/CAPE/core/DoppelPaymer.py,sha256=LPAQ-7imcAWFciAd7Qb_r6js2PdIsTt9fRdYKoEkFMg,2537
|
|
37
39
|
cape_parsers/CAPE/core/DridexLoader.py,sha256=8NKppvGz7tVXnNTGEgS7R3LGn5vtW4xslQYbo38wQUg,7087
|
|
@@ -47,7 +49,7 @@ cape_parsers/CAPE/core/QakBot.py,sha256=SmXRuwOiaDLL7uN9RwCiQP62P3ctxGJ6y54zJG9y
|
|
|
47
49
|
cape_parsers/CAPE/core/Quickbind.py,sha256=5A077RFQQOL8dtr2Q9vmlTKsWk96JkRWuHGseApyTmU,3675
|
|
48
50
|
cape_parsers/CAPE/core/README.md,sha256=Zd84WEUj9NzKzGnVZV1jx6gMiEOtz01m32B7xEuS91k,17
|
|
49
51
|
cape_parsers/CAPE/core/RedLine.py,sha256=bZeKLvxaS6HDpWY4RDXtSEBt93qTNzZG5iE6FNS0dOY,5734
|
|
50
|
-
cape_parsers/CAPE/core/Remcos.py,sha256=
|
|
52
|
+
cape_parsers/CAPE/core/Remcos.py,sha256=MIpO2FwehBGIhO7hS0TT2hdDsgvxlI5ps4rAwyFwdTY,9483
|
|
51
53
|
cape_parsers/CAPE/core/Rhadamanthys.py,sha256=mx7kEF1e8LJZbwh2uUwU56ZKgrpLqZvYVDoqm-Dvl9w,6075
|
|
52
54
|
cape_parsers/CAPE/core/SmokeLoader.py,sha256=ruQ_GDiZvqtGxUTbN2N6fajUYWkIylFTvMXijgZ8L20,3890
|
|
53
55
|
cape_parsers/CAPE/core/Socks5Systemz.py,sha256=jSt6QejL5K99dIB3qdItvUHL28w6N60xuwc8EQHM5Mk,783
|
|
@@ -60,7 +62,7 @@ cape_parsers/CAPE/core/test_cape.py,sha256=CrmghlO43hpnTLv0X8Dw4hTcrVHuJ0X20dPXc
|
|
|
60
62
|
cape_parsers/RATDecoders/README.md,sha256=SHgVQraCdp033IQjM4Cm6t70U4kULn1MfSwTq3rsZv8,22
|
|
61
63
|
cape_parsers/RATDecoders/__init__.py,sha256=JcY8WPKzUFYgexwV1eyKIuT1JyNZzMJjBynlPSzxY_I,7
|
|
62
64
|
cape_parsers/RATDecoders/test_rats.py,sha256=swkWvbnCd6_2aUP6MnIF4hyPL8zsdhtjlsBfx5Phgk4,610
|
|
63
|
-
cape_parsers/__init__.py,sha256=
|
|
65
|
+
cape_parsers/__init__.py,sha256=yInPN6GBAPBgIDGTXVgWB1wrRZ3yJsMk0ywcWAGvop0,6232
|
|
64
66
|
cape_parsers/deprecated/BackOffLoader.py,sha256=gIwNDsWm1xGR9whKEEj1eTBB1-KTLY0_yNE50xVScKo,1402
|
|
65
67
|
cape_parsers/deprecated/BackOffPOS.py,sha256=lG7a_bXD3Exaoy-_lHpa90yiv_DesICFqClhqS_d8nk,1486
|
|
66
68
|
cape_parsers/deprecated/BlackNix.py,sha256=NPqXiHWt_UtLm35gi58UriEJRt_L_UWGfS8jvblAECM,2667
|
|
@@ -106,7 +108,7 @@ cape_parsers/utils/blzpack_lib.so,sha256=5PJtnggw8fV5q4DlhwMJk4ZadvC3fFTsVTNZKvE
|
|
|
106
108
|
cape_parsers/utils/dotnet_utils.py,sha256=pzQGbCqccz7DRv8T_i1JURlrKDIlDT2axxViiFF9hsU,1672
|
|
107
109
|
cape_parsers/utils/lznt1.py,sha256=X-BmJtP6AwYSl0ORg5dfSt-NIuXbHrtCO5kUaaJI2C8,4066
|
|
108
110
|
cape_parsers/utils/strings.py,sha256=a-nbvP9jYST7b6t_H37Ype-fK2jEmQr-wMF5a4i04e4,3062
|
|
109
|
-
cape_parsers-0.1.
|
|
110
|
-
cape_parsers-0.1.
|
|
111
|
-
cape_parsers-0.1.
|
|
112
|
-
cape_parsers-0.1.
|
|
111
|
+
cape_parsers-0.1.49.dist-info/LICENSE,sha256=88c01_HLG8WPj7R7aU_b-O-UoF38vrrifvcko4KDxcE,1069
|
|
112
|
+
cape_parsers-0.1.49.dist-info/METADATA,sha256=JiiD9noZxpf8bqsPWQRJ2siYMcHkqo6VrpkwYD2r3MU,1753
|
|
113
|
+
cape_parsers-0.1.49.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
114
|
+
cape_parsers-0.1.49.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|