Skip to content

Commit f0bae01

Browse files
committed
Merge branch 'master' into staging
1 parent a373073 commit f0bae01

File tree

10 files changed

+2471
-274
lines changed

10 files changed

+2471
-274
lines changed

analyzer/linux/lib/core/packages.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ def __init__(self, target, **kwargs):
101101
self.timeout = kwargs.get("timeout")
102102
# Command-line arguments for the target.
103103

104-
self.args = self.options.get("arguments", [])
104+
_args = self.options.get("arguments", [])
105+
if isinstance(_args, str):
106+
self.args = _args.split()
105107
# Choose an analysis method (or fallback to apicalls)
106108
self.method = self.options.get("method", "apicalls")
107109
# Should our target be launched as root or not
@@ -202,11 +204,7 @@ def strace_analysis(self):
202204

203205
target_cmd = f"{self.target}"
204206
if "args" in kwargs:
205-
args = kwargs["args"]
206-
if not isinstance(args, str):
207-
args = " ".join(args)
208-
target_cmd += f" {args}"
209-
207+
target_cmd += f' {" ".join(kwargs["args"])}'
210208

211209
# eg: strace_args=-e trace=!recvfrom;epoll_pwait
212210
strace_args = self.options.get("strace_args", "").replace(";", ",")

data/yara/CAPE/Zloader.yar

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,34 @@ rule Zloader
1616
condition:
1717
uint16(0) == 0x5A4D and 1 of ($decrypt_conf*) and (1 of ($decrypt_key*) or $rc4_init)
1818
}
19+
20+
rule Zloader2024
21+
{
22+
meta:
23+
author = "enzok"
24+
description = "Zloader Payload"
25+
cape_type = "Zloader Payload"
26+
hash = "49405370a33abbf131c5d550cebe00780cc3fd3cbe888220686582ae88f16af7"
27+
strings:
28+
$conf_1 = {48 01 ?? 48 8D 15 [4] 41 B8 ?? 04 00 00 E8 [4] [0-5] C7 [1-2] 00 00 00 00}
29+
$confkey_1 = {48 8D 15 [4] 48 89 ?? 49 89 ?? E8 [4] [0-5] C7 [1-2] 00 00 00 00}
30+
$confkey_2 = {48 01 ?? 48 8D 15 [4] 41 B8 10 00 00 00 E8 [4] [0-5] C7 [1-2] 00 00 00 00 (48 8B|8B)}
31+
$confkey_3 = {48 01 ?? 48 8D 15 [4] 41 B8 10 00 00 00 E8 [4] [0-5] C7 [1-2] 00 00 00 00 48 83 C4}
32+
condition:
33+
uint16(0) == 0x5A4D and $conf_1 and 2 of ($confkey_*)
34+
}
35+
36+
rule Zloader2025
37+
{
38+
meta:
39+
author = "enzok"
40+
description = "Zloader Payload"
41+
cape_type = "Zloader Payload"
42+
hash = "86ffd411b42d8d06bdb294f48e79393adeea586c56c5c75c1a68ce6315932881"
43+
strings:
44+
$conf = {4? 01 ?? [4] E8 [4] 4? 8D 15 [4] 4? 89 ?? 4? 89 ?? E8 [4] C7 46 30 00 00 00 00 8B 7E 34}
45+
$confkey_1 = {4? 01 ?? [2] E8 [4] 4? 8D 15 [4] 4? 89 ?? 4? 89 ?? E8 [4] C7 46 34 00 00 00 00 8B 46 38}
46+
$confkey_2 = {4? 01 ?? [2] E8 [4] 4? 8D 15 [4] 4? 89 ?? 4? 89 ?? E8 [4] C7 46 38 00 00 00 00 48 83 C4 28}
47+
condition:
48+
uint16(0) == 0x5A4D and $conf and all of ($confkey_*)
49+
}

lib/cuckoo/common/integrations/file_extra_info.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from lib.cuckoo.common.integrations.parse_pe import HAVE_PEFILE, PortableExecutable
3333
from lib.cuckoo.common.integrations.parse_rdp import parse_rdp_file
3434
from lib.cuckoo.common.integrations.parse_wsf import WindowsScriptFile # EncodedScriptFile
35+
from lib.cuckoo.common.integrations.parse_msi import parse_msi
3536

3637
# from lib.cuckoo.common.integrations.parse_elf import ELF
3738
from lib.cuckoo.common.load_extra_modules import file_extra_info_load_modules
@@ -175,6 +176,9 @@ def static_file_info(
175176
):
176177
log.info("Missed dependencies: pip3 install oletools")
177178

