CAPE-parsers 0.1.55__py3-none-any.whl → 0.1.56__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.
@@ -0,0 +1,186 @@
1
+ import base64
2
+ import json
3
+ import pefile
4
+ import yara
5
+ import struct
6
+ import re
7
+ import ipaddress
8
+ from contextlib import suppress
9
+
10
+
11
+ DESCRIPTION = "Amatera Stealer parser"
12
+ AUTHOR = "YungBinary"
13
+
14
+ RULE_SOURCE = """
15
+ rule AmateraDecrypt
16
+ {
17
+ meta:
18
+ author = "YungBinary"
19
+ description = "Find Amatera XOR key"
20
+ strings:
21
+ $decrypt = {
22
+ A1 ?? ?? ?? ?? // mov eax, dword ptr ds:szXorKey ; "852149723"
23
+ 89 45 ?? // mov dword ptr [ebp+xor_key], eax
24
+ 8B 0D ?? ?? ?? ?? // mov ecx, dword ptr ds:szXorKey+4 ; "49723"
25
+ 89 4D ?? // mov dword ptr [ebp+xor_key+4], ecx
26
+ 66 8B 15 ?? ?? ?? ?? // mov dx, word ptr ds:szXorKey+8 ; "3"
27
+ 66 89 55 ?? // mov word ptr [ebp+xor_key+8], dx
28
+ 8D 45 ?? // lea eax, [ebp+xor_key]
29
+ 50 // push eax
30
+ E8 // call
31
+ }
32
+ condition:
33
+ uint16(0) == 0x5A4D and $decrypt
34
+ }
35
+ """
36
+
37
+
38
+ RULE_SOURCE_AES_KEY = """
39
+ rule AmateraAESKey
40
+ {
41
+ meta:
42
+ author = "YungBinary"
43
+ description = "Find Amatera AES key"
44
+ strings:
45
+ $aes_key_on_stack = {
46
+ 83 EC 2C // sub esp, 2Ch
47
+ C6 45 D4 ?? // mov byte ptr [ebp-2Ch], ??
48
+ C6 45 D5 ?? // mov byte ptr [ebp-2Bh], ??
49
+ C6 45 D6 ?? // mov byte ptr [ebp-2Ah], ??
50
+ C6 45 D7 ?? // mov byte ptr [ebp-29h], ??
51
+ C6 45 D8 ?? // mov byte ptr [ebp-28h], ??
52
+ C6 45 D9 ?? // mov byte ptr [ebp-27h], ??
53
+ C6 45 DA ?? // mov byte ptr [ebp-26h], ??
54
+ C6 45 DB ?? // mov byte ptr [ebp-25h], ??
55
+ C6 45 DC ?? // mov byte ptr [ebp-24h], ??
56
+ C6 45 DD ?? // mov byte ptr [ebp-23h], ??
57
+ C6 45 DE ?? // mov byte ptr [ebp-22h], ??
58
+ C6 45 DF ?? // mov byte ptr [ebp-21h], ??
59
+ C6 45 E0 ?? // mov byte ptr [ebp-20h], ??
60
+ C6 45 E1 ?? // mov byte ptr [ebp-1Fh], ??
61
+ C6 45 E2 ?? // mov byte ptr [ebp-1Eh], ??
62
+ C6 45 E3 ?? // mov byte ptr [ebp-1Dh], ??
63
+ C6 45 E4 ?? // mov byte ptr [ebp-1Ch], ??
64
+ C6 45 E5 ?? // mov byte ptr [ebp-1Bh], ??
65
+ C6 45 E6 ?? // mov byte ptr [ebp-1Ah], ??
66
+ C6 45 E7 ?? // mov byte ptr [ebp-19h], ??
67
+ C6 45 E8 ?? // mov byte ptr [ebp-18h], ??
68
+ C6 45 E9 ?? // mov byte ptr [ebp-17h], ??
69
+ C6 45 EA ?? // mov byte ptr [ebp-16h], ??
70
+ C6 45 EB ?? // mov byte ptr [ebp-15h], ??
71
+ C6 45 EC ?? // mov byte ptr [ebp-14h], ??
72
+ C6 45 ED ?? // mov byte ptr [ebp-13h], ??
73
+ C6 45 EE ?? // mov byte ptr [ebp-12h], ??
74
+ C6 45 EF ?? // mov byte ptr [ebp-11h], ??
75
+ C6 45 F0 ?? // mov byte ptr [ebp-10h], ??
76
+ C6 45 F1 ?? // mov byte ptr [ebp-0Fh], ??
77
+ C6 45 F2 ?? // mov byte ptr [ebp-0Eh], ??
78
+ C6 45 F3 ?? // mov byte ptr [ebp-0Dh], ??
79
+ C7 45 F4 10 00 00 00 // mov dword ptr [ebp-0Ch], 10h
80
+ }
81
+ condition:
82
+ uint16(0) == 0x5A4D and $aes_key_on_stack
83
+ }
84
+ """
85
+
86
+ DOMAIN_REGEX = r'^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'
87
+
88
+
89
+ def yara_scan(raw_data: bytes, rule_source: str):
90
+ yara_rules = yara.compile(source=rule_source)
91
+ matches = yara_rules.match(data=raw_data)
92
+
93
+ for match in matches:
94
+ for block in match.strings:
95
+ for instance in block.instances:
96
+ return instance.offset
97
+
98
+
99
+ def extract_base64_strings(data: bytes, minchars: int, maxchars: int):
100
+ """
101
+ Generator that returns ASCII formatted base64 strings
102
+ """
103
+ apat = b"([A-Za-z0-9+/=]{" + str(minchars).encode() + b"," + str(maxchars).encode() + b"})\x00"
104
+ for s in re.findall(apat, data):
105
+ yield s.decode()
106
+
107
+
108
+ def xor_data(data, key):
109
+ decoded = bytearray()
110
+ for i in range(len(data)):
111
+ decoded.append(key[i % len(key)] ^ data[i])
112
+ return decoded
113
+
114
+
115
+ def is_public_ip(ip):
116
+ try:
117
+ # This will raise a ValueError if the IP format is incorrect
118
+ ip_obj = ipaddress.ip_address(ip.decode())
119
+ if ip_obj.is_private:
120
+ return False
121
+ return True
122
+ except Exception:
123
+ return False
124
+
125
+
126
+ def is_valid_domain(data):
127
+ try:
128
+ if re.fullmatch(DOMAIN_REGEX, data.decode()):
129
+ return True
130
+ return False
131
+ except Exception:
132
+ return False
133
+
134
+
135
+ def extract_config(data):
136
+ """
137
+ Extract Amatera malware configuration.
138
+ """
139
+ config_dict = {}
140
+
141
+ with suppress(Exception):
142
+ pe = pefile.PE(data=data)
143
+ image_base = pe.OPTIONAL_HEADER.ImageBase
144
+
145
+ # Identify XOR key decryption routine and extract key
146
+ offset = yara_scan(data, RULE_SOURCE)
147
+ if not offset:
148
+ return config_dict
149
+ key_str_va = struct.unpack('i', data[offset + 1: offset + 5])[0]
150
+ key_str = pe.get_string_at_rva(key_str_va - image_base, max_length=20) + b'\x00'
151
+
152
+ # Extract AES 256 key
153
+ aes_key_offset = yara_scan(data, RULE_SOURCE_AES_KEY)
154
+ aes_key = bytearray()
155
+ if aes_key_offset:
156
+ aes_block = data[aes_key_offset : aes_key_offset + 131]
157
+ for i in range(0, len(aes_block) - 4, 4):
158
+ aes_key.append(aes_block[i+6])
159
+
160
+ # Handle each base64 string -> decode -> decrypt with XOR key
161
+ for b64_str in extract_base64_strings(data, 8, 20):
162
+ try:
163
+ decoded = base64.b64decode(b64_str, validate=True)
164
+ decrypted = xor_data(decoded, key_str)
165
+ if not is_public_ip(decrypted) and not is_valid_domain(decrypted):
166
+ continue
167
+
168
+ config_dict["CNCs"] = [f"https://{decrypted.decode()}"]
169
+
170
+ if aes_key:
171
+ config_dict["cryptokey"] = aes_key.hex()
172
+ config_dict["cryptokey_type"] = "AES"
173
+
174
+ return config_dict
175
+ except Exception:
176
+ continue
177
+
178
+ return config_dict
179
+
180
+
181
+ if __name__ == "__main__":
182
+ import sys
183
+
184
+ with open(sys.argv[1], "rb") as f:
185
+ config_json = json.dumps(extract_config(f.read()), indent=4)
186
+ print(config_json)
@@ -119,7 +119,7 @@ def extract_config(data):
119
119
  encoded_payload = remove_nulls(encoded_payload, encoded_payload_size)
