diff --git a/README.md b/README.md
index 234fe24..a3159dd 100644
--- a/README.md
+++ b/README.md
@@ -4,23 +4,6 @@ This repository contains various non-core plugins for [MemProcFS - The Memory Pr
Plugins range from non-core plugins to plugins that have offensive capabilities - such as _pypykatz_. Please find a short description for each plugin below:
-## pypykatz
-
-#### Author:
-Tamas Jos ([@skelsec](https://twitter.com/SkelSec)) , info@skelsec.com , https://github.com/skelsec/
-
-#### Overview:
-_pypykatz_ for MemProcFS exposes mimikatz functionality in the folder `/py/secrets/` in the file system root provided that the target is a supported Windows system. Functionality includes retrieval of hashes, passwords, kerberos tickets and various other credentials.
-

-
-#### Installation instructions:
-1) Ensure MemProcFS supported version of 64-bit Python for Windows is on the system path (or specify in `-pythonpath` option when starting MemProcFS). NB! embedded Python will not work with _pypykatz_ since it requires access to Python pip installed packages.
-2) Install _pypykatz_ pip package, in correct python environment, by running `pip install dissect.cstruct pypykatz`.
-3) Copy the _pypykatz_ for _MemProcFS_ plugin by copying all files from [`/files/plugins/pym_pypykatz`](https://github.com/ufrisk/MemProcFS-plugins/tree/master/files/plugins/pym_pypykatz) to corresponding folder in MemProcFS - overwriting any existing files there.
-4) Start MemProcFS.
-
-#### Last updated: 2021-03-21
-
## pypykatz regsecrets
#### Author:
diff --git a/files/plugins/pym_pypykatz/__init__.py b/files/plugins/pym_pypykatz/__init__.py
deleted file mode 100644
index 47e4c46..0000000
--- a/files/plugins/pym_pypykatz/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from plugins.pym_pypykatz.pym_pypykatz import (
- Initialize,
- Notify,
-)
-
-__all__ = [
- "Initialize",
- "Notify",
-]
\ No newline at end of file
diff --git a/files/plugins/pym_pypykatz/pym_pypykatz.py b/files/plugins/pym_pypykatz/pym_pypykatz.py
deleted file mode 100644
index df82296..0000000
--- a/files/plugins/pym_pypykatz/pym_pypykatz.py
+++ /dev/null
@@ -1,296 +0,0 @@
-# pym_pypykatz.py
-#
-# Pypykatz plugin for MemProcFss
-#
-# https://github.com/skelsec/
-#
-# (c) Tamas Jos, 2019
-# Author: Tamas Jos (@skelsec), info@skelsec.com
-#
-
-import memprocfs
-from vmmpyplugin import *
-import json
-import traceback
-import datetime
-
-# globals needed for FS
-all_secrets = '' #all secrets extracted from lsass in json format
-luids = {} #secrets per-luid (logon session) in txt format
-domains = {}
-kerberos = {}
-
-first_run = True
-
-import_failed = None
-parsing_failed = None
-
-import_error_text_template = """
-The imports for pypykatz plugin have failed at some point.
-Common causes:
- 1. You dont have pypykatz installed
- 2. Python runtime environment used by MemProcFs is not the same as you have installed pypykatz in.
- 3. You are not using the correct python version
-
-Error traceback:
-%s
-"""
-
-parsing_error_template = """
-pypykatz plugin tried to parse the lsass.exe process in your memory dump but failed.
-This could be caused by multiple things:
- 1. The pypykatz's parser code is potato
- 2. MemProcFs could not fully parse the memory, usually this happens with incorrect memory dump files.
- Check for error strings like "Could not load segment data"
-
-In case you are cretain the problem is caused by the parser,
-please submit an issue with the info below this line:
-%s
-
-%s
-"""
-
-import_error_text = None
-parsing_error_text = None
-
-try:
- from pypykatz.pypykatz import pypykatz
- from pypykatz.commons.common import UniversalEncoder
- from plugins.pym_pypykatz.pypyreader import MemProcFsReader
-
- #this needs to be the last line!
- import_failed = False
-
-
-except Exception as e:
- import_failed = True
- if VmmPyPlugin_fPrintV:
- traceback.print_exc()
- import_error_text = import_error_text_template % traceback.format_exc()
- pass
-
-class KerberosInfo:
- def __init__(self, type, domain, user, data):
- self.type = type.name
- self.domain = domain
- self.user = user
- self.data = data
-
- def get_filename_base(self):
- return '%s_%s_%s' % (self.type, self.domain, self.user)
-
-def process_lsass():
- """
- Processing lsass.exe with pypykatz, storing the results in the globals
-
- """
- global all_secrets
- global luids
- global domains
- global kerberos
- global parsing_error_text
- global parsing_failed
-
- basic_info = ''
- try:
- memreader = MemProcFsReader()
-
- basic_info = '===== BASIC INFO. SUBMIT THIS IF THERE IS AN ISSUE =====\r\n'
- basic_info += 'CPU arch: %s\r\n' % memreader.sysinfo.architecture.name
- basic_info += 'OS: %s\r\n' % memreader.sysinfo.operating_system
- basic_info += 'BuildNumber: %s\r\n' % memreader.sysinfo.buildnumber
- basic_info += 'MajorVersion: %s\r\n' % memreader.sysinfo.major_version
- basic_info += 'MSV timestamp: %s\r\n' % memreader.sysinfo.msv_dll_timestamp
-
-
- mimi = pypykatz(memreader, memreader.sysinfo)
- mimi.start()
-
- all_secrets = json.dumps(mimi, cls = UniversalEncoder, indent=4, sort_keys=True)
- for luid in mimi.logon_sessions:
- luids[str(luid)] = str(mimi.logon_sessions[luid])
- for kc in mimi.logon_sessions[luid].kerberos_creds:
- for ticket in kc.tickets:
- if str(luid) not in kerberos:
- kerberos[str(luid)] = []
-
- ki = KerberosInfo(ticket.type, ticket.DomainName, '.'.join(ticket.EClientName), ticket.to_asn1().dump())
- kerberos[str(luid)].append(ki)
- domain = mimi.logon_sessions[luid].domainname
- user = mimi.logon_sessions[luid].username
-
- if domain == '':
- domain = 'local'
-
- if user == '':
- user = 'empty'
-
- if domain not in domains:
- domains[domain] = {}
- if user not in domains[domain]:
- domains[domain][user] = {}
-
- domains[domain][user][str(luid)] = str(mimi.logon_sessions[luid])
-
- parsing_failed = False
-
- except Exception as e:
- parsing_failed = True
- if VmmPyPlugin_fPrintV:
- traceback.print_exc()
- parsing_error_text = parsing_error_template % (basic_info, traceback.format_exc())
- pass
-
-
-def ReadAllResults(pid, file_path, file_name, file_attr, bytes_length, bytes_offset):
- """
- reads the all_results data as file on the virtual FS
- """
-
- return all_secrets[bytes_offset:bytes_offset+bytes_length].encode()
-
-def ReadLuid(pid, file_path, file_name, file_attr, bytes_length, bytes_offset):
- """
- reads the secrets for a specific luid data as file on the virtual FS
- """
- try:
-
- luid = file_name.rsplit('.', 1)[0]
- if luid.find('_') != -1:
- luid = luid.split('_')[1]
- return luids[luid].encode()[bytes_offset:bytes_offset+bytes_length]
-
- except Exception as e:
- if VmmPyPlugin_fPrintV:
- traceback.print_exc()
- return None
-
-def ReadKerberos(pid, file_path, file_name, file_attr, bytes_length, bytes_offset):
- try:
-
- t = file_name.rsplit('.', 1)[0]
- t, luid, pos = t.rsplit('_', 2)
- data = kerberos[luid][int(pos)].data
-
- return data[bytes_offset:bytes_offset+bytes_length]
-
- except Exception as e:
- if VmmPyPlugin_fPrintV:
- traceback.print_exc()
- return None
-
-def ReadErrors(pid, file_path, file_name, file_attr, bytes_length, bytes_offset):
- try:
-
- if file_name == 'import_error.txt':
- return import_error_text.encode()[bytes_offset:bytes_offset+bytes_length]
- if file_name == 'parsing_error.txt':
- return parsing_error_text.encode()[bytes_offset:bytes_offset+bytes_length]
-
- except Exception as e:
- if VmmPyPlugin_fPrintV:
- traceback.print_exc()
- return None
-
-def List(pid, path):
- #
- # List function - this module employs a dynamic list function - which makes
- # it responsible for providing directory listings of its contents in a
- # highly optimized way. It is very important that the List function is as
- # speedy as possible - to avoid locking up the file system.
- #
- # First check the directory to be listed. Only the module root directory is
- # allowed. If it's not the module root directory return None.
- global first_run
- try:
-
- if path[:7] != 'secrets':
- return None
-
- if first_run == True:
- process_lsass()
- first_run = False
-
- if import_failed == True:
- print(import_failed)
- result = {
- 'import_error.txt': {'size': len(import_error_text), 'read': ReadErrors, 'write': None},
- }
- return result
-
- if parsing_failed == True:
- result = {
- 'parsing_error.txt': {'size': len(parsing_error_text), 'read': ReadErrors, 'write': None},
- }
- return result
-
-
- if path == 'secrets':
- result = {
- 'all_results.json': {'size': len(all_secrets), 'read': ReadAllResults, 'write': None},
- }
- result['by_luid'] = {'dirs' : True}
- result['by_domain'] = {'dirs' : True}
- result['kerberos'] = {'dirs' : True}
- return result
-
- if path == 'secrets/by_luid':
- result = {}
- for luid in luids:
- result['%s.txt' % luid] = {'size': len(luids[luid]), 'read': ReadLuid, 'write': None}
- return result
-
- if path == 'secrets/by_domain':
- result = {}
- for domain in domains:
- result[domain] = {'dirs' : True}
- return result
-
- if path.find('secrets/by_domain/') == 0:
- result = {}
- domain = path.rsplit('/',1)[1]
-
- if domain not in domains and domain.lower() == 'system32':
- #this is a special case for an unknown behaviour coming from windows itself.
- #for some reason it requests /System32 and /System32/System32 for reasons beyond science...
- return None
-
- for user in domains[domain]:
- for luid in domains[domain][user]:
- result['%s_%s.txt' % (user, luid)] = {'size': len(luids[luid]), 'read': ReadLuid, 'write': None}
- return result
-
- if path == 'secrets/kerberos':
- result = {}
- for luid in kerberos:
- result[luid] = {'dirs' : True}
- return result
-
- if path.find('secrets/kerberos/') == 0:
-
- luid = path.rsplit('/',1)[1]
- result = {}
- for i, ki in enumerate(kerberos[luid]):
- result['%s_%s_%s.kirbi' % (ki.get_filename_base(), luid, i)] = {'size': len(ki.data), 'read': ReadKerberos, 'write': None}
-
- return result
-
- except Exception as e:
- if VmmPyPlugin_fPrintV:
- traceback.print_exc()
- return None
-
-
-def Notify(fEvent, bytesData):
- if fEvent == memprocfs.PLUGIN_EVENT_TOTALREFRESH and not import_failed and not parsing_failed:
- global first_run
- first_run = True
-
-
-def Initialize(target_system, target_memorymodel):
- # Check that the operating system is 32-bit or 64-bit Windows. If it's not
- # then raise an exception to terminate loading of this module.
- if target_system != memprocfs.SYSTEM_WINDOWS_X64 and target_system != memprocfs.SYSTEM_WINDOWS_X86:
- raise RuntimeError("Only Windows is supported by the pym_pypykatz module.")
- VmmPyPlugin_FileRegisterDirectory(None, 'secrets', List)
-
\ No newline at end of file
diff --git a/files/plugins/pym_pypykatz/pypyreader.py b/files/plugins/pym_pypykatz/pypyreader.py
deleted file mode 100644
index a441397..0000000
--- a/files/plugins/pym_pypykatz/pypyreader.py
+++ /dev/null
@@ -1,283 +0,0 @@
-# pypyreader.py
-#
-# Pypykatz reader plugin for MemProcFss
-#
-# https://github.com/skelsec/
-#
-# (c) Tamas Jos, 2019
-# Author: Tamas Jos (@skelsec), info@skelsec.com
-#
-
-from pypykatz.commons.common import KatzSystemArchitecture, KatzSystemInfo
-from .sysinfo_helpers import *
-
-import memprocfs
-from vmmpyplugin import *
-import copy
-
-class Module:
- def __init__(self):
- self.name = None
- self.baseaddress = None
- self.size = None
- self.endaddress = None
- self.pages = []
-
- self.versioninfo = None
- self.checksum = None
- self.timestamp = None
-
- def inrange(self, addr):
- return self.baseaddress <= addr < self.endaddress
-
- def parse(data, timestamp = None):
- m = Module()
- m.name = data.name
- m.baseaddress = data.base
- m.size = data.image_size
- m.endaddress = m.baseaddress + m.size
-
- m.timestamp = timestamp
-
- return m
-
- def __str__(self):
- return '%s %s %s %s %s' % (self.name, hex(self.baseaddress), hex(self.size), hex(self.endaddress), self.timestamp )
-
-class Page:
- def __init__(self):
- self.BaseAddress = None
- self.AllocationBase = None
- self.AllocationProtect = None
- self.RegionSize = None
- self.EndAddress = None
- self.data = None
-
- @staticmethod
- def parse(page_info, module):
- p = Page()
- p.BaseAddress = page_info['VirtualAddress'] + module.baseaddress
- p.AllocationBase = None #page_info['VirtualAddress'] ???? TODO
- p.AllocationProtect = None #page_info['VirtualAddress'] ???? TODO
- p.RegionSize = min(page_info['SizeOfRawData'], 100*1024*1024) # TODO: need this currently to stop infinite search
- p.EndAddress = p.BaseAddress + p.RegionSize
- return p
-
- @staticmethod
- def parse_raw(page_info):
- p = Page()
- p.BaseAddress = page_info['va']
- p.AllocationBase = None #page_info['VirtualAddress'] ???? TODO
- p.AllocationProtect = None #page_info['VirtualAddress'] ???? TODO
- p.RegionSize = min(page_info['size'], 100*1024*1024) # TODO: need this currently to stop infinite search
- p.EndAddress = p.BaseAddress + p.RegionSize
- return p
-
- def read_data(self, pid):
- self.data = vmm.process(pid).memory.read(self.BaseAddress, self.RegionSize)
-
- def inrange(self, addr):
- return self.BaseAddress <= addr < self.EndAddress
-
- def search(self, pattern, pid):
- if len(pattern) > self.RegionSize:
- return []
- data = vmm.process(pid).memory.read(self.BaseAddress, self.RegionSize)
- fl = []
- offset = 0
- while len(data) > len(pattern):
- marker = data.find(pattern)
- if marker == -1:
- return fl
- fl.append(marker + offset + self.BaseAddress)
- data = data[marker+1:]
- offset = marker + 1
-
- return fl
-
- def __str__(self):
- return '0x%08x 0x%08x %s 0x%08x' % (self.BaseAddress, self.AllocationBase, self.AllocationProtect, self.RegionSize)
-
-
-class MemProcFsReader:
- def __init__(self, process_name = 'lsass.exe', filename = None):
- self.filename = filename
- self.process_name = process_name
- self.sysinfo = None
- self.process = None
- self.current_position = None
- self.modules = []
-
-
- self.setup()
-
- def get_sysinfo(self):
- self.sysinfo = KatzSystemInfo()
-
- #print('[+] Getting BuildNumer')
- self.sysinfo.buildnumber = vmm.get_config(memprocfs.OPT_WIN_VERSION_BUILD)
- #print('[+] Found BuildNumber %s' % self.sysinfo.buildnumber)
-
- #print('[+] Getting msv_dll_timestamp')
- self.sysinfo.msv_dll_timestamp = int(PEGetFileTime(self.process, self.process_name))
- #print('[+] Found msv_dll_timestamp %s' % self.sysinfo.msv_dll_timestamp)
-
- #print('[+] Getting arch')
- val = vmm.get_config(memprocfs.OPT_CORE_SYSTEM)
- if val == memprocfs.SYSTEM_WINDOWS_X64:
- self.sysinfo.architecture = KatzSystemArchitecture.X64
- else:
- self.sysinfo.architecture = KatzSystemArchitecture.X86
-
- #print('[+] Got arch %s' % self.sysinfo.architecture)
-
-
- def setup(self):
-
- if self.filename:
- # if filename is specified we dont want to use the virtual FS, but then we need to init the vmmpy module
- VmmPy_Initialize(["-device", self.filename,'-vv'])
-
- #print('[+] Searching LSASS')
- self.process = vmm.process(self.process_name)
- #print('[+] Found LSASS on PID %s' % self.process.pid)
-
- self.get_sysinfo()
-
- #print('[+] Getting modules info')
- for module in self.process.module_list():
- #print('moduleinfo: %s' % str(moduleinfo))
- m = Module.parse(module)
- try:
- for pageinfo in module.maps.sections():
- #print('pageinfo: %s' % str(pageinfo))
- m.pages.append(Page.parse(pageinfo, m))
-
- self.modules.append(m)
- except:
- #module is paged out, hoping that it's not a module that is needed
- pass
-
- #print('[+] Got modules info')
-
-
- def get_module_by_name(self, module_name):
- for mod in self.modules:
- if mod.name.lower().find(module_name.lower()) != -1:
- return mod
- return None
-
- def find_in_module(self, module_name, pattern):
- mod = self.get_module_by_name(module_name)
- if mod is None:
- raise Exception('Could not find module! %s' % module_name)
- t = []
- for page in mod.pages:
- t += self.find(page.BaseAddress, page.EndAddress, pattern)
- return t
-
- @staticmethod
- def find_all_pattern(data, pattern):
- substring_length = len(pattern)
- def recurse(locations_found, start):
- location = data.find(pattern, start)
- if location != -1:
- return recurse(locations_found + [location], location+substring_length)
- else:
- return locations_found
-
- return recurse([], 0)
-
- def find(self, start, end, pattern):
- """
- Searches for all occurrences of a pattern in the current memory segment, returns all occurrences as a list
- """
- data = self.process.memory.read(start, end - start)
- pos = []
- for p in MemProcFsReader.find_all_pattern(data, pattern):
- pos.append( p + start)
- return pos
-
- def seek(self, offset, whence = 0):
- """
- Changes the current address to an offset of offset.
- """
- self.current_position += offset
- return
-
- def move(self, address):
- """
- Moves the buffer to a virtual address specified by address
- """
- self.current_position = address
- return
-
- def align(self, alignment = None):
- """
- Repositions the current reader to match architecture alignment
- """
- if alignment is None:
- if self.sysinfo.architecture == KatzSystemArchitecture.X64:
- alignment = 8
- else:
- alignment = 4
- offset = self.current_position % alignment
- if offset == 0:
- return
- offset_to_aligned = (alignment - offset) % alignment
- self.seek(offset_to_aligned, 1)
- return
-
- def tell(self):
- """
- Returns the current virtual address
- """
- return self.current_position
-
- def peek(self, length):
- t = self.current_position
- data = self.read(length)
- self.current_position = t
-
- return data
-
- def read(self, size = -1):
- data = self.process.memory.read(self.current_position, size)
- self.current_position += size
- return data
-
- def read_int(self):
- """
- Reads an integer. The size depends on the architecture.
- Reads a 4 byte small-endian singed int on 32 bit arch
- Reads an 8 byte small-endian singed int on 64 bit arch
- """
- if self.sysinfo.architecture == KatzSystemArchitecture.X64:
- return int.from_bytes(self.read(8), byteorder = 'little', signed = True)
- else:
- return int.from_bytes(self.read(4), byteorder = 'little', signed = True)
-
- def read_uint(self):
- """
- Reads an integer. The size depends on the architecture.
- Reads a 4 byte small-endian unsinged int on 32 bit arch
- Reads an 8 byte small-endian unsinged int on 64 bit arch
- """
- if self.sysinfo.architecture == KatzSystemArchitecture.X64:
- return int.from_bytes(self.read(8), byteorder = 'little', signed = False)
- else:
- return int.from_bytes(self.read(4), byteorder = 'little', signed = False)
-
- def get_ptr(self, pos):
- self.move(pos)
- return self.read_uint()
-
- def get_ptr_with_offset(self, pos):
- if self.sysinfo.architecture == KatzSystemArchitecture.X64:
- self.move(pos)
- ptr = int.from_bytes(self.read(4), byteorder = 'little', signed = True)
- return pos + 4 + ptr
- else:
- self.move(pos)
- return self.read_uint()
-
diff --git a/files/plugins/pym_pypykatz/sysinfo_helpers.py b/files/plugins/pym_pypykatz/sysinfo_helpers.py
deleted file mode 100644
index 425f76a..0000000
--- a/files/plugins/pym_pypykatz/sysinfo_helpers.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# sysinfo_helpers.py
-#
-# Helper functions to retrieve the file time and version information from a file.
-# NB! there are cleaner and better ways to do this, but this works ...
-#
-# https://github.com/ufrisk/
-#
-# (c) Ulf Frisk, 2019-2021
-# Author: Ulf Frisk, pcileech@frizk.net
-#
-
-from io import BytesIO
-from dissect import cstruct
-import memprocfs
-
-PE_STRUCT_DEFINITIONS = """
- #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
- #define IMAGE_SIZEOF_SHORT_NAME 8
- typedef struct _IMAGE_DOS_HEADER
- {
- WORD e_magic;
- WORD e_cblp;
- WORD e_cp;
- WORD e_crlc;
- WORD e_cparhdr;
- WORD e_minalloc;
- WORD e_maxalloc;
- WORD e_ss;
- WORD e_sp;
- WORD e_csum;
- WORD e_ip;
- WORD e_cs;
- WORD e_lfarlc;
- WORD e_ovno;
- WORD e_res[4];
- WORD e_oemid;
- WORD e_oeminfo;
- WORD e_res2[10];
- LONG e_lfanew;
- } IMAGE_DOS_HEADER;
- typedef struct _IMAGE_FILE_HEADER {
- WORD Machine;
- WORD NumberOfSections;
- DWORD TimeDateStamp;
- DWORD PointerToSymbolTable;
- DWORD NumberOfSymbols;
- WORD SizeOfOptionalHeader;
- WORD Characteristics;
- } IMAGE_FILE_HEADER;
-"""
-
-def PEGetFileTime(process, module):
- mz_va = process.module(module).base
- mz_bytes = process.memory.read(mz_va, 0x1000)
- mz_stream = BytesIO(mz_bytes)
- # Set up dissect.cstruct
- pestruct = cstruct.cstruct()
- pestruct.load(PE_STRUCT_DEFINITIONS)
- # Parse MZ header
- struct_mz = pestruct.IMAGE_DOS_HEADER(mz_stream)
- if struct_mz.e_magic != 0x5a4d:
- return 0
- mz_stream.seek(struct_mz.e_lfanew)
- signature = pestruct.uint32(mz_stream)
- if signature != 0x4550:
- return 0
- # Parse the PE file_header struct.
- struct_file_header = pestruct.IMAGE_FILE_HEADER(mz_stream)
- return struct_file_header.TimeDateStamp
diff --git a/files/plugins/pym_pypykatz/version.txt b/files/plugins/pym_pypykatz/version.txt
deleted file mode 100644
index f0bb29e..0000000
--- a/files/plugins/pym_pypykatz/version.txt
+++ /dev/null
@@ -1 +0,0 @@
-1.3.0