From ea2e75d169acaea678fedf0ab8a11f1983e34116 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Wed, 18 Oct 2017 13:02:52 -0500 Subject: [PATCH] Windows - update svchost check to svchost across volumes --- .../plugins/overlays/windows/windows.py | 95 ++++++++++++++++--- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/volatility/plugins/overlays/windows/windows.py b/volatility/plugins/overlays/windows/windows.py index a460ba45..55366817 100644 --- a/volatility/plugins/overlays/windows/windows.py +++ b/volatility/plugins/overlays/windows/windows.py @@ -18,7 +18,7 @@ # along with Volatility. If not, see . # -import datetime, struct, calendar +import datetime, struct, calendar, re import volatility.plugins.overlays.basic as basic import volatility.plugins.kpcrscan as kpcr import volatility.plugins.kdbgscan as kdbg @@ -376,6 +376,12 @@ class DosDate(obj.NativeType): class _EPROCESS(obj.CType, ExecutiveObjectMixin): """ An extensive _EPROCESS with bells and whistles """ + def __init__(self, *args, **kwargs): + self.is_svchost_sec = -1 + + obj.CType.__init__(self, *args, **kwargs) + ExecutiveObjectMixin.__init__(self) + @property def Peb(self): """ Returns a _PEB object which is using the process address space. @@ -537,6 +543,70 @@ class _EPROCESS(obj.CType, ExecutiveObjectMixin): yield offset + hit offset += min(to_read, constants.SCAN_BLOCKSIZE) + def _write_exec_checks(self, vad, protect): + ret = False + + # This is a typical VirtualAlloc'd injection + if vad.VadFlags.PrivateMemory == 1 and vad.Tag == "VadS": + ret = True + + # This is a stuxnet-style injection + elif (vad.VadFlags.PrivateMemory == 0 and + protect != "PAGE_EXECUTE_WRITECOPY"): + ret = True + + return ret + + def _check_svchost_sec(self): + self.is_svchost_sec = 0 + + regex = re.compile(r"\\Device\\HarddiskVolume\d\\Windows\\System32\\svchost.exe") + + exe_address = self.SectionBaseAddress.v() + + for vad in self.VadRoot.traverse(): + if vad.Start == exe_address: + control_area = vad.ControlArea + if control_area: + file_object = vad.FileObject + if file_object: + file_name = str(file_object.file_name_with_device()) + + # check for real service host process + if regex.match(file_name): + token = self.get_token() + + # check for the 'WinDefend' sid + for sid_string in token.get_sids(): + if sid_string == "S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736": + self.is_svchost_sec = 1 + break + + break + + def _mz_checks(self, vad, pas, write, exe): + ret = False + + if pas.zread(vad.Start, 2) == "MZ": + # R/W/-X - looking for DLLs being read into memory from another source, + # such as an encrypted DLL in a EXE's resource section + # This also finds .net executables that are loaded through memory-only APIs + if not exe and vad.VadFlags.PrivateMemory == 1: + # we skip the security svchost (Windows Defender) + # as it will map MANY dlls into its address space and cause false positives + # the check looks for the full path (from the kernel) as well as the associated SID + if self.is_svchost_sec == -1: + self._check_svchost_sec() + + if self.is_svchost_sec == 0: + ret = True + + # +X - find PE files in executable regions that are not file backed + elif exe and not self._mapped_file_filter(vad): + ret = True + + return ret + def _injection_filter(self, vad): """ This is a callback that's executed by get_vads() @@ -557,22 +627,19 @@ class _EPROCESS(obj.CType, ExecutiveObjectMixin): contain injected code. """ protect = vadinfo.PROTECT_FLAGS.get(vad.VadFlags.Protection.v(), "") - write_exec = "EXECUTE" in protect and "WRITE" in protect + exe = "EXECUTE" in protect + write = "WRITE" in protect + + ret = False - # The Write/Execute check applies to everything - if not write_exec: - return False + if write and exe: + ret = self._write_exec_checks(vad, protect) - # This is a typical VirtualAlloc'd injection - if vad.VadFlags.PrivateMemory == 1 and vad.Tag == "VadS": - return True + if ret == False: + pas = self.get_process_address_space() + ret = self._mz_checks(vad, pas, write, exe) - # This is a stuxnet-style injection - if (vad.VadFlags.PrivateMemory == 0 and - protect != "PAGE_EXECUTE_WRITECOPY"): - return True - - return False + return ret def _mapped_file_filter(self, vad): """