120
120
  decoded_payload = xor_data(encoded_payload, xor_key)
121
121
  cncs = find_c2(decoded_payload)
122
- if cncs and list(filter(cncs, None)):
122
+ if cncs:
123
123
  config["CNCs"] = cncs
124
124
 
125
125
  return config
@@ -33,7 +33,7 @@ rule NitroBunnyDownloader
33
33
  hash = "960e59200ec0a4b5fb3b44e6da763f5fec4092997975140797d4eec491de411b"
34
34
  strings:
35
35
  $config1 = {E8 [3] 00 41 B8 ?? ?? 00 00 48 8D 15 [3] 00 48 89 C1 48 89 ?? E8 [3] 00}
36
- $config2 = {E8 [3] 00 48 8D 15 [3] 00 41 B8 ?? ?? 00 00 48 89 C1 48 89 ?? E8 [3] 00}
36
+ $config2 = {E8 [3] 00 48 8D 15 [3] 00 41 B8 ?? ?? 00 00 48 89 C1 48 89 ?? E8 [3] 00}
37
37
  $string1 = "X-Amz-User-Agent:" wide
38
38
  $string2 = "Amz-Security-Flag:" wide
39
39
  $string3 = "/cart" wide
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CAPE-parsers
3
- Version: 0.1.55
3
+ Version: 0.1.56
4
4
  Summary: CAPE: Malware Configuration Extraction
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,7 @@
1
1
  cape_parsers/CAPE/__init__.py,sha256=JcY8WPKzUFYgexwV1eyKIuT1JyNZzMJjBynlPSzxY_I,7
