-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
481 changed files
with
586 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import csv | ||
import logging | ||
import os | ||
import platform | ||
import shlex | ||
import subprocess | ||
from winreg import HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_DWORD, CreateKeyEx, SetValueEx | ||
|
||
from lib.common.abstracts import Auxiliary | ||
from lib.common.exceptions import CuckooPackageError | ||
from lib.common.results import upload_to_host | ||
from lib.core.config import Config | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
__author__ = "[Canadian Centre for Cyber Security] @CybercentreCanada" | ||
|
||
|
||
class Autoruns(Auxiliary): | ||
"""Autoruns from sysinternals""" | ||
|
||
def __init__(self, options, config): | ||
Auxiliary.__init__(self, options, config) | ||
self.config = Config(cfg="analysis.conf") | ||
self.enabled = self.config.autoruns | ||
self.output_dir = "C:\\\\autoruns" | ||
self.output_file_start = "autoruns_start.txt" | ||
self.output_file_end = "autoruns_end.txt" | ||
self.output_file_diff = "autoruns.diff" | ||
self.startupinfo = subprocess.STARTUPINFO() | ||
self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW | ||
|
||
if not os.path.exists(self.output_dir): | ||
os.makedirs(self.output_dir) | ||
# reg.exe ADD "HKCU\Software\Sysinternals\Autoruns" /v EulaAccepted /t REG_DWORD /d 1 /f | ||
try: | ||
with CreateKeyEx(HKEY_CURRENT_USER, "Software\Sysinternals\Autoruns", 0, KEY_ALL_ACCESS) as key: | ||
SetValueEx(key, "EulaAccepted", 0, REG_DWORD, 1) | ||
except OSError as e: | ||
pass | ||
|
||
bin_path = os.path.join(os.getcwd(), "bin") | ||
# First figure out what architecture the system in running (64 or 86) | ||
if "AMD64" in platform.uname(): | ||
autoruns = os.path.join(bin_path, "autorunsc64.exe") | ||
else: | ||
autoruns = os.path.join(bin_path, "autorunsc.exe") | ||
|
||
if not os.path.exists(autoruns): | ||
raise CuckooPackageError( | ||
"In order to use the Autorun functionality, it " | ||
"is required to have the appropriate Autorunsc executable in the analyzer/windows/bin/ path." | ||
) | ||
autoruns = autoruns.replace("\\", "\\\\") | ||
|
||
run_args = self.options.get("run_args") | ||
if not run_args: | ||
# -a : entry selection * for all entries | ||
# -c : Print output as csv | ||
# -o : Output file path | ||
run_args = f"-a * -c -o {self.output_dir}\\\\{self.output_file_end}" | ||
|
||
run_cmd = f"{autoruns} {run_args}" | ||
self.run_cmd = shlex.split(run_cmd) | ||
|
||
def start(self): | ||
|
||
log.debug(self.run_cmd) | ||
subprocess.Popen(self.run_cmd, startupinfo=self.startupinfo) | ||
|
||
def stop(self): | ||
log.debug(self.run_cmd) | ||
process = subprocess.Popen(self.run_cmd, startupinfo=self.startupinfo) | ||
process.wait() | ||
|
||
start_elements = [] | ||
end_elements = [] | ||
diff_elements = [] | ||
path_start = os.path.join(self.output_dir, self.output_file_start) | ||
path_end = os.path.join(self.output_dir, self.output_file_end) | ||
path_diff = os.path.join(self.output_dir, self.output_file_diff) | ||
|
||
with open(path_start, mode="r") as f: | ||
reader = csv.DictReader(f, delimiter=",") | ||
for row in reader: | ||
start_elements.append(row) | ||
with open(path_end, mode="r") as f: | ||
reader = csv.DictReader(f, delimiter=",") | ||
for row in reader: | ||
keys = row.keys() | ||
end_elements.append(row) | ||
|
||
for element in start_elements: | ||
if element not in end_elements: | ||
diff_elements.append(f"--,{element}") | ||
for element in end_elements: | ||
if element not in start_elements: | ||
diff_elements.append(f"++,{element}") | ||
|
||
with open(path_diff, mode="w") as f: | ||
if len(diff_elements) > 0: | ||
f.write(f"Operation,{keys}\n") | ||
f.writelines(f"{s}\n" for s in diff_elements) | ||
|
||
files_to_upload = set() | ||
|
||
if os.path.isfile(path_diff): | ||
try: | ||
if os.path.getsize(path_diff) > 0: | ||
files_to_upload.add(path_diff) | ||
else: | ||
log.debug("Diff file is empty") | ||
except Exception as e: | ||
log.debug("Diff file doesn't seem to exist") | ||
|
||
# Upload the autoruns diff file to the host. | ||
log.debug(files_to_upload) | ||
for f in files_to_upload: | ||
# Prepend file name with autoruns to indicate autoruns | ||
file_path_list = f.split("\\") | ||
file_name = file_path_list[-1] | ||
dumppath = os.path.join("autoruns", file_name) | ||
log.debug("Autoruns Aux Module is uploading %s" % f) | ||
upload_to_host(f, dumppath) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
rule EXE_in_LNK | ||
{ | ||
meta: | ||
description = "Identifies executable artefacts in shortcut (LNK) files." | ||
author = "@bartblaze" | ||
date = "2020-01" | ||
tlp = "White" | ||
strings: | ||
$ = ".exe" ascii wide nocase | ||
$ = ".dll" ascii wide nocase | ||
$ = ".scr" ascii wide nocase | ||
$ = ".pif" ascii wide nocase | ||
$ = "This program" ascii wide nocase | ||
$ = "TVqQAA" ascii wide nocase //MZ Base64 | ||
condition: | ||
isLNK and any of them | ||
} | ||
|
||
rule Long_RelativePath_LNK | ||
{ | ||
meta: | ||
description = "Identifies shortcut (LNK) file with a long relative path. Might be used in an attempt to hide the path." | ||
author = "@bartblaze" | ||
date = "2020-01" | ||
tlp = "White" | ||
strings: | ||
$ = "..\\..\\..\\..\\" ascii wide nocase | ||
condition: | ||
isLNK and any of them | ||
} | ||
|
||
rule MSOffice_in_LNK | ||
{ | ||
meta: | ||
description = "Identifies Microsoft Office artefacts in shortcut (LNK) files." | ||
author = "@bartblaze" | ||
date = "2020-01" | ||
tlp = "White" | ||
strings: | ||
$ = "winword" ascii wide nocase | ||
$ = "excel" ascii wide nocase | ||
$ = "powerpnt" ascii wide nocase | ||
$ = ".rtf" ascii wide nocase | ||
$ = ".doc" ascii wide nocase //.doc and .docx | ||
$ = ".dot" ascii wide nocase //.dot and .dotm | ||
$ = ".xls" ascii wide nocase //.xls and .xlsx | ||
$ = ".xla" ascii wide nocase | ||
$ = ".csv" ascii wide nocase | ||
$ = ".ppt" ascii wide nocase //.ppt and .pptx | ||
$ = ".pps" ascii wide nocase //.pps and .ppsx | ||
$ = ".xml" ascii wide nocase | ||
condition: | ||
isLNK and any of them | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import csv | ||
import logging | ||
import os | ||
|
||
from lib.cuckoo.common.abstracts import Processing | ||
from lib.cuckoo.common.exceptions import CuckooProcessingError | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
__author__ = "[Canadian Centre for Cyber Security] @CybercentreCanada" | ||
__version__ = "1.0.0" | ||
|
||
|
||
class Autoruns(Processing): | ||
def run(self): | ||
self.key = "autoruns" | ||
autoruns_dir = os.path.join(self.analysis_path, "autoruns") | ||
autoruns_data_path = os.path.join(autoruns_dir, "autoruns.diff") | ||
|
||
if os.path.exists(autoruns_data_path): | ||
autoruns_path = autoruns_data_path | ||
else: | ||
return | ||
|
||
data = {} | ||
try: | ||
with open(autoruns_path, "r") as f: | ||
# Operation,Time,Entry Location,Entry,Enabled,Category,Profile,Description,Company,Image Path,Version,Launch String | ||
reader = csv.DictReader(f, delimiter=",") | ||
count = 0 | ||
for row in reader: | ||
count += 1 | ||
data[str(count)] = str(row) | ||
|
||
if count == 0: | ||
data = None | ||
|
||
except Exception as e: | ||
raise CuckooProcessingError(f"Failed parsing {autoruns_path}: {e}") | ||
|
||
return data |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
from lib.cuckoo.common.abstracts import Signature | ||
|
||
|
||
class LinuxDeletesFiles(Signature): | ||
name = "deletes_files" | ||
description = "Deletes files from disk" | ||
os = "linux" | ||
severity = 3 | ||
categories = ["persistence", "stealth"] | ||
authors = ["winson0123"] | ||
minimum = "1.3" | ||
evented = True | ||
ttps = ["T1107"] # MITRE v6 | ||
ttps += ["T1070", "T1070.004"] # MITRE v7,8 | ||
mbcs = ["OB0006", "F0007"] | ||
mbcs += ["OC0001", "C0047"] # micro-behaviour | ||
|
||
filter_apinames = set( | ||
[ | ||
"truncate", | ||
"ftruncate", | ||
"open", | ||
"openat", | ||
"openat2", | ||
"unlink", | ||
"unlinkat", | ||
] | ||
) | ||
flags = "O_TRUNC" # truncating makes file empty, take as a form of deletion | ||
|
||
def __init__(self, *args, **kwargs): | ||
Signature.__init__(self, *args, **kwargs) | ||
self.loadctr = 0 | ||
|
||
def get_filename(self, call): | ||
"""Retrieves the filename of read-related API call. | ||
@param call: API call object. | ||
@return: value of the required argument. | ||
""" | ||
# Check if the call passed to it was cached already. | ||
# If not, we can start caching it and store a copy converted to a dict. | ||
if call is not self._current_call_cache: | ||
self._current_call_cache = call | ||
self._current_call_list = [argument["value"] for argument in call["arguments"]] | ||
|
||
# Return the filename from retrieved from the api call. | ||
if self._current_call_list: | ||
return self._current_call_list[0].split(" ")[1][1:-1] | ||
|
||
return None | ||
|
||
def on_call(self, call, process): | ||
if call["api"] in ["truncate", "ftruncate"] and call["return"] == "0": | ||
self.loadctr += 1 | ||
if call["api"] == "truncate": | ||
self.data.append({"DeletedFile": self.get_argument(call, "const char *path")}) | ||
if call["api"] == "ftruncate": | ||
self.data.append({"DeletedFile": self.get_filename(call)}) | ||
if call["api"] in ["unlink", "unlinkat"] and call["return"] == "0": | ||
self.loadctr += 1 | ||
self.data.append({"DeletedFile": self.get_argument(call, "const char *pathname")}) | ||
if call["api"] == "open" and call["return"] > "-1": | ||
if self.flags in self.get_argument(call, "int flags"): | ||
self.loadctr += 1 | ||
self.data.append({"DeletedFile": self.get_filename(call)}) | ||
if call["api"] == "openat" and call["return"] > "-1": | ||
if self.flags in self.get_argument(call, "int flags"): | ||
self.loadctr += 1 | ||
self.data.append({"DeletedFile": self.get_argument(call, "const char *filename")}) | ||
if call["api"] == "openat2" and call["return"] > "-1": | ||
if self.flags in self.get_argument(call, "struct open_how *how"): | ||
self.loadctr += 1 | ||
self.data.append({"DeletedFile": self.get_argument(call, "const char *filename")}) | ||
|
||
def on_complete(self): | ||
if self.loadctr > 0: | ||
return True |
Oops, something went wrong.