CAPE-parsers 0.1.37__py3-none-any.whl → 0.1.38__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.
@@ -3,10 +3,10 @@ import json
3
3
  import re
4
4
  import struct
5
5
  from contextlib import suppress
6
-
7
6
  import pefile
8
7
  import yara
9
8
 
9
+
10
10
  RULE_SOURCE_BUILD_ID = """rule LummaBuildId
11
11
  {
12
12
  meta:
@@ -37,6 +37,42 @@ RULE_SOURCE_LUMMA = """rule LummaConfig
37
37
  $chunk_1
38
38
  }"""
39
39
 
40
+ RULE_SOURCE_LUMMA_NEW_KEYS = """rule LummaConfigNewKeys
41
+ {
42
+ meta:
43
+ author = "YungBinary"
44
+ strings:
45
+ $key_nonce = {
46
+ 88 44 24 ??
47
+ B8 ?? ?? ?? ??
48
+ BF ?? ?? ?? ??
49
+ B9 08 00 00 00
50
+ 96
51
+ F3 A5
52
+ 96
53
+ B8 ?? ?? ?? ??
54
+ }
55
+ condition:
56
+ uint16(0) == 0x5A4D and $key_nonce
57
+ }"""
58
+
59
+ RULE_SOURCE_LUMMA_NEW_ENCRYPTED_C2 = """rule LummaConfigNewEncryptedStrings
60
+ {
61
+ meta:
62
+ author = "YungBinary"
63
+ strings:
64
+ $encrypted_array = {
65
+ 0F B6 C?
66
+ C1 E0 07
67
+ 8D 80 ?? ?? ?? ??
68
+ 8D 74 24 10
69
+ FF
70
+ }
71
+ condition:
72
+ uint16(0) == 0x5A4D and $encrypted_array
73
+ }"""
74
+
75
+
40
76
 
41
77
  def yara_scan_generator(raw_data, rule_source):
42
78
  yara_rules = yara.compile(source=rule_source)
@@ -225,6 +261,15 @@ def get_build_id(pe, data):
225
261
  continue
226
262
  return build_id
227
263
 
264
+ def get_build_id_new(data):
265
+ build_id = ""
266
+ pattern = b'123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\x00'
267
+ offset = data.find(pattern)
268
+ if offset != -1:
269
+ build_id = data[offset + len(pattern):].split(b'\x00', 1)[0]
270
+ build_id = build_id.decode()
271
+
272
+ return build_id
228
273
 
229
274
  def extract_config(data):
230
275
  config_dict = {"C2": []}
@@ -236,32 +281,73 @@ def extract_config(data):
236
281
  pe = pefile.PE(data=data, fast_load=True)
237
282
  image_base = pe.OPTIONAL_HEADER.ImageBase
238
283
 
239
- offset = yara_scan(data, RULE_SOURCE_LUMMA)
240
- if offset:
241
- key = data[offset + 16 : offset + 48]
242
- nonce = b"\x00\x00\x00\x00" + data[offset + 48 : offset + 56]
243
-
244
- for i in range(9):
245
- try:
246
- start_offset = offset + 56 + (i * 4)
247
- end_offset = start_offset + 4
248
- c2_dword_rva = struct.unpack('i', data[start_offset : end_offset])[0]
249
- if pe:
250
- c2_dword_offset = pe.get_offset_from_rva(c2_dword_rva - image_base)
251
- else:
252
- c2_dword_offset = c2_dword_rva - image_base
284
+ # Parse the latest version
285
+ key = None
286
+ nonce = None
287
+ for offset in yara_scan_generator(data, RULE_SOURCE_LUMMA_NEW_KEYS):
288
+ key_rva = struct.unpack('i', data[offset + 5 : offset + 9])[0]
289
+ key_offset = pe.get_offset_from_rva(key_rva - image_base)
290
+ key = data[key_offset : key_offset + 32]
291
+ nonce_rva = struct.unpack('i', data[offset + 24 : offset + 28])[0]
292
+ nonce_offset = pe.get_offset_from_rva(nonce_rva - image_base)
293
+ nonce = b'\x00\x00\x00\x00' + data[nonce_offset : nonce_offset + 8]
294
+
295
+ if key and nonce:
296
+ for offset in yara_scan_generator(data, RULE_SOURCE_LUMMA_NEW_ENCRYPTED_C2):
297
+ encrypted_strings_rva = struct.unpack('i', data[offset + 8 : offset + 12])[0]
298
+ encrypted_strings_offset = pe.get_offset_from_rva(encrypted_strings_rva - image_base)
299
+ step_size = 0x80
300
+ counter = 2
301
+ for i in range(12):
302
+ encrypted_string = data[encrypted_strings_offset:encrypted_strings_offset+40]
303
+ decoded_c2 = chacha20_xor(encrypted_string, key, nonce, counter).split(b'\x00', 1)[0]
304
+ if contains_non_printable(decoded_c2):
305
+ break
306
+ config_dict["C2"].append(decoded_c2.decode())
307
+ encrypted_strings_offset = encrypted_strings_offset + step_size
308
+ counter += 2
309
+
310
+ if config_dict["C2"]:
311
+ # If found C2 servers try to find build ID
312
+ build_id = get_build_id_new(data)
313
+ if build_id:
314
+ config_dict["Build ID"] = build_id
315
+
316
+
317
+ # If no C2s try with the version after Jan 21, 2025
318
+ if not config_dict["C2"]:
319
+ offset = yara_scan(data, RULE_SOURCE_LUMMA)
320
+ if offset:
321
+ key = data[offset + 16 : offset + 48]
322
+ nonce = b"\x00\x00\x00\x00" + data[offset + 48 : offset + 56]
253
323
 
254
- c2_encrypted = data[c2_dword_offset : c2_dword_offset + 0x80]
255
- counters = [0, 2, 4, 6, 8, 10, 12, 14, 16]
256
- for counter in counters:
257
- decrypted = chacha20_xor(c2_encrypted, key, nonce, counter)
258
- c2 = extract_c2_domain(decrypted)
259
- if c2 is not None and len(c2) > 10:
260
- config_dict["C2"].append(c2.decode())
261
- break
324
+ for i in range(9):
325
+ try:
326
+ start_offset = offset + 56 + (i * 4)
327
+ end_offset = start_offset + 4
328
+ c2_dword_rva = struct.unpack('i', data[start_offset : end_offset])[0]
329
+ if pe:
330
+ c2_dword_offset = pe.get_offset_from_rva(c2_dword_rva - image_base)
331
+ else:
332
+ c2_dword_offset = c2_dword_rva - image_base
333
+
334
+ c2_encrypted = data[c2_dword_offset : c2_dword_offset + 0x80]
335
+ counters = [0, 2, 4, 6, 8, 10, 12, 14, 16]
336
+ for counter in counters:
337
+ decrypted = chacha20_xor(c2_encrypted, key, nonce, counter)
338
+ c2 = extract_c2_domain(decrypted)
339
+ if c2 is not None and len(c2) > 10:
340
+ config_dict["C2"].append(c2.decode())
341
+ break
262
342
 
263
- except Exception:
264
- continue
343
+ except Exception:
344
+ continue
345
+
346
+ if config_dict["C2"] and pe is not None:
347
+ # If found C2 servers try to find build ID
348
+ build_id = get_build_id(pe, data)
349
+ if build_id:
350
+ config_dict["Build ID"] = build_id
265
351
 
266
352
 
267
353
  # If no C2s try with version prior to Jan 21, 2025
@@ -293,11 +379,12 @@ def extract_config(data):
293
379
  except Exception:
294
380
  return
295
381
 
296
- if config_dict["C2"] and pe is not None:
297
- # If found C2 servers try to find build ID
298
- build_id = get_build_id(pe, data)
299
- if build_id:
300
- config_dict["Build ID"] = build_id
382
+ if config_dict["C2"] and pe is not None:
383
+ # If found C2 servers try to find build ID
384
+ build_id = get_build_id(pe, data)
385
+ if build_id:
386
+ config_dict["Build ID"] = build_id
387
+
301
388
 
302
389
  return config_dict
303
390
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: CAPE-parsers
3
- Version: 0.1.37
3
+ Version: 0.1.38
4
4
  Summary: CAPE: Malware Configuration Extraction
5
5
  License: MIT
6
6
  Keywords: cape,parsers,malware,configuration
@@ -14,7 +14,7 @@ cape_parsers/CAPE/community/Fareit.py,sha256=NYkcF7Ddf7SqaSJwGesGTumTJ2p8AT9qBE4
14
14
  cape_parsers/CAPE/community/Greame.py,sha256=99W1aUoSNAQ9KMO85liel5rAN0Wutzo-m176iwfOzds,3633
15
15
  cape_parsers/CAPE/community/KoiLoader.py,sha256=ZTDm7tGGNFyW8N9l35_ta7ucBuE5AL9YprNR36kfid8,4029
16
16
  cape_parsers/CAPE/community/LokiBot.py,sha256=whdVVLqu760ai90Ep-_Ghc_Z1yaty9fMSOcnY5IajXc,5660
17
- cape_parsers/CAPE/community/Lumma.py,sha256=o8XiT1f48e_JdkbjCUFJ04uzCUr-L57bhH_12duiFyE,8978
17
+ cape_parsers/CAPE/community/Lumma.py,sha256=XX3mMJyRqxyqKevSDlkFOFAOLUaYqAe0qBcGFbrGPfg,12009
18
18
  cape_parsers/CAPE/community/NanoCore.py,sha256=0dqhCoAyDJaYgAlbXIwCa1esfEuQSk5AtH1Rl4bj1l8,6120
19
19
  cape_parsers/CAPE/community/Nighthawk.py,sha256=eXnDqwabnrlRROg503oXYLEgotMW4hKeYwLas8SrkTc,12104
20
20
  cape_parsers/CAPE/community/Njrat.py,sha256=_noQM5058BYwTMcYCpcTD9gIxw4ANI35tUSLMAlN97Q,4713
@@ -105,7 +105,7 @@ cape_parsers/utils/blzpack_lib.so,sha256=5PJtnggw8fV5q4DlhwMJk4ZadvC3fFTsVTNZKvE
105
105
  cape_parsers/utils/dotnet_utils.py,sha256=pzQGbCqccz7DRv8T_i1JURlrKDIlDT2axxViiFF9hsU,1672
106
106
  cape_parsers/utils/lznt1.py,sha256=X-BmJtP6AwYSl0ORg5dfSt-NIuXbHrtCO5kUaaJI2C8,4066
107
107
  cape_parsers/utils/strings.py,sha256=a-nbvP9jYST7b6t_H37Ype-fK2jEmQr-wMF5a4i04e4,3062
108
- cape_parsers-0.1.37.dist-info/LICENSE,sha256=88c01_HLG8WPj7R7aU_b-O-UoF38vrrifvcko4KDxcE,1069
109
- cape_parsers-0.1.37.dist-info/METADATA,sha256=P_Q2_z1zWOvRibQcJzs0DfetfEoqFCI4H9edQLnlHeM,1149
110
- cape_parsers-0.1.37.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
111
- cape_parsers-0.1.37.dist-info/RECORD,,
108
+ cape_parsers-0.1.38.dist-info/LICENSE,sha256=88c01_HLG8WPj7R7aU_b-O-UoF38vrrifvcko4KDxcE,1069
109
+ cape_parsers-0.1.38.dist-info/METADATA,sha256=7KCoZihNV7M_C9WmWwwcQPZpOSGNWUnKIvD2Y1rS5kY,1149
110
+ cape_parsers-0.1.38.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
111
+ cape_parsers-0.1.38.dist-info/RECORD,,