2
2
  cape_parsers/CAPE/community/AgentTesla.py,sha256=ln5MqFXkTb7WrlDrUHNTnMWBYRHDSqyK4VHeq0ZldtA,4047
3
3
  cape_parsers/CAPE/community/Amadey.py,sha256=IUyt909q9IDQPPip6UW9uD16rJMD_gvkwvNZ8NHTW-k,5577
4
+ cape_parsers/CAPE/community/Amatera.py,sha256=8s94SW58r04bs2KuIdJJ5-dm4WfoOrwINEqdSuFJKHk,7096
4
5
  cape_parsers/CAPE/community/Arkei.py,sha256=k36qHxdo5yPa9V1cg7EImSWP06kMog0rBda4KXqLKCY,3783
5
6
  cape_parsers/CAPE/community/AsyncRAT.py,sha256=0-FRT3d2x63KQ_cs1xmKFj7x0JRf7ID6QDc_DvBa0PM,1003
6
7
  cape_parsers/CAPE/community/AuroraStealer.py,sha256=LRu2QFBYkGhRGDJBw3GlcKub4E0_TBWmjdR2PnobDZM,2643
@@ -9,7 +10,7 @@ cape_parsers/CAPE/community/CobaltStrikeBeacon.py,sha256=U4Q0ObCrPRpiO5B5fBmkgr6
9
10
  cape_parsers/CAPE/community/CobaltStrikeStager.py,sha256=HLxROBjz453uHNq1bPz0VSAhtyWDfz79ZacTPdjuWmY,7535
10
11
  cape_parsers/CAPE/community/DCRat.py,sha256=0-FRT3d2x63KQ_cs1xmKFj7x0JRf7ID6QDc_DvBa0PM,1003
11
12
  cape_parsers/CAPE/community/Fareit.py,sha256=OyKeZdcvyAhjxZgJqkDPJHP4Npv1ArvTHJZ5F0C1Iac,1875
