Skip to content

Commit

Permalink
Merge branch 'master' into staging
Browse files Browse the repository at this point in the history
  • Loading branch information
doomedraven committed Nov 1, 2024
2 parents 3003c89 + bdbc19d commit abdac65
Show file tree
Hide file tree
Showing 206 changed files with 5,528 additions and 2,597 deletions.
4 changes: 2 additions & 2 deletions .github/actions/python-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ runs:
- name: Install poetry
shell: bash
run: pip install poetry
run: PIP_BREAK_SYSTEM_PACKAGES=1 pip install poetry poetry-plugin-export

- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v5
Expand All @@ -27,4 +27,4 @@ runs:
- name: Install requirements
shell: bash
run: |
poetry install --no-interaction --no-root
PIP_BREAK_SYSTEM_PACKAGES=1 poetry install --no-interaction --no-root
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:

- name: Install pyattck
run: |
poetry run pip install pyattck==7.1.2
poetry run pip install pyattck==7.1.2 maco
- name: Run Ruff
run: poetry run ruff . --line-length 132 --ignore E501,E402
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Malware can be classified in CAPE via three mechanisms:

![image](https://github.com/kevoreilly/CAPEv2/assets/22219888/a44f2f8a-10df-47cc-9690-5ef08f04ea6b)

Parsing can be done using CAPE's own framework, alternatively the following frameworks are supported: [RATDecoders](https://github.com/kevthehermit/RATDecoders), [DC3-MWCP](https://github.com/Defense-Cyber-Crime-Center/DC3-MWCP) or [MalDuck](https://github.com/CERT-Polska/malduck/tree/master/malduck/)
Parsing can be done using CAPE's own framework, alternatively the following frameworks are supported: [RATDecoders](https://github.com/kevthehermit/RATDecoders), [DC3-MWCP](https://github.com/Defense-Cyber-Crime-Center/DC3-MWCP), [MalDuck](https://github.com/CERT-Polska/malduck/tree/master/malduck/), or [MaCo](https://github.com/CybercentreCanada/maco)

#### Special note about config parsing frameworks:
* Due to the nature of malware, since it changes constantly when any new version is released, something might become broken!
Expand Down Expand Up @@ -162,10 +162,10 @@ A huge thank you to @D00m3dR4v3n for single-handedly porting CAPE to Python 3.
* Replace `<username>` with a real pattern.
* You need to replace all `<WOOT>` inside!
* Read it! You must understand what it does! It has configuration in header of the script.
* `sudo ./kvm-qemu.sh all <username> | tee kvm-qemu.log`
* `sudo ./kvm-qemu.sh all <username> 2>&1 | tee kvm-qemu.log`
4. To install CAPE itself, [cape2.sh](https://github.com/kevoreilly/CAPEv2/blob/master/installer/cape2.sh) with all optimizations
* Read and understand what it does! This is not a silver bullet for all your problems! It has configuration in header of the script.
* `sudo ./cape2.sh base | tee cape.log`
* `sudo ./cape2.sh base 2>&1 | tee cape.log`
5. After installing everything save both installation logs as gold!
6. Configure CAPE by doing mods to config files inside `conf` folder.
7. Restart all CAPE services to pick config changes and run CAPE properly!
Expand Down Expand Up @@ -228,5 +228,3 @@ If you use CAPEv2 in your work, please cite it as specified in the "Cite this re

### Docs
* [ReadTheDocs](https://capev2.readthedocs.io/en/latest/#)


21 changes: 19 additions & 2 deletions analyzer/windows/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,11 +503,27 @@ def run(self):
mod_name = name.split(".")[-1]
if mod_name in windows_modules:
mod_name += "_windows"
# if hasattr(self.config, mod_name) and getattr(self.config, mod_name, False):
# log.debug('Imported auxiliary module "%s"', name)
if hasattr(self.config, mod_name) and getattr(self.config, mod_name, False):
__import__(name, globals(), locals(), ["dummy"])
log.debug('Imported auxiliary module "%s"', name)
except ImportError as e:
log.warning('Unable to import the auxiliary module "%s": %s', name, e)

def configure_aux_from_data(instance):
# Do auxiliary module configuration stored in 'data/auxiliary/<package_name>'
_class = type(instance)
try:
log.debug("attempting to configure '%s' from data", _class.__name__)
instance.configure_from_data()
except ModuleNotFoundError:
# let it go, not every module is configurable from data
log.debug("module %s does not support data configuration, ignoring", _class.__name__)
except ImportError as iexc:
# let it go but emit a warning; assume a dependency is missing
log.warning("configuration error for module %s: %s", _class.__name__, iexc)
except Exception as exc:
log.error("error configuring module %s: %s", _class.__name__, exc)

# Walk through the available auxiliary modules.
aux_modules = []

Expand All @@ -516,6 +532,7 @@ def run(self):
aux = module(self.options, self.config)
log.debug('Initialized auxiliary module "%s"', module.__name__)
aux_modules.append(aux)
configure_aux_from_data(aux)
log.debug('Trying to start auxiliary module "%s"...', module.__module__)
aux.start()
except (NotImplementedError, AttributeError) as e:
Expand Down
9 changes: 5 additions & 4 deletions analyzer/windows/data/yara/Formbook.yar
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ rule FormhookB
meta:
author = "kevoreilly"
description = "Formbook Anti-hook Bypass"
cape_options = "clear,bp0=$decode,action0=scan,hc0=1,bp1=$remap_ntdll+6,action1=setdst:ntdll,count=0,force-sleepskip=1"
cape_options = "clear,bp0=$entry,action0=scan,hc0=1,bp1=$new_remap+6,action1=setdst:ntdll,count=0,force-sleepskip=1"
packed = "08c5f44d57f5ccc285596b3d9921bf7fbbbf7f9a827bb3285a800e4c9faf6731"
strings:
$decode = {55 8B EC 83 EC 24 53 56 57 [480-520] 8B E5 5D C3}
$remap_ntdll = {90 90 90 90 90 90 8B (86 [2] 00 00|46 ??|06) 5F 5E 5B 8B E5 5D C3}
$remap_ntdll = {33 96 [2] 00 00 8D 86 [2] 00 00 68 F0 00 00 00 50 89 [2-5] E8 [4-10] 6A 00 6A 0? 8D 4D ?? 51 6A}
$entry = {55 8B EC 83 EC ?4 53 56 57 [480-520] 8B E5 5D C3}
$new_remap = {90 90 90 90 90 90 8B (86 [2] 00 00|46 ??|06) 5F 5E 5B 8B E5 5D C3}
condition:
any of them
2 of them
}

rule FormconfA
Expand Down
41 changes: 41 additions & 0 deletions analyzer/windows/data/yara/NitrogenLoader.yar
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
rule LoaderSyscall
{
meta:
author = "enzok"
description = "Loader Syscall"
cape_options = "sysbp=$syscall*-2,count=0"
strings:
$makehashes = {48 89 4C 24 ?? 48 89 54 24 ?? 4? 89 44 24 ?? 4? 89 4C 24 ?? 4? 83 EC ?? B? [4] E8 [3] 00}
$number = {49 89 C3 B? [4] E8 [3] 00}
$syscall = {48 83 C4 ?? 4? 8B 4C 24 ?? 4? 8B 54 24 ?? 4? 8B 44 24 ?? 4? 8B 4C 24 ?? 4? 89 CA 4? FF E3}
condition:
all of them
}

rule NitrogenLoaderAES
{
meta:
author = "enzok"
description = "NitrogenLoader AES and IV"
cape_options = "bp0=$keyiv0+8,action0=dump:ecx::64,hc0=1,bp1=$keyiv0*-4,action1=dump:ecx::32,hc1=1,count=0"
strings:
$keyiv0 = {48 8B 8C 24 [4] E8 [3] 00 4? 89 84 24 [4] 4? 8B 84 24 [4] 4? 89 84 24 [4] 4? 8B 8C 24 [4] E8 [3] 00}
$keyiv1 = {48 89 84 24 [4] 4? 8B 84 24 [4] 4? 8B 94 24 [4] 4? 8D 8C 24 [4] E8 [3] FF}
$keyiv2 = {48 63 84 24 [4] 4? 8B C0 4? 8B 94 24 [4] 4? 8D 8C 24 [4] E8 [3] FF 4? 8B 84 24}
condition:
all of them
}

rule NitrogenLoaderBypass
{
meta:
author = "enzok"
description = "Nitrogen Loader Exit Bypass"
cape_options = "bp2=$exit-2,action2=jmp,count=0"
strings:
$string1 = "LoadResource"
$syscall = {48 83 C4 ?? 4? 8B 4C 24 ?? 4? 8B 54 24 ?? 4? 8B 44 24 ?? 4? 8B 4C 24 ?? 4? 89 CA 4? FF E3}
$exit = {33 C9 E8 [4] E8 [4] 48 8D 84 24 [4] 48 89 44 24 ?? 4? B? E4 00 00 00 4? 8B 05 [4] B? 03 00 00 00 48 8D}
condition:
all of them
}
12 changes: 12 additions & 0 deletions analyzer/windows/data/yara/PrivateLoader.yar
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
rule PrivateLoader
{
meta:
author = "kevoreilly"
description = "PrivateLoader indirect syscall capture"
cape_options = "clear,sysbp=$syscall*-2"
packed = "075d0dafd7b794fbabaf53d38895cfd7cffed4a3fe093b0fc7853f3b3ce642a4"
strings:
$syscall = {48 31 C0 4C 8B 19 8B 41 10 48 8B 49 08 49 89 CA 41 FF E3}
condition:
any of them
}
Binary file modified analyzer/windows/dll/capemon.dll
Binary file not shown.
Binary file modified analyzer/windows/dll/capemon_x64.dll
Binary file not shown.
26 changes: 26 additions & 0 deletions analyzer/windows/lib/common/abstracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,29 @@ def add_pid(self, pid):

def del_pid(self, pid):
pass

def configure_from_data(self):
"""Do private auxiliary module-specific configuration.
Auxiliary modules can implement this method to perform pre-analysis
configuration based on runtime data contained in "data/auxiliary/<package_name>".
This method raises:
- ImportError when any exception occurs during import
- AttributeError if the module configure function is invalid
- ModuleNotFoundError if the module does not support configuration from data
"""
package_module_name = self.__class__.__module__.split(".")[-1]
module_name = f"data.auxiliary.{package_module_name}"
try:
mod = importlib.import_module(module_name)
except ModuleNotFoundError as exc:
raise exc
except Exception as exc:
raise ImportError(f"error importing {module_name}: {exc}") from exc

spec = inspect.getfullargspec(mod.configure)
if len(spec.args) != 1:
err_msg = f"{module_name}.configure: expected 1 arguments, got {len(spec.args)}"
raise AttributeError(err_msg)
mod.configure(self)
17 changes: 5 additions & 12 deletions analyzer/windows/modules/auxiliary/browsermonitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ def __init__(self, options=None, config=None):
self.startupinfo = subprocess.STARTUPINFO()
self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
self.browser_logfile = ""
self.last_modification = 0.0
self._is_first_save = True

def _find_browser_extension(self):
temp_dir = tempfile.gettempdir()
while not self.browser_logfile and self.do_run:
temp_dir_list = os.listdir(temp_dir)
for directory in temp_dir_list:
# TOR Browser saves directly to %temp%
if directory.startswith("bext_") and directory.endswith(".json"):
log.debug(f"Found extension logs: {self.browser_logfile}")
self.browser_logfile = os.path.join(temp_dir, directory)
break
tmp_directory_path = os.path.join(temp_dir, directory)
if not os.path.isdir(tmp_directory_path):
continue
Expand All @@ -49,22 +52,12 @@ def _find_browser_extension(self):
time.sleep(1)

def _collect_browser_logs(self):
if not self._is_first_save and self.last_modification != os.path.getmtime(self.browser_logfile):
return
self.last_modification = os.path.getmtime(self.browser_logfile)
upload_to_host(self.browser_logfile, "browser/requests.log")
self._is_first_save = False

def run(self):
self.do_run = True
if self.enabled:
self._find_browser_extension()
self.last_modification = os.path.getmtime(self.browser_logfile)
while self.do_run:
self._collect_browser_logs()
time.sleep(1)
return True
return False

def stop(self):
if self.enabled:
Expand Down
6 changes: 4 additions & 2 deletions analyzer/windows/modules/auxiliary/disguise.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,11 @@ def randomizeUUID(self):
SetValueEx(key, "MachineGuid", 0, REG_SZ, createdUUID)

def add_persistent_route(self):
self.run_as_system(["C:\\Windows\\System32\ROUTE.exe", "-p", "add", "0.0.0.0", "mask", "0.0.0.0", PERSISTENT_ROUTE_GATEWAY])
self.run_as_system(
["C:\\Windows\\System32\ROUTE.exe", "-p", "change", "0.0.0.0", "mask", "0.0.0.0", PERSISTENT_ROUTE_GATEWAY]
["C:\\Windows\\System32\\ROUTE.exe", "-p", "add", "0.0.0.0", "mask", "0.0.0.0", PERSISTENT_ROUTE_GATEWAY]
)
self.run_as_system(
["C:\\Windows\\System32\\ROUTE.exe", "-p", "change", "0.0.0.0", "mask", "0.0.0.0", PERSISTENT_ROUTE_GATEWAY]
)

def start(self):
Expand Down
2 changes: 2 additions & 0 deletions analyzer/windows/modules/auxiliary/human.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"don't send",
"don't save",
"continue",
"connect",
"unzip",
"open",
"close the program",
Expand Down Expand Up @@ -115,6 +116,7 @@
DONT_CLICK_BUTTONS = (
# english
"check online for a solution",
"don't ask me again for remote connections from this publisher",
"don't run",
"do not ask again until the next update is available",
"cancel",
Expand Down
48 changes: 0 additions & 48 deletions analyzer/windows/modules/packages/Shellcode-Unpacker.py

This file was deleted.

42 changes: 0 additions & 42 deletions analyzer/windows/modules/packages/Unpacker.py

This file was deleted.

Loading

0 comments on commit abdac65

Please sign in to comment.