179+
if "MSI Installer" in data_dictionary["type"]:
180+
data_dictionary["msi"] = parse_msi(file_path)
181+
178182
# ToDo we need type checking as it wont work for most of static jobs
179183
if HAVE_PEFILE and ("PE32" in data_dictionary["type"] or "MS-DOS executable" in data_dictionary["type"]):
180184
data_dictionary["pe"] = PortableExecutable(file_path).run(task_id)
@@ -679,6 +683,7 @@ def msi_extract(file: str, *, filetype: str, **kwargs) -> ExtractorReturnType:
679683
if "MSI Installer" not in filetype:
680684
return
681685

686+
# ToDo replace MsiExtract with pymsi
682687
extracted_files = []
683688
# sudo apt install msitools
684689
with extractor_ctx(file, "MsiExtract", prefix="msidump_", folder=tools_folder) as ctx:
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import logging
2+
from contextlib import suppress
3+
4+
HAVE_PYMSI = False
5+
with suppress(ImportError):
6+
import pymsi
7+
HAVE_PYMSI = True
8+
9+
log = logging.getLogger(__name__)
10+
11+
type_dll = 0x00000001 # msidbCustomActionTypeDll
12+
type_exe = 0x00000002 # msidbCustomActionTypeExe
13+
type_text_data = 0x00000003 # msidbCustomActionTypeTextData
14+
type_jscript = 0x00000005 # msidbCustomActionTypeJScript
15+
type_vbscript = 0x00000006 # msidbCustomActionTypeVBScript
16+
type_install = 0x00000007 # msidbCustomActionTypeInstall
17+
type_binary_data = 0x00000000 # msidbCustomActionTypeBinaryData
18+
type_source_file = 0x00000010 # msidbCustomActionTypeSourceFile
19+
type_directory = 0x00000020 # msidbCustomActionTypeDirectory
20+
type_property = 0x00000030 # msidbCustomActionTypeProperty
21+
type_continue = 0x00000040 # msidbCustomActionTypeContinue (Async/Sync ignore return)
22+
type_async = 0x00000080 # msidbCustomActionTypeAsync
23+
type_first_sequence = 0x00000100 # msidbCustomActionTypeFirstSequence
24+
type_once_per_process = 0x00000200 # msidbCustomActionTypeOncePerProcess
25+
type_client_repeat = 0x00000300 # msidbCustomActionTypeClientRepeat
26+
type_in_script = 0x00000400 # msidbCustomActionTypeInScript (Deferred)
27+
type_rollback = 0x00000100 # msidbCustomActionTypeRollback
28+
type_commit = 0x00000200 # msidbCustomActionTypeCommit
29+
type_no_impersonate = 0x00000800 # msidbCustomActionTypeNoImpersonate
30+
type_64bit_script = 0x00001000 # msidbCustomActionType64BitScript
31+
type_hide_target = 0x00002000 # msidbCustomActionTypeHideTarget
32+
type_ts_aware = 0x00004000 # msidbCustomActionTypeTSAware
33+
type_patch_uninstall = 0x00008000 # msidbCustomActionTypePatchUninstall
34+
35+
mask_basic_type = 0x7
36+
mask_source_type = 0x30
37+
mask_return_type = 0xC0
38+
mask_execution = 0xF00
39+
40+
# Mask of all known bits to calculate remainder / check for bogus data
41+
mask_all_known = (
42+
mask_basic_type |
43+
mask_source_type |
44+
mask_return_type |
45+
mask_execution |
46+
type_64bit_script |
47+
type_hide_target |
48+
type_ts_aware |
49+
type_patch_uninstall
50+
)
51+
52+
basic_type_map = {
53+
type_dll: "DLL (msidbCustomActionTypeDll)",
54+
type_exe: "EXE (msidbCustomActionTypeExe)",
55+
type_text_data: "Text Data (msidbCustomActionTypeTextData)",
56+
type_jscript: "JScript (msidbCustomActionTypeJScript)",
57+
type_vbscript: "VBScript (msidbCustomActionTypeVBScript)",
58+
type_install: "Install (msidbCustomActionTypeInstall)",
59+
0: "None/Error (0)",
60+
}
61+
62+
def parse_msi_action_type(input_int):
63+
"""
64+
Parses an MSI Custom Action Type integer into a dictionary of properties.
65+
"""
66+
# =========================================================================
67+
# MSI CONSTANTS (Based on C++ Header Definitions)
68+
# =========================================================================
69+
if not input_int:
70+
return {}
71+
72+
try:
73+
val = int(input_int)
74+
except ValueError:
75+
return {}
76+
77+
result = {
78+
"basic_type": "",
79+
"source": "",
80+
"return_processing": "",
81+
"execution": "",
82+
"flags": [],
83+
"remainder": 0
84+
}
85+
86+
# Basic Type
87+
b_type = val & mask_basic_type
88+
result["basic_type"] = basic_type_map.get(b_type, "Unknown Type (%d)" % b_type)
89+
90+
# Source Location
91+
s_loc = val & mask_source_type
92+
if s_loc == type_binary_data:
93+
result["source"] = "Binary Table (msidbCustomActionTypeBinaryData)"
94+
elif s_loc == type_source_file:
95+
result["source"] = "Source File (msidbCustomActionTypeSourceFile)"
96+
elif s_loc == type_directory:
97+
result["source"] = "Directory (msidbCustomActionTypeDirectory)"
98+
elif s_loc == type_property:
99+
result["source"] = "Property (msidbCustomActionTypeProperty)"
100+
101+
# Return Processing (Bits 6-7)
102+
r_type = val & mask_return_type
103+
if r_type == 0:
104+
# No specific constant exists for 0 (default), labeled as (None) for consistency with MSDN
105+
result["return_processing"] = "Synchronous, Check Return Code (None)"
106+
elif r_type == type_continue:
107+
result["return_processing"] = "Synchronous, Ignore Return Code (msidbCustomActionTypeContinue)"
108+
elif r_type == type_async:
109+
result["return_processing"] = "Asynchronous, Wait for Exit (msidbCustomActionTypeAsync)"
110+
elif r_type == (type_async | type_continue):
111+
result["return_processing"] = (
112+
"Asynchronous, Do Not Wait (msidbCustomActionTypeAsync | msidbCustomActionTypeContinue)")
113+
114+
# Execution Scheduling
115+
exec_val = val & mask_execution
116+
exec_parts = []
117+
is_deferred = (exec_val & type_in_script) == type_in_script
118+
if is_deferred:
119+
exec_parts.append("Deferred (msidbCustomActionTypeInScript)")
120+
121+
# In Deferred execution, bits 0x100 and 0x200 are independent (Rollback/Commit)
122+
if exec_val & type_rollback:
123+
exec_parts.append("Rollback (msidbCustomActionTypeRollback)")
124+
125+
if exec_val & type_commit:
126+
exec_parts.append("Commit (msidbCustomActionTypeCommit)")
127+
128+
else:
129+
# In Immediate execution, 0x300 is a specific combination (msidbCustomActionTypeClientRepeat)
130+
# https://learn.microsoft.com/en-us/windows/win32/msi/custom-action-execution-scheduling-options
131+
sched_bits = exec_val & (type_first_sequence | type_once_per_process)
132+
133+
if sched_bits == type_client_repeat:
134+
exec_parts.append("ClientRepeat (msidbCustomActionTypeClientRepeat)")
135+
else:
136+
# Handle individual bits if it's not the specific ClientRepeat combo
137+
if exec_val & type_first_sequence:
138+
exec_parts.append("FirstSequence (msidbCustomActionTypeFirstSequence)")
139+
140+
if exec_val & type_once_per_process:
141+
exec_parts.append("OncePerProcess (msidbCustomActionTypeOncePerProcess)")
142+
143+
# Check for NoImpersonate
144+
if exec_val & type_no_impersonate:
145+
exec_parts.append("NoImpersonate (msidbCustomActionTypeNoImpersonate)")
146+
147+
if not exec_parts:
148+
result["execution"] = "Immediate (User Context)"
149+
else:
150+
result["execution"] = " + ".join(exec_parts)
151+
152+
# Parse Global Flags
153+
if val & type_64bit_script:
154+
result["flags"].append("64-bit Script (msidbCustomActionType64BitScript)")
155+
if val & type_hide_target:
156+
result["flags"].append("Hide Target (msidbCustomActionTypeHideTarget)")
157+
if val & type_ts_aware:
158+
result["flags"].append("Terminal Server Aware (msidbCustomActionTypeTSAware)")
159+
if val & type_patch_uninstall:
160+
result["flags"].append("Patch Uninstall (msidbCustomActionTypePatchUninstall)")
161+
162+
# Check for bogus / malformed stuffs; remainder is 0 in tested samples
163+
result["remainder"] = val & ~mask_all_known
164+
165+
return result
166+
167+
def parse_msi(msi_path: str):
168+
msi = {}
169+
if not HAVE_PYMSI:
170+
return msi
171+
try:
172+
with pymsi.Package(msi_path) as package:
173+
if "CustomAction" in package.tables:
174+
current_table_obj = package.get("CustomAction")
175+
msi = {
176+
"rows": [row for row in current_table_obj.rows],
177+
"columns": [column.name for column in current_table_obj.columns],
178+
}
179+
for row in msi["rows"]:
180+
row["Enrich"] = parse_msi_action_type(row["Type"])
181+
except Exception as e:
182+
log.error("parse_msi: %s", e)
183+
return msi
184+
185+
186+
if __name__ == "__main__":
187+
import sys
188+
from pprint import pprint as pp
189+
pp(parse_msi(sys.argv[1]))
190+
# pymsi uses CamelCase for their dict keys, and my function does not. so if you want it to be clean feel free to make it CamelCase

lib/cuckoo/core/database.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"msix",
100100
"ps1",
101101
"msg",
102+
"nodejs",
102103
"eml",
103104
"js",
104105
"html",

0 commit comments

Comments
 (0)