12
- cape_parsers/CAPE/community/KoiLoader.py,sha256=cWV3TcVz5Qg7kkmMYYtuh5cXhAKohGAk6d6aXuId0lc,4077
13
+ cape_parsers/CAPE/community/KoiLoader.py,sha256=tCl22pzNnuzC9dvlh13oa_KEBlMwchrEhy5KjipfyiM,4048
13
14
  cape_parsers/CAPE/community/LokiBot.py,sha256=355kqLx0LNMr8XcGfPL7cxG8QZalcmE7ttVBqoWtTWE,5754
14
15
  cape_parsers/CAPE/community/Lumma.py,sha256=Iqd9yvt3g0FeV_bYRmL1RKp4C1H92qeGg4fXivVDSxw,12206
15
16
  cape_parsers/CAPE/community/MonsterV2.py,sha256=cFxhYxo7FruTMmFY3OtBO-E0hDyxfsC3zWX3BlcB-qI,2915
@@ -44,7 +45,7 @@ cape_parsers/CAPE/core/GuLoader.py,sha256=wH6t1e7rO60Bwe0ulqFdZq12-M087zT5WQtC_W
44
45
  cape_parsers/CAPE/core/IcedID.py,sha256=TEsvFq8qHz_D5kIURKWSC4lbvWaQbMriDZ3jQsVu2VA,4029
45
46
  cape_parsers/CAPE/core/IcedIDLoader.py,sha256=YUOEILpTycO01KK4qqAxGSplsRVs2EzjscUw4T-DGWs,1602
46
47
  cape_parsers/CAPE/core/Latrodectus.py,sha256=1K9yUUYtzRJ2c3unrYIUaA8nE--Zoqi5pjXY7t7t1qg,7751
47
- cape_parsers/CAPE/core/NitroBunnyDownloader.py,sha256=nxySGP7t5yv7-YL31-IiMxzkKCYKrDQgzWfMXadha2g,5265
48
+ cape_parsers/CAPE/core/NitroBunnyDownloader.py,sha256=EbQbfwGQXiOv2_cOK1-lcwbpKYo9t2cqHBv0FAw3keI,5257
48
49
  cape_parsers/CAPE/core/Oyster.py,sha256=QStBScevJuLyd5d4Rw093SxTlbRG1LFkDwYgmjZx-EQ,4881
49
50
  cape_parsers/CAPE/core/PikaBot.py,sha256=6Q8goXfMsSoU8UkdE9iuZY2KTxX_AmWhH1szke_HfWA,5280
50
51
  cape_parsers/CAPE/core/PlugX.py,sha256=lGwr1T3mttG6CTbZCj_Cf5HnOad60A3LP264jlCsGsc,13192
@@ -111,7 +112,7 @@ cape_parsers/utils/blzpack_lib.so,sha256=5PJtnggw8fV5q4DlhwMJk4ZadvC3fFTsVTNZKvE
111
112
  cape_parsers/utils/dotnet_utils.py,sha256=pzQGbCqccz7DRv8T_i1JURlrKDIlDT2axxViiFF9hsU,1672
112
113
  cape_parsers/utils/lznt1.py,sha256=X-BmJtP6AwYSl0ORg5dfSt-NIuXbHrtCO5kUaaJI2C8,4066
113
114
  cape_parsers/utils/strings.py,sha256=a-nbvP9jYST7b6t_H37Ype-fK2jEmQr-wMF5a4i04e4,3062
114
- cape_parsers-0.1.55.dist-info/METADATA,sha256=eQvSMB-kC-_Hrdc78Sv-uxnNrVywaTqlLf_cjIG-gy8,1826
115
- cape_parsers-0.1.55.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
116
- cape_parsers-0.1.55.dist-info/licenses/LICENSE,sha256=88c01_HLG8WPj7R7aU_b-O-UoF38vrrifvcko4KDxcE,1069
117
- cape_parsers-0.1.55.dist-info/RECORD,,
115
+ cape_parsers-0.1.56.dist-info/METADATA,sha256=ZF16inhJ6lLHbtRig0HNpioM6cBkYIeCILPFpcxfQbA,1826
116
+ cape_parsers-0.1.56.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
117
+ cape_parsers-0.1.56.dist-info/licenses/LICENSE,sha256=88c01_HLG8WPj7R7aU_b-O-UoF38vrrifvcko4KDxcE,1069
118
+ cape_parsers-0.1.56.dist-info/RECORD,,