CAPE-parsers 0.1.53__tar.gz → 0.1.54__tar.gz
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-0.1.53 → cape_parsers-0.1.54}/PKG-INFO +1 -1
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Lumma.py +10 -5
- cape_parsers-0.1.54/cape_parsers/CAPE/core/NitroBunnyDownloader.py +151 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Rhadamanthys.py +35 -12
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/pyproject.toml +1 -1
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/LICENSE +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/README.md +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/__init__.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/AgentTesla.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Amadey.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Arkei.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/AsyncRAT.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/AuroraStealer.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Carbanak.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/CobaltStrikeBeacon.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/CobaltStrikeStager.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/DCRat.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Fareit.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/KoiLoader.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/LokiBot.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/MonsterV2.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/MyKings.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/NanoCore.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Nighthawk.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Njrat.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/PhemedroneStealer.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/QuasarRAT.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/README.md +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Snake.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/SparkRAT.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/Stealc.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/VenomRAT.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/WinosStager.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/XWorm.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/XenoRAT.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/__init__.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/AdaptixBeacon.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/AuraStealer.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Azorult.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/BitPaymer.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/BlackDropper.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Blister.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/BruteRatel.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/BumbleBee.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/DarkGate.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/DoppelPaymer.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/DridexLoader.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Formbook.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/GuLoader.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/IcedID.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/IcedIDLoader.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Latrodectus.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Oyster.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/PikaBot.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/PlugX.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/QakBot.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Quickbind.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/README.md +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/RedLine.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Remcos.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/SmokeLoader.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Socks5Systemz.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/SquirrelWaffle.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Strrat.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/WarzoneRAT.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/Zloader.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/__init__.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/core/test_cape.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/RATDecoders/README.md +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/RATDecoders/__init__.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/RATDecoders/test_rats.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/__init__.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/BackOffLoader.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/BackOffPOS.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/BlackNix.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/BuerLoader.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/ChChes.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Emotet.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Enfal.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/EvilGrab.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Greame.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Hancitor.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/HttpBrowser.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/JavaDropper.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Nymaim.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Pandora.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/PoisonIvy.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/PredatorPain.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Punisher.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/RCSession.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/REvil.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/RedLeaf.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Retefe.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/Rozena.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/SmallNet.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/TSCookie.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/TrickBot.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/UrsnifV3.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/_ShadowTech.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/_VirusRat.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/_jRat.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/unrecom.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/deprecated/xRAT.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/malduck/LICENSE +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/malduck/README.md +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/malduck/__init__.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/malduck/test_malduck.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/mwcp/README.md +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/mwcp/__init__.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/mwcp/test_mwcp.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/utils/__init__.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/utils/aplib.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/utils/blzpack.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/utils/blzpack_lib.so +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/utils/dotnet_utils.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/utils/lznt1.py +0 -0
- {cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/utils/strings.py +0 -0
|
@@ -5,7 +5,7 @@ import struct
|
|
|
5
5
|
from contextlib import suppress
|
|
6
6
|
import pefile
|
|
7
7
|
import yara
|
|
8
|
-
|
|
8
|
+
from Cryptodome.Cipher import ChaCha20
|
|
9
9
|
|
|
10
10
|
RULE_SOURCE_BUILD_ID = """rule LummaBuildId
|
|
11
11
|
{
|
|
@@ -142,7 +142,7 @@ def contains_non_printable(byte_array):
|
|
|
142
142
|
return True
|
|
143
143
|
return False
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
"""
|
|
146
146
|
def mask32(x):
|
|
147
147
|
return x & 0xFFFFFFFF
|
|
148
148
|
|
|
@@ -242,7 +242,7 @@ def chacha20_xor(message, key, nonce, counter):
|
|
|
242
242
|
xor_key.append(message[i] ^ key_stream[i])
|
|
243
243
|
|
|
244
244
|
return xor_key
|
|
245
|
-
|
|
245
|
+
"""
|
|
246
246
|
|
|
247
247
|
def extract_c2_domain(data):
|
|
248
248
|
pattern = rb"([\w-]+\.[\w]+)\x00"
|
|
@@ -315,7 +315,9 @@ def extract_config(data):
|
|
|
315
315
|
counter = 2
|
|
316
316
|
for i in range(12):
|
|
317
317
|
encrypted_string = data[encrypted_strings_offset : encrypted_strings_offset + 40]
|
|
318
|
-
|
|
318
|
+
chacha20_cipher = ChaCha20.new(key=key, nonce=nonce)
|
|
319
|
+
chacha20_cipher.seek(counter)
|
|
320
|
+
decoded_c2 = chacha20_cipher.decrypt(encrypted_string).split(b"\x00", 1)[0]
|
|
319
321
|
if contains_non_printable(decoded_c2):
|
|
320
322
|
break
|
|
321
323
|
config.setdefault("CNCs", []).append("https://" + decoded_c2.decode())
|
|
@@ -348,7 +350,10 @@ def extract_config(data):
|
|
|
348
350
|
c2_encrypted = data[c2_dword_offset : c2_dword_offset + 0x80]
|
|
349
351
|
counters = [0, 2, 4, 6, 8, 10, 12, 14, 16]
|
|
350
352
|
for counter in counters:
|
|
351
|
-
decrypted = chacha20_xor(c2_encrypted, key, nonce, counter)
|
|
353
|
+
# decrypted = chacha20_xor(c2_encrypted, key, nonce, counter)
|
|
354
|
+
chacha20_cipher = ChaCha20.new(key=key, nonce=nonce)
|
|
355
|
+
chacha20_cipher.seek(counter)
|
|
356
|
+
decrypted = chacha20_cipher.decrypt(c2_encrypted).split(b"\x00", 1)[0]
|
|
352
357
|
c2 = extract_c2_domain(decrypted)
|
|
353
358
|
if c2 is not None and len(c2) > 10:
|
|
354
359
|
config["CNCs"].append("https://" + c2.decode())
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Copyright (C) 2024 enzok
|
|
2
|
+
# This program is free software: you can redistribute it and/or modify
|
|
3
|
+
# it under the terms of the GNU General Public License as published by
|
|
4
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
5
|
+
# (at your option) any later version.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
# GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License
|
|
13
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import struct
|
|
17
|
+
|
|
18
|
+
import pefile
|
|
19
|
+
import yara
|
|
20
|
+
|
|
21
|
+
log = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
DESCRIPTION = "NitroBunnyDownloader configuration parser."
|
|
24
|
+
AUTHOR = "enzok"
|
|
25
|
+
|
|
26
|
+
yara_rule = """
|
|
27
|
+
rule NitroBunnyDownloader
|
|
28
|
+
{
|
|
29
|
+
meta:
|
|
30
|
+
author = "enzok"
|
|
31
|
+
description = "NitroBunnyDownloader Payload"
|
|
32
|
+
cape_type = "NitroBunnyDownloader Payload"
|
|
33
|
+
hash = "960e59200ec0a4b5fb3b44e6da763f5fec4092997975140797d4eec491de411b"
|
|
34
|
+
strings:
|
|
35
|
+
$config = {E8 [3] 00 41 B8 ?? ?? 00 00 48 8D 15 [3] 00 48 89 C1 48 89 ?? E8 [3] 00}
|
|
36
|
+
$string1 = "X-Amz-User-Agent:" wide
|
|
37
|
+
$string2 = "Amz-Security-Flag:" wide
|
|
38
|
+
$string3 = "/cart" wide
|
|
39
|
+
$string4 = "Cookie: " wide
|
|
40
|
+
$string5 = "wishlist" wide
|
|
41
|
+
condition:
|
|
42
|
+
uint16(0) == 0x5A4D and $config and 2 of ($string*)
|
|
43
|
+
}
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
yara_rules = yara.compile(source=yara_rule)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def yara_scan(raw_data):
|
|
50
|
+
try:
|
|
51
|
+
return yara_rules.match(data=raw_data)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print(e)
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def read_dword(data, off):
|
|
58
|
+
if off + 4 > len(data):
|
|
59
|
+
raise ValueError(f"EOF reading dword at {off}")
|
|
60
|
+
val = struct.unpack_from("<I", data, off)[0]
|
|
61
|
+
return val, off + 4
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def read_qword(data, off):
|
|
65
|
+
"""Read a 64-bit unsigned little-endian value."""
|
|
66
|
+
if off + 8 > len(data):
|
|
67
|
+
raise ValueError(f"EOF reading qword at {off}")
|
|
68
|
+
val = struct.unpack_from("<Q", data, off)[0]
|
|
69
|
+
return val, off + 8
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def read_utf16le_string(data, off, length):
|
|
73
|
+
if off + length > len(data):
|
|
74
|
+
raise ValueError(f"EOF reading string at {off} len={length}")
|
|
75
|
+
raw = data[off:off + length]
|
|
76
|
+
s = raw.decode("utf-16le", errors="replace").rstrip("\x00")
|
|
77
|
+
return s, off + length
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def read_string_list(data, off, count):
|
|
81
|
+
items = []
|
|
82
|
+
for i in range(count):
|
|
83
|
+
length_words, off = read_qword(data, off)
|
|
84
|
+
s, off = read_utf16le_string(data, off, length_words)
|
|
85
|
+
items.append(s)
|
|
86
|
+
return items, off
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def extract_config(filebuf):
|
|
90
|
+
yara_hit = yara_scan(filebuf)
|
|
91
|
+
if not yara_hit:
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
cfg = {}
|
|
95
|
+
config_code_offset = None
|
|
96
|
+
for hit in yara_hit:
|
|
97
|
+
if hit.rule != "NitroBunnyDownloader":
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
for item in hit.strings:
|
|
101
|
+
for instance in item.instances:
|
|
102
|
+
if "$config" in item.identifier:
|
|
103
|
+
config_code_offset = instance.offset
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
if config_code_offset is None:
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
pe = pefile.PE(data=filebuf, fast_load=True)
|
|
111
|
+
config_length = pe.get_dword_from_offset(config_code_offset + 7)
|
|
112
|
+
config_offset = pe.get_dword_from_offset(config_code_offset + 14)
|
|
113
|
+
rva = pe.get_rva_from_offset(config_code_offset + 18)
|
|
114
|
+
config_rva = rva + config_offset
|
|
115
|
+
data = pe.get_data(config_rva, config_length)
|
|
116
|
+
off = 0
|
|
117
|
+
raw = cfg["raw"] = {}
|
|
118
|
+
port, off = read_dword(data, off)
|
|
119
|
+
num, off = read_dword(data, off)
|
|
120
|
+
cncs, off = read_string_list(data, off, num)
|
|
121
|
+
num, off = read_qword(data, off)
|
|
122
|
+
raw["user_agent"], off = read_utf16le_string(data, off, num)
|
|
123
|
+
num, off = read_dword(data, off)
|
|
124
|
+
raw["http_header_items"], off = read_string_list(data, off, num)
|
|
125
|
+
num, off = read_dword(data, off)
|
|
126
|
+
raw["uri_list"], off = read_string_list(data, off, num)
|
|
127
|
+
raw["unknown_1"], off = read_dword(data, off)
|
|
128
|
+
raw["unknown_2"], off = read_dword(data, off)
|
|
129
|
+
|
|
130
|
+
if cncs:
|
|
131
|
+
cfg["CNCs"] = []
|
|
132
|
+
schema = {80: "http", 443: "https"}.get(port, "tcp")
|
|
133
|
+
for cnc in cncs:
|
|
134
|
+
cnc = f"{schema}://{cnc}"
|
|
135
|
+
if port not in (80, 443):
|
|
136
|
+
cnc += f":{port}"
|
|
137
|
+
|
|
138
|
+
cfg["CNCs"].append(cnc)
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
log.error("Error: %s", e)
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
return cfg
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == "__main__":
|
|
148
|
+
import sys
|
|
149
|
+
|
|
150
|
+
with open(sys.argv[1], "rb") as f:
|
|
151
|
+
print(extract_config(f.read()))
|
|
@@ -150,6 +150,8 @@ def lzo_noheader_decompress(data: bytes, decompressed_size: int):
|
|
|
150
150
|
ctrl = data[src]
|
|
151
151
|
src += 1
|
|
152
152
|
|
|
153
|
+
# Special short match
|
|
154
|
+
# Copies exactly 3 bytes from dst starting match_len + 1 bytes back.
|
|
153
155
|
if ctrl == 0x20:
|
|
154
156
|
match_len = data[src]
|
|
155
157
|
src += 1
|
|
@@ -159,21 +161,28 @@ def lzo_noheader_decompress(data: bytes, decompressed_size: int):
|
|
|
159
161
|
dst.extend(dst[start:end])
|
|
160
162
|
|
|
161
163
|
elif ctrl >= 0xE0 or ctrl == 0x40:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
elif ctrl == 0x40:
|
|
166
|
-
copy_len = x
|
|
167
|
-
start = data[src + 1]
|
|
168
|
-
if ctrl == 0x40:
|
|
169
|
-
start = data[src]
|
|
164
|
+
# Compute base copy length from the upper bits of ctrl
|
|
165
|
+
base_len = ((ctrl >> 5) - 1) + 3
|
|
166
|
+
|
|
170
167
|
if ctrl >= 0xE0:
|
|
168
|
+
# Long copy: extra length byte follows
|
|
169
|
+
copy_len = base_len + data[src]
|
|
170
|
+
# Offset is byte after
|
|
171
|
+
start = data[src + 1]
|
|
171
172
|
src += 2
|
|
172
173
|
elif ctrl == 0x40:
|
|
174
|
+
# Short copy: offset byte after control code
|
|
175
|
+
copy_len = base_len
|
|
176
|
+
start = data[src]
|
|
173
177
|
src += 1
|
|
178
|
+
|
|
179
|
+
# Calculate offset in output buffer
|
|
174
180
|
offset = len(dst) - start - 1
|
|
181
|
+
|
|
175
182
|
#print(f"Control code: {hex(ctrl)}, Offset backtrack length: {hex(start)}, Current offset: {hex(len(dst))}, New offset: {hex(len(dst) - start)}, Length to copy: {hex(copy_len)}")
|
|
176
|
-
|
|
183
|
+
|
|
184
|
+
# Copy from previously decompressed data
|
|
185
|
+
dst.extend(dst[offset:offset + copy_len])
|
|
177
186
|
|
|
178
187
|
else:
|
|
179
188
|
# Literal run
|
|
@@ -243,11 +252,12 @@ def extract_config(data):
|
|
|
243
252
|
|
|
244
253
|
custom_alphabets = [
|
|
245
254
|
b"ABC1fghijklmnop234NOPQRSTUVWXY567DEFGHIJKLMZ089abcdeqrstuvwxyz-|",
|
|
246
|
-
b"4NOPQRSTUVWXY567DdeEqrstuvwxyz-ABC1fghop23Fijkbc|lmnGHIJKLMZ089a"
|
|
255
|
+
b"4NOPQRSTUVWXY567DdeEqrstuvwxyz-ABC1fghop23Fijkbc|lmnGHIJKLMZ089a", # 0.9.2
|
|
256
|
+
b"3Fijkbc|l4NOPQRSTUVWXY567DdewxEqrstuvyz-ABC1fghop2mnGHIJKLMZ089a", # 0.9.3
|
|
247
257
|
]
|
|
248
258
|
|
|
249
259
|
# Extract base64 strings
|
|
250
|
-
extracted_strings = extract_base64_strings(data,
|
|
260
|
+
extracted_strings = extract_base64_strings(data, 100, 256)
|
|
251
261
|
if not extracted_strings:
|
|
252
262
|
return config_dict
|
|
253
263
|
|
|
@@ -267,7 +277,7 @@ def extract_config(data):
|
|
|
267
277
|
return config_dict
|
|
268
278
|
else:
|
|
269
279
|
# Handle new variants that compress the Command and Control server(s)
|
|
270
|
-
custom_b64_decoded = custom_b64decode(string, custom_alphabets[
|
|
280
|
+
custom_b64_decoded = custom_b64decode(string, custom_alphabets[2])
|
|
271
281
|
xor_key = chacha20_xor(custom_b64_decoded, key, nonce)
|
|
272
282
|
config = decrypt_config(xor_key)
|
|
273
283
|
|
|
@@ -277,6 +287,19 @@ def extract_config(data):
|
|
|
277
287
|
|
|
278
288
|
decompressed = lzo_noheader_decompress(parsed['compressed_data'], parsed['decompressed_size'])
|
|
279
289
|
|
|
290
|
+
# Try old alphabet for 0.9.2
|
|
291
|
+
if not decompressed:
|
|
292
|
+
custom_b64_decoded = custom_b64decode(string, custom_alphabets[1])
|
|
293
|
+
xor_key = chacha20_xor(custom_b64_decoded, key, nonce)
|
|
294
|
+
config = decrypt_config(xor_key)
|
|
295
|
+
|
|
296
|
+
parsed = parse_compression_header(config)
|
|
297
|
+
if not parsed:
|
|
298
|
+
return config_dict
|
|
299
|
+
|
|
300
|
+
decompressed = lzo_noheader_decompress(parsed['compressed_data'], parsed['decompressed_size'])
|
|
301
|
+
|
|
302
|
+
|
|
280
303
|
cncs = [f"https://{chunk.decode()}" for chunk in pattern.split(decompressed) if chunk]
|
|
281
304
|
if cncs:
|
|
282
305
|
config_dict = {"CNCs": cncs}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/CobaltStrikeBeacon.py
RENAMED
|
File without changes
|
{cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/CobaltStrikeStager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cape_parsers-0.1.53 → cape_parsers-0.1.54}/cape_parsers/CAPE/community/PhemedroneStealer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|