Skip to content

Commit e145063

Browse files
authored
Merge branch 'master' into staging
2 parents 269a449 + 4451402 commit e145063

File tree

14 files changed

+377
-107
lines changed

14 files changed

+377
-107
lines changed

changelog.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
### [11.02.2025]
2+
* `selfextract.conf` renamed to `integrations.conf`.
3+
* Please rename your config file.
4+
5+
### [10.02.2025]
6+
* We are now on `Poetry v2`. If you see next message, you need to upgrade your `poetry` version.
7+
* This one might be tricky as depends if your `poetry` was installed with `apt` or script. But something like this should works:
8+
* `curl -sSL https://install.python-poetry.org | POETRY_HOME=/etc/poetry python3 -`
9+
```
10+
The Poetry configuration is invalid:
11+
- Additional properties are not allowed ('requires-poetry' was unexpected)
12+
```
13+
* If you see missed `crispy_bootstrap4`. Just run `poetry install` as `cape` user.
14+
115
### [05.02.2025]
216
* Monitor update: Fix hooking deadlock with delay-loaded dlls & make LdrpCallInitRoutine hook transparent
317

@@ -524,7 +538,7 @@ rule X_cryptor {
524538
* You need to download version for your CPU and extract it to `data/NETReactorSlayer.CLI`
525539
* In case if you are on x64 host, then just run: `poetry run python utils/community.py -waf`
526540
* Add execution permission with `chmod a+x data/NETReactorSlayer.CLI`
527-
* Now each section inside of `selfextract.conf` has timeout value. Default is 60 seconds
541+
* Now each section inside of `integrations.conf` has timeout value. Default is 60 seconds
528542

529543
### [24.12.2022]
530544
* Monitor updates: Fix NtAllocateVirtualMemoryEx & NtMapViewOfSectionEx hooks and rebuild with Visual Studio 2022

conf/default/selfextract.conf.default renamed to conf/default/integrations.conf.default

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# This config is to be able to enable/disable things like MSI/NSIS/UnAutoIt etc
1+
# This config is to be able to enable/disable things like MSI/NSIS/UnAutoIt, 3rd part services integraitons, etc
22

33
[general]
44
pefiles = yes
@@ -14,6 +14,17 @@ hwp = yes
1414
# Number of workers for pool to run them in parallel
1515
max_workers = 6
1616

17+
[mandiant_intel]
18+
enabled = no
19+
api_access =
20+
api_secret =
21+
22+
# Create your apikey: https://threatfox.abuse.ch/api/#auth_key
23+
[threatfox]
24+
enabled = no
25+
apikey =
26+
27+
1728
# sudo apt install msitools
1829
[msi_extract]
1930
enabled = yes

lib/cuckoo/common/abstracts.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
HAVE_TLDEXTRACT = False
6464

6565
repconf = Config("reporting")
66+
integrations_conf = Config("integrations")
6667
_, categories_need_VM = load_categories()
6768

6869
mitre, HAVE_MITRE, _ = mitre_load(repconf.mitre.enabled)
@@ -1363,6 +1364,19 @@ def check_argument(self, pattern, name=None, api=None, category=None, process=No
13631364

13641365
return None
13651366

1367+
def check_threatfox(self, searchterm: str):
1368+
if not integrations_conf.threatfox.enabled or not integrations_conf.threatfox.apikey:
1369+
return
1370+
try:
1371+
response = requests.post(
1372+
"https://threatfox-api.abuse.ch/api/v1/",
1373+
data={"query": "search_ioc", "search_term": searchterm},
1374+
headers={"Auth-Key": integrations_conf.threatfox.apikey, "User-Agent": "CAPE Sandbox"},
1375+
)
1376+
return response.json()
1377+
except Exception as e:
1378+
log.error("ThreatFox error: %s", str(e))
1379+
13661380
def check_dnsbbl(self, domain: str):
13671381
"""
13681382
https://en.wikipedia.org/wiki/Domain_Name_System_blocklist

lib/cuckoo/common/demux.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def demux_sample(filename: bytes, package: str, options: str, use_sflock: bool =
290290
{
291291
os.path.basename(
292292
filename
293-
): "Detected password protected office file, but no sflock is installed or correct password provided"
293+
).decode(): "Detected password protected office file, but no sflock is installed or correct password provided"
294294
}
295295
)
296296

@@ -313,8 +313,9 @@ def demux_sample(filename: bytes, package: str, options: str, use_sflock: bool =
313313
else:
314314
error_list.append(
315315
{
316-
os.path.basename(filename),
317-
"File too big, enable 'allow_ignore_size' in web.conf or use 'ignore_size_check' option",
316+
os.path.basename(
317+
filename
318+
).decode(): "File too big, enable 'allow_ignore_size' in web.conf or use 'ignore_size_check' option",
318319
}
319320
)
320321
return retlist, error_list
@@ -349,8 +350,9 @@ def demux_sample(filename: bytes, package: str, options: str, use_sflock: bool =
349350
else:
350351
error_list.append(
351352
{
352-
os.path.basename(filename),
353-
"File too big, enable 'allow_ignore_size' in web.conf or use 'ignore_size_check' option",
353+
os.path.basename(
354+
filename
355+
).decode(): "File too big, enable 'allow_ignore_size' in web.conf or use 'ignore_size_check' option",
354356
}
355357
)
356358
new_retlist.append((filename, platform))

lib/cuckoo/common/integrations/file_extra_info.py

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565

6666
cfg = Config()
6767
processing_conf = Config("processing")
68-
selfextract_conf = Config("selfextract")
68+
integration_conf = Config("integrations")
6969

7070
try:
7171
from modules.signatures.recon_checkip import dns_indicators
@@ -108,9 +108,9 @@
108108
HAVE_BAT_DECODER = False
109109
print("OPTIONAL! Missed dependency: poetry run pip install -U git+https://github.com/DissectMalware/batch_deobfuscator")
110110

111-
unautoit_binary = os.path.join(CUCKOO_ROOT, selfextract_conf.UnAutoIt_extract.binary)
112-
innoextact_binary = os.path.join(CUCKOO_ROOT, selfextract_conf.Inno_extract.binary)
113-
sevenzip_binary = os.path.join(CUCKOO_ROOT, selfextract_conf.SevenZip_unpack.binary)
111+
unautoit_binary = os.path.join(CUCKOO_ROOT, integration_conf.UnAutoIt_extract.binary)
112+
innoextact_binary = os.path.join(CUCKOO_ROOT, integration_conf.Inno_extract.binary)
113+
sevenzip_binary = os.path.join(CUCKOO_ROOT, integration_conf.SevenZip_unpack.binary)
114114
if not path_exists(sevenzip_binary):
115115
sevenzip_binary = "/usr/bin/7z"
116116

@@ -135,6 +135,12 @@
135135

136136
HAVE_VIRUSTOTAL = True
137137

138+
HAVE_MANDIANT_INTEL = False
139+
if integration_conf.mandiant_intel.enabled:
140+
from lib.cuckoo.common.integrations.mandiant_intel import mandiant_lookup
141+
142+
HAVE_MANDIANT_INTEL = True
143+
138144
exclude_startswith = ("parti_",)
139145
excluded_extensions = (".parti",)
140146
tools_folder = os.path.join(cfg.cuckoo.get("tmppath", "/tmp"), "cape-external")
@@ -180,40 +186,40 @@ def static_file_info(
180186
data_dictionary["floss"] = floss_strings
181187

182188
if "Mono" in data_dictionary["type"]:
183-
if selfextract_conf.general.dotnet:
189+
if integration_conf.general.dotnet:
184190
data_dictionary["dotnet"] = DotNETExecutable(file_path).run()
185191
if processing_conf.strings.dotnet:
186192
dotnet_strings = dotnet_user_strings(file_path)
187193
if dotnet_strings:
188194
data_dictionary.setdefault("dotnet_strings", dotnet_strings)
189195

190-
elif HAVE_OLETOOLS and package in {"doc", "ppt", "xls", "pub"} and selfextract_conf.general.office:
196+
elif HAVE_OLETOOLS and package in {"doc", "ppt", "xls", "pub"} and integration_conf.general.office:
191197
# options is dict where we need to get pass get_options
192198
data_dictionary["office"] = Office(file_path, task_id, data_dictionary["sha256"], options_dict).run()
193-
elif ("PDF" in data_dictionary["type"] or file_path.endswith(".pdf")) and selfextract_conf.general.pdf:
199+
elif ("PDF" in data_dictionary["type"] or file_path.endswith(".pdf")) and integration_conf.general.pdf:
194200
data_dictionary["pdf"] = PDF(file_path).run()
195201
elif (
196202
package in {"wsf", "hta"} or data_dictionary["type"] == "XML document text" or file_path.endswith(".wsf")
197-
) and selfextract_conf.general.windows_script:
203+
) and integration_conf.general.windows_script:
198204
data_dictionary["wsf"] = WindowsScriptFile(file_path).run()
199205
# elif package in {"js", "vbs"}:
200206
# data_dictionary["js"] = EncodedScriptFile(file_path).run()
201-
elif (package == "lnk" or "MS Windows shortcut" in data_dictionary["type"]) and selfextract_conf.general.lnk:
207+
elif (package == "lnk" or "MS Windows shortcut" in data_dictionary["type"]) and integration_conf.general.lnk:
202208
data_dictionary["lnk"] = LnkShortcut(file_path).run()
203-
elif ("Java Jar" in data_dictionary["type"] or file_path.endswith(".jar")) and selfextract_conf.general.java:
204-
if selfextract_conf.procyon.binary and not path_exists(selfextract_conf.procyon.binary):
209+
elif ("Java Jar" in data_dictionary["type"] or file_path.endswith(".jar")) and integration_conf.general.java:
210+
if integration_conf.procyon.binary and not path_exists(integration_conf.procyon.binary):
205211
log.error("procyon_path specified in processing.conf but the file does not exist")
206212
else:
207-
data_dictionary["java"] = Java(file_path, selfextract_conf.procyon.binary).run()
213+
data_dictionary["java"] = Java(file_path, integration_conf.procyon.binary).run()
208214
elif file_path.endswith(".rdp") or data_dictionary.get("name", {}).endswith(".rdp"):
209215
data_dictionary["rdp"] = parse_rdp_file(file_path)
210216
# It's possible to fool libmagic into thinking our 2007+ file is a zip.
211217
# So until we have static analysis for zip files, we can use oleid to fail us out silently,
212218
# yeilding no static analysis results for actual zip files.
213-
# elif ("ELF" in data_dictionary["type"] or file_path.endswith(".elf")) and selfextract_conf.general.elf:
219+
# elif ("ELF" in data_dictionary["type"] or file_path.endswith(".elf")) and integration_conf.general.elf:
214220
# data_dictionary["elf"] = ELF(file_path).run()
215221
# data_dictionary["keys"] = f.get_keys()
216-
# elif HAVE_OLETOOLS and package == "hwp" and selfextract_conf.general.hwp:
222+
# elif HAVE_OLETOOLS and package == "hwp" and integration_conf.general.hwp:
217223
# data_dictionary["hwp"] = HwpDocument(file_path).run()
218224

219225
data = path_read_file(file_path)
@@ -251,6 +257,11 @@ def static_file_info(
251257
if vt_details:
252258
data_dictionary["virustotal"] = vt_details
253259

260+
if HAVE_MANDIANT_INTEL and processing_conf.mandiant_intel.enabled:
261+
mandiant_intel_details = mandiant_lookup("file", file_path, results)
262+
if mandiant_intel_details:
263+
data_dictionary["mandiant_intel"] = mandiant_intel_details
264+
254265
generic_file_extractors(
255266
file_path,
256267
destination_folder,
@@ -456,7 +467,7 @@ def generic_file_extractors(
456467
]
457468

458469
futures = {}
459-
with pebble.ProcessPool(max_workers=int(selfextract_conf.general.max_workers)) as pool:
470+
with pebble.ProcessPool(max_workers=int(integration_conf.general.max_workers)) as pool:
460471
# Prefer custom modules over the built-in ones, since only 1 is allowed
461472
# to be the extracted_files_tool.
462473
if extra_info_modules:
@@ -468,12 +479,12 @@ def generic_file_extractors(
468479
for extraction_func in file_info_funcs:
469480
funcname = extraction_func.__name__.split(".")[-1]
470481
if (
471-
not getattr(selfextract_conf, funcname, {}).get("enabled", False)
482+
not getattr(integration_conf, funcname, {}).get("enabled", False)
472483
and getattr(extraction_func, "enabled", False) is False
473484
):
474485
continue
475486

476-
func_timeout = int(getattr(selfextract_conf, funcname, {}).get("timeout", 60))
487+
func_timeout = int(getattr(integration_conf, funcname, {}).get("timeout", 60))
477488
futures[funcname] = pool.schedule(extraction_func, args=args, kwargs=kwargs, timeout=func_timeout)
478489
pool.join()
479490

@@ -591,7 +602,7 @@ def eziriz_deobfuscate(file: str, *, data_dictionary: dict, **_) -> ExtractorRet
591602
if all(".NET Reactor" not in string for string in data_dictionary.get("die", [])):
592603
return
593604

594-
binary = shlex.split(selfextract_conf.eziriz_deobfuscate.binary.strip())[0]
605+
binary = shlex.split(integration_conf.eziriz_deobfuscate.binary.strip())[0]
595606
binary = os.path.join(CUCKOO_ROOT, binary)
596607
if not binary:
597608
log.warning("eziriz_deobfuscate.binary is not defined in the configuration.")
@@ -614,7 +625,7 @@ def eziriz_deobfuscate(file: str, *, data_dictionary: dict, **_) -> ExtractorRet
614625
_ = run_tool(
615626
[
616627
os.path.join(CUCKOO_ROOT, binary),
617-
*shlex.split(selfextract_conf.eziriz_deobfuscate.extra_args.strip()),
628+
*shlex.split(integration_conf.eziriz_deobfuscate.extra_args.strip()),
618629
file,
619630
],
620631
universal_newlines=True,
@@ -633,7 +644,7 @@ def de4dot_deobfuscate(file: str, *, filetype: str, **_) -> ExtractorReturnType:
633644
if "Mono" not in filetype:
634645
return
635646

636-
binary = shlex.split(selfextract_conf.de4dot_deobfuscate.binary.strip())[0]
647+
binary = shlex.split(integration_conf.de4dot_deobfuscate.binary.strip())[0]
637648
if not binary:
638649
log.warning("de4dot_deobfuscate.binary is not defined in the configuration.")
639650
return
@@ -647,7 +658,7 @@ def de4dot_deobfuscate(file: str, *, filetype: str, **_) -> ExtractorReturnType:
647658
_ = run_tool(
648659
[
649660
binary,
650-
*shlex.split(selfextract_conf.de4dot_deobfuscate.extra_args.strip()),
661+
*shlex.split(integration_conf.de4dot_deobfuscate.extra_args.strip()),
651662
"-f",
652663
file,
653664
"-o",
@@ -676,7 +687,7 @@ def msi_extract(file: str, *, filetype: str, **kwargs) -> ExtractorReturnType:
676687
if not kwargs.get("tests"):
677688
# msiextract in different way that 7z, we need to add subfolder support
678689
output = run_tool(
679-
[selfextract_conf.msi_extract.binary, file, "--directory", tempdir],
690+
[integration_conf.msi_extract.binary, file, "--directory", tempdir],
680691
universal_newlines=True,
681692
stderr=subprocess.PIPE,
682693
)

0 commit comments

Comments
 (0)