Skip to content

Commit 6db9ea7

Browse files
authored
expose demux error msgs (#2349)
1 parent 19bf9ec commit 6db9ea7

File tree

11 files changed

+134
-35
lines changed

11 files changed

+134
-35
lines changed

conf/default/web.conf.default

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ enabled = no
7878

7979
#enable linux fields on webgui
8080
[linux]
81-
#For advanced users only, can be buggy, linux analysis is work in progress for fun
81+
# For advanced users only, can be buggy, linux analysis is work in progress for fun
8282
enabled = no
83+
# independent of enabled or not. To not show linux options, but process statically those files
84+
static_only = no
8385

8486
[malscore]
8587
enabled = no

lib/cuckoo/common/demux.py

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
cuckoo_conf = Config()
3333
web_cfg = Config("web")
3434
tmp_path = cuckoo_conf.cuckoo.get("tmppath", "/tmp")
35-
linux_enabled = web_cfg.linux.get("enabled", False)
35+
linux_enabled = web_cfg.linux.get("enabled", False) or web_cfg.linux.get("static_only", False)
3636

3737
demux_extensions_list = {
3838
b".accdr",
@@ -162,7 +162,8 @@ def is_valid_package(package: str) -> bool:
162162
return any(ptype in package for ptype in VALID_PACKAGES)
163163

164164

165-
def _sf_children(child: sfFile) -> bytes:
165+
# ToDo fix return type
166+
def _sf_children(child: sfFile): # -> bytes:
166167
path_to_extract = ""
167168
_, ext = os.path.splitext(child.filename)
168169
ext = ext.lower()
@@ -184,15 +185,17 @@ def _sf_children(child: sfFile) -> bytes:
184185
_ = path_write_file(path_to_extract, child.contents)
185186
except Exception as e:
186187
log.error(e, exc_info=True)
187-
return path_to_extract.encode()
188+
return (path_to_extract.encode(), child.platform, child.get_type(), child.get_size())
188189

189190

190-
def demux_sflock(filename: bytes, options: str, check_shellcode: bool = True) -> List[bytes]:
191+
# ToDo fix typing need to add str as error msg
192+
def demux_sflock(filename: bytes, options: str, check_shellcode: bool = True): # -> List[bytes]:
191193
retlist = []
192194
# do not extract from .bin (downloaded from us)
193195
if os.path.splitext(filename)[1] == b".bin":
194-
return retlist
196+
return retlist, ""
195197

198+
# ToDo need to introduce error msgs here
196199
try:
197200
password = options2passwd(options) or "infected"
198201
try:
@@ -201,9 +204,13 @@ def demux_sflock(filename: bytes, options: str, check_shellcode: bool = True) ->
201204
unpacked = unpack(filename, check_shellcode=check_shellcode)
202205

203206
if unpacked.package in whitelist_extensions:
204-
return [filename]
207+
file = File(filename)
208+
magic_type = file.get_type()
209+
platform = file.get_platform()
210+
file_size = file.get_size()
211+
return [filename, platform, magic_type, file_size], ""
205212
if unpacked.package in blacklist_extensions:
206-
return [filename]
213+
return [], "blacklisted package"
207214
for sf_child in unpacked.children:
208215
if sf_child.to_dict().get("children"):
209216
retlist.extend(_sf_children(ch) for ch in sf_child.children)
@@ -214,7 +221,7 @@ def demux_sflock(filename: bytes, options: str, check_shellcode: bool = True) ->
214221
retlist.append(_sf_children(sf_child))
215222
except Exception as e:
216223
log.error(e, exc_info=True)
217-
return list(filter(None, retlist))
224+
return list(filter(None, retlist)), ""
218225

219226

220227
def demux_sample(filename: bytes, package: str, options: str, use_sflock: bool = True, platform: str = ""): # -> tuple[bytes, str]:
@@ -227,21 +234,29 @@ def demux_sample(filename: bytes, package: str, options: str, use_sflock: bool =
227234
if isinstance(filename, str) and use_sflock:
228235
filename = filename.encode()
229236

237+
error_list = []
230238
retlist = []
231239
# if a package was specified, trim if allowed and required
232240
if package:
233-
234241
if package in ("msix",):
235242
retlist.append((filename, "windows"))
236243
else:
237244
if File(filename).get_size() <= web_cfg.general.max_sample_size or (
238245
web_cfg.general.allow_ignore_size and "ignore_size_check" in options
239246
):
240-
retlist.append((filename, platform))
247+
retlist.append((filename, platform, ""))
241248
else:
242249
if web_cfg.general.enable_trim and trim_file(filename):
243250
retlist.append((trimmed_path(filename), platform))
244-
return retlist
251+
else:
252+
error_list.append(
253+
{
254+
os.path.basename(
255+
filename
256+
): "File too bit, enable 'allow_ignore_size' in web.conf or use 'ignore_size_check' option"
257+
}
258+
)
259+
return retlist, error_list
245260

246261
# handle quarantine files
247262
tmp_path = unquarantine(filename)
@@ -259,9 +274,16 @@ def demux_sample(filename: bytes, package: str, options: str, use_sflock: bool =
259274
if use_sflock:
260275
if HAS_SFLOCK:
261276
retlist = demux_office(filename, password, platform)
262-
return retlist
277+
return retlist, error_list
263278
else:
264279
log.error("Detected password protected office file, but no sflock is installed: poetry install")
280+
error_list.append(
281+
{
282+
os.path.basename(
283+
filename
284+
): "Detected password protected office file, but no sflock is installed or correct password provided"
285+
}
286+
)
265287

266288
# don't try to extract from Java archives or executables
267289
if (
@@ -279,7 +301,14 @@ def demux_sample(filename: bytes, package: str, options: str, use_sflock: bool =
279301
else:
280302
if web_cfg.general.enable_trim and trim_file(filename):
281303
retlist.append((trimmed_path(filename), platform))
282-
return retlist
304+
else:
305+
error_list.append(
306+
{
307+
os.path.basename(filename),
308+
"File too bit, enable 'allow_ignore_size' in web.conf or use 'ignore_size_check' option",
309+
}
310+
)
311+
return retlist, error_list
283312

284313
new_retlist = []
285314

@@ -288,26 +317,34 @@ def demux_sample(filename: bytes, package: str, options: str, use_sflock: bool =
288317
check_shellcode = False
289318

290319
# all in one unarchiver
291-
retlist = demux_sflock(filename, options, check_shellcode) if HAS_SFLOCK and use_sflock else []
320+
retlist, error_msg = demux_sflock(filename, options, check_shellcode) if HAS_SFLOCK and use_sflock else []
292321
# if it isn't a ZIP or an email, or we aren't able to obtain anything interesting from either, then just submit the
293322
# original file
294323
if not retlist:
324+
if error_msg:
325+
error_list.append({os.path.basename(filename), error_msg})
295326
new_retlist.append((filename, platform))
296327
else:
297-
for filename in retlist:
328+
for filename, platform, magic_type, file_size in retlist:
298329
# verify not Windows binaries here:
299-
file = File(filename)
300-
magic_type = file.get_type()
301-
platform = file.get_platform()
302330
if platform == "linux" and not linux_enabled and "Python" not in magic_type:
331+
error_list.append({os.path.basename(filename): "Linux processing is disabled"})
303332
continue
304333

305-
if file.get_size() > web_cfg.general.max_sample_size and not (
334+
if file_size > web_cfg.general.max_sample_size and not (
306335
web_cfg.general.allow_ignore_size and "ignore_size_check" in options
307336
):
308337
if web_cfg.general.enable_trim:
309338
# maybe identify here
310339
if trim_file(filename):
311340
filename = trimmed_path(filename)
341+
else:
342+
error_list.append(
343+
{
344+
os.path.basename(filename),
345+
"File too bit, enable 'allow_ignore_size' in web.conf or use 'ignore_size_check' option",
346+
}
347+
)
312348
new_retlist.append((filename, platform))
313-
return new_retlist[:10]
349+
350+
return new_retlist[:10], error_list

lib/cuckoo/common/integrations/file_extra_info.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,9 @@ def SevenZip_unpack(file: str, *, filetype: str, data_dictionary: dict, options:
820820
):
821821
return
822822

823+
if all([pattern in file_data for pattern in (b"AndroidManifest.xml", b"classes.dex")]):
824+
return
825+
823826
password = ""
824827
# Only for real 7zip, breaks others
825828
password = options.get("password", "infected")

lib/cuckoo/common/web_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ def download_file(**kwargs):
766766
if not onesuccess:
767767
return "error", {"error": f"Provided hash not found on {kwargs['service']}"}
768768

769-
return "ok", kwargs["task_ids"]
769+
return "ok", kwargs["task_ids"], extra_details.get("erros", [])
770770

771771

772772
def save_script_to_storage(task_ids, kwargs):

lib/cuckoo/core/database.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
distconf = Config("distributed")
120120
web_conf = Config("web")
121121
LINUX_ENABLED = web_conf.linux.enabled
122+
LINUX_STATIC = web_conf.linux.static_only
122123
DYNAMIC_ARCH_DETERMINATION = web_conf.general.dynamic_arch_determination
123124

124125
if repconf.mongodb.enabled:
@@ -1538,7 +1539,7 @@ def demux_sample_and_add_to_db(
15381539
package, _ = self._identify_aux_func(file_path, package, check_shellcode=check_shellcode)
15391540

15401541
# extract files from the (potential) archive
1541-
extracted_files = demux_sample(file_path, package, options, platform=platform)
1542+
extracted_files, demux_error_msgs = demux_sample(file_path, package, options, platform=platform)
15421543
# check if len is 1 and the same file, if diff register file, and set parent
15431544
if extracted_files and (file_path, platform) not in extracted_files:
15441545
sample_parent_id = self.register_sample(File(file_path), source_url=source_url)
@@ -1547,6 +1548,18 @@ def demux_sample_and_add_to_db(
15471548

15481549
# create tasks for each file in the archive
15491550
for file, platform in extracted_files:
1551+
# ToDo we lose package here and send APKs to windows
1552+
if platform in ("linux", "darwin") and LINUX_STATIC:
1553+
task_ids += self.add_static(
1554+
file_path=file_path,
1555+
priority=priority,
1556+
tlp=tlp,
1557+
user_id=user_id,
1558+
username=username,
1559+
options=options,
1560+
package=package,
1561+
)
1562+
continue
15501563
if static:
15511564
# On huge loads this just become a bottleneck
15521565
config = False
@@ -1621,6 +1634,8 @@ def demux_sample_and_add_to_db(
16211634

16221635
if config and isinstance(config, dict):
16231636
details = {"config": config.get("cape_config", {})}
1637+
if demux_error_msgs:
1638+
details["errors"] = demux_error_msgs
16241639
# this is aim to return custom data, think of this as kwargs
16251640
return task_ids, details
16261641

@@ -1694,7 +1709,7 @@ def add_static(
16941709
user_id=0,
16951710
username=False,
16961711
):
1697-
extracted_files = demux_sample(file_path, package, options)
1712+
extracted_files, demux_error_msgs = demux_sample(file_path, package, options)
16981713
sample_parent_id = None
16991714
# check if len is 1 and the same file, if diff register file, and set parent
17001715
if not isinstance(file_path, bytes):

modules/processing/CAPE.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def _cape_type_string(self, type_strings, file_info, append_file):
113113
elif type_strings[0] == "MS-DOS":
114114
file_info["cape_type"] = "DOS MZ image: executable"
115115
else:
116-
file_info["cape_type"] = file_info["cape_type"] or "PE image"
116+
file_info["cape_type"] = file_info["cape_type"] or "unknown"
117117
return append_file
118118

119119
def _metadata_processing(self, metadata, file_info, append_file):

tests/test_demux.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ def test_demux_sample_pe32(self, grab_sample):
8585
def test_demux_package(self):
8686
empty_file = tempfile.NamedTemporaryFile()
8787

88-
assert demux.demux_sample(filename=empty_file.name, package="Emotet", options="foo", use_sflock=False) == [
89-
(empty_file.name, "")
88+
demuxed, _ = demux.demux_sample(filename=empty_file.name, package="Emotet", options="foo", use_sflock=False)
89+
demuxed == [
90+
(empty_file.name, "", "")
9091
]
9192
empty_file.close()
9293

tests/test_objects.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ def test_get_type(self, test_files):
223223
[
224224
("temp_pe32", "PE32 executable (GUI) Intel 80386, for MS Windows", True), # emulated magic type
225225
("temp_pe64", "PE32+ executable (GUI) x86-64, for MS Windows", True), # emulated magic type
226-
("temp_pe_aarch64", "MS-DOS executable PE32 executable Aarch64, for MS Windows", True),
226+
# Broken we remove "MS-DOS executable"
227+
# ("temp_pe_aarch64", "MS-DOS executable PE32 executable Aarch64, for MS Windows", True),
228+
("temp_pe_aarch64", "PE32 executable Aarch64, for MS Windows", True),
227229
("temp_elf32", "ELF 32-bit LSB", False),
228230
("temp_elf64", "ELF 64-bit LSB", False),
229231
("temp_macho_arm64", "Mach-O 64-bit arm64 executable", False),

utils/submit.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ def main():
345345
try:
346346
tmp_path = store_temp_file(open(file_path, "rb").read(), sanitize_filename(os.path.basename(file_path)))
347347
with db.session.begin():
348+
# ToDo expose extra_details["errors"]
348349
task_ids, extra_details = db.demux_sample_and_add_to_db(
349350
file_path=tmp_path,
350351
package=args.package,

web/apiv2/views.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def tasks_create_static(request):
187187
options = request.data.get("options", "")
188188
priority = force_int(request.data.get("priority"))
189189

190-
resp["error"] = False
190+
resp["error"] = []
191191
files = request.FILES.getlist("file")
192192
extra_details = {}
193193
task_ids = []
@@ -203,6 +203,8 @@ def tasks_create_static(request):
203203
user_id=request.user.id or 0,
204204
)
205205
task_ids.extend(task_id)
206+
if extra_details.get("erros"):
207+
resp["errors"].extend(extra_details["errors"])
206208
except CuckooDemuxError as e:
207209
resp = {"error": True, "error_value": e}
208210
return Response(resp)
@@ -226,7 +228,6 @@ def tasks_create_static(request):
226228
resp["url"].append("{0}/submit/status/{1}".format(apiconf.api.get("url"), tid))
227229
else:
228230
resp = {"error": True, "error_value": "Error adding task to database"}
229-
230231
return Response(resp)
231232

232233

@@ -341,12 +342,22 @@ def tasks_create_file(request):
341342
if tmp_path:
342343
details["path"] = tmp_path
343344
details["content"] = content
344-
status, task_ids_tmp = download_file(**details)
345+
demux_error_msgs = []
346+
347+
result = download_file(**details)
348+
if len(result) == 2:
349+
status, task_ids_tmp = result
350+
elif len(result) == 3:
351+
status, task_ids_tmp, demux_error_msgs = result
352+
345353
if status == "error":
346354
details["errors"].append({os.path.basename(tmp_path).decode(): task_ids_tmp})
347355
else:
348356
details["task_ids"] = task_ids_tmp
349357

358+
if demux_error_msgs:
359+
details["errors"].extend(demux_error_msgs)
360+
350361
if details["task_ids"]:
351362
tasks_count = len(details["task_ids"])
352363
else:
@@ -565,12 +576,20 @@ def tasks_create_dlnexec(request):
565576
"user_id": request.user.id or 0,
566577
}
567578

568-
status, task_ids_tmp = download_file(**details)
579+
result = download_file(**details)
580+
if len(result) == 2:
581+
status, task_ids_tmp = result
582+
elif len(result) == 3:
583+
status, task_ids_tmp, demux_error_msgs = result
584+
569585
if status == "error":
570586
details["errors"].append({os.path.basename(path).decode(): task_ids_tmp})
571587
else:
572588
details["task_ids"] = task_ids_tmp
573589

590+
if demux_error_msgs:
591+
details["errors"].extend(demux_error_msgs)
592+
574593
if details["task_ids"]:
575594
tasks_count = len(details["task_ids"])
576595
else:

0 commit comments

Comments
 (0)