Skip to content

Commit 33e74ca

Browse files
author
dotpy
committed
Add possibility to use different sessions for execution and file interaction
DumpMethod now differs between execution and file interaction so that it is possible to use winrm as execution path but smb as file interaction or vise versa. When execution and file interactions are the same both sessions refer to the same session object.
1 parent 61ee472 commit 33e74ca

24 files changed

+133
-66
lines changed

lsassy/console.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ def main():
4444
", ".join(Dumper.list_exec_methods())
4545
),
4646
)
47+
group_dump.add_argument("--file-interaction", action="store", help="Methods for file interaction, default is smb", choices=["smb", "winrm"], default="smb")
48+
4749
group_dump.add_argument(
4850
"--no-powershell", action="store_true", help="Disable PowerShell"
4951
)
@@ -84,23 +86,34 @@ def main():
8486
help="Do not delete lsass dump on remote host",
8587
)
8688

89+
# There might be cases for boxes where the user may have access to smb but not winrm or vise versa
8790
group_auth = parser.add_argument_group("authentication")
8891
group_auth.add_argument("-u", "--username", action="store", help="Username")
92+
group_auth.add_argument("-fu", "--file-username", action="store", help="Username for file interaction (if file interaction and exec differ!)")
93+
8994
group_auth.add_argument(
9095
"-p", "--password", action="store", help="Plaintext password"
9196
)
97+
group_auth.add_argument(
98+
"-fp", "--file-password", action="store", help="Plaintext password for file interaction (if file interaction and exec differ!)"
99+
)
92100
group_auth.add_argument(
93101
"-d", "--domain", default="", action="store", help="Domain name"
94102
)
95103
group_auth.add_argument(
96-
"--port", default=445, type=int, action="store", help="Port (Default: 445)"
104+
"--port", default=0, type=int, action="store", help="Port (Default: 445)"
105+
)
106+
group_auth.add_argument(
107+
"--file-port", default=0, type=int, action="store", help="Port for file interaction (if file interaction and exec differ!)"
97108
)
98109
group_auth.add_argument(
99110
"--no-pass",
100111
action="store_true",
101112
help="Do not provide password (Default: False)",
102113
)
103114
group_auth.add_argument("-H", "--hashes", action="store", help="[LM:]NT hash")
115+
group_auth.add_argument("-FH", "--file-hashes", action="store", help="[LM:]NT hash for file interaction (if file interaction and exec differ!)")
116+
104117
group_auth.add_argument(
105118
"-k",
106119
"--kerberos",

lsassy/core.py

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ def run(self):
102102

103103
# Credential parsing
104104
username = self.args.username if self.args.username else ""
105+
file_username = self.args.file_username if self.args.file_username else username
106+
105107
password = self.args.password if self.args.password else ""
108+
file_password = self.args.file_password if self.args.file_password else password
106109

107110
lmhash, nthash = "", ""
108111
if not password and self.args.hashes:
@@ -111,11 +114,27 @@ def run(self):
111114
else:
112115
lmhash, nthash = "aad3b435b51404eeaad3b435b51404ee", self.args.hashes
113116

117+
file_lmhash, file_nthash = "", ""
118+
if not file_password and self.args.file_hashes:
119+
if ":" in self.args.file_hashes:
120+
file_lmhash, file_nthash = self.args.file_hashes.split(":")
121+
else:
122+
file_lmhash, file_nthash = "aad3b435b51404eeaad3b435b51404ee", self.args.file_hashes
123+
124+
125+
126+
114127
# Exec methods parsing
115128
exec_methods = self.args.exec.split(",") if self.args.exec else None
116129
if exec_methods and "winrm" in exec_methods and len(exec_methods)>1:
117130
lsassy_logger.error(f"Incompatible methods winrm and {exec_methods} - can only use either winrm or others")
118131

132+
# Ports parsing
133+
if self.args.file_port == 0:
134+
self.args.file_port = 5985 if self.args.file_interaction == "winrm" else 445
135+
if self.args.port == 0:
136+
self.args.port = 5985 if exec_methods and "winrm" in exec_methods else 445
137+
119138
# Dump modules options parsing
120139
options = (
121140
{v.split("=")[0]: v.split("=")[1] for v in self.args.options.split(",")}
@@ -155,33 +174,57 @@ def run(self):
155174
return False
156175

157176
try:
158-
if exec_methods and "winrm" in exec_methods:
177+
if self.args.file_interaction == "winrm":
159178
session = WinrmSession()
160-
self.args.port = 5985
161179
else:
162180
session = Session()
163181
session.get_session(
164182
address=self.target,
165183
target_ip=self.target,
166-
port=self.args.port,
167-
lmhash=lmhash,
168-
nthash=nthash,
169-
username=username,
170-
password=password,
184+
port=self.args.file_port,
185+
lmhash=file_lmhash,
186+
nthash=file_nthash,
187+
username=file_username,
188+
password=file_password,
171189
domain=self.args.domain,
172190
aesKey=self.args.aesKey,
173191
dc_ip=self.args.dc_ip,
174192
kerberos=self.args.kerberos,
175193
timeout=self.args.timeout,
176194
)
177195

196+
if exec_methods and "winrm" in exec_methods:
197+
exec_session = WinrmSession()
198+
else:
199+
exec_session = Session()
200+
# if the sessions are the same we dont want two sessions
201+
if isinstance(exec_session, type(session)):
202+
del exec_session
203+
exec_session = session
204+
else:
205+
exec_session.get_session(
206+
address=self.target,
207+
target_ip=self.target,
208+
port=self.args.port,
209+
lmhash=lmhash,
210+
nthash=nthash,
211+
username=username,
212+
password=password,
213+
domain=self.args.domain,
214+
aesKey=self.args.aesKey,
215+
dc_ip=self.args.dc_ip,
216+
kerberos=self.args.kerberos,
217+
timeout=self.args.timeout,
218+
)
219+
220+
178221
if session.smb_session is None:
179222
lsassy_logger.warning("Couldn't connect to remote host")
180223
return False
181224

182225
if not parse_only:
183226
dumper = Dumper(
184-
session, self.args.timeout, self.args.time_between_commands
227+
exec_session, session, self.args.timeout, self.args.time_between_commands
185228
).load(self.args.dump_method)
186229
if dumper is None:
187230
lsassy_logger.error("Unable to load dump module")
@@ -295,3 +338,11 @@ def run(self):
295338
lsassy_logger.debug(
296339
"Potential issue while closing SMB session: {}".format(str(e))
297340
)
341+
try:
342+
# Im not gonna break up the whole try except block. If exec_session is unassigned we dont care.
343+
exec_session.smb_session.close()
344+
lsassy_logger.debug("SMB session closed")
345+
except Exception as e:
346+
lsassy_logger.debug(
347+
"Potential issue while closing SMB session: {}".format(str(e))
348+
)

lsassy/dumper.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ class Dumper:
1313
Returns None if doesn't exist.
1414
"""
1515

16-
def __init__(self, session, timeout, time_between_commands):
17-
self._session = session
16+
def __init__(self, exec_session, file_session, timeout, time_between_commands):
17+
self._session = file_session
18+
self._exec_session = exec_session
1819
self._timeout = timeout
1920
self._time_between_commands = time_between_commands
2021

@@ -27,7 +28,7 @@ def load(self, dump_module):
2728
try:
2829
return importlib.import_module(
2930
"lsassy.dumpmethod.{}".format(dump_module.lower()), "DumpMethod"
30-
).DumpMethod(self._session, self._timeout, self._time_between_commands)
31+
).DumpMethod(self._exec_session, self._session, self._timeout, self._time_between_commands)
3132
except ModuleNotFoundError:
3233
lsassy_logger.warning("Dump module '{}' doesn't exist".format(dump_module))
3334
return None

lsassy/dumpmethod/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,10 @@ class IDumpMethod:
157157
"vmrs",
158158
]
159159

160-
def __init__(self, session, timeout, time_between_commands, *args, **kwargs):
161-
self._session = session
162-
self._file = session.correct_file_handler()(self._session)
160+
def __init__(self, exec_session, file_session, timeout, time_between_commands, *args, **kwargs):
161+
self._session = file_session
162+
self._exec_session = exec_session
163+
self._file = file_session.correct_file_handler()(self._session)
163164
self._file_handle = None
164165
self._executor_name = ""
165166
self._executor_path = ""
@@ -171,7 +172,7 @@ def get_exec_method(self, exec_method, no_powershell=False):
171172
try:
172173
exec_method = importlib.import_module(
173174
f"lsassy.exec.{exec_method.lower()}", "Exec"
174-
).Exec(self._session)
175+
).Exec(self._exec_session)
175176
except ModuleNotFoundError:
176177
lsassy_logger.error(
177178
f"Exec module '{exec_method.lower()}' doesn't exist",
@@ -394,6 +395,7 @@ def dump(
394395
)
395396
return None
396397

398+
# why is this whole block unreachable??
397399
for e, exec_method in valid_exec_methods.items():
398400
lsassy_logger.info(f"Trying {e} method")
399401
exec_commands = self.build_exec_command(

lsassy/dumpmethod/comsvcs_stealth.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
class DumpMethod(IDumpMethod):
1010
need_debug_privilege = True
1111

12-
def __init__(self, session, timeout, time_between_commands):
13-
super().__init__(session, timeout, time_between_commands)
12+
def __init__(self, exec_session, file_session, timeout, time_between_commands):
13+
super().__init__(exec_session, file_session, timeout, time_between_commands)
1414

1515
# If default, set to 7. Otherwise, keep custom time
1616
if self._time_between_commands == 1:

lsassy/dumpmethod/dllinject.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ class DumpMethod(IDumpMethod):
99
dump_share = "C$"
1010
dump_path = "\\Windows\\Temp\\"
1111

12-
def __init__(self, session, timeout, time_between_commands):
13-
super().__init__(session, timeout, time_between_commands)
12+
def __init__(self, exec_session, file_session, timeout, time_between_commands):
13+
super().__init__(exec_session, file_session, timeout, time_between_commands)
1414
self.loader = Dependency("loader", "loader.exe")
1515
self.dll = Dependency("dll", "calc.dll")
1616

lsassy/dumpmethod/dumpert.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ class DumpMethod(IDumpMethod):
1313
dump_share = "C$"
1414
dump_path = "\\Windows\\Temp\\"
1515

16-
def __init__(self, session, timeout, time_between_commands):
17-
super().__init__(session, timeout, time_between_commands)
16+
def __init__(self, exec_session, file_session, timeout, time_between_commands):
17+
super().__init__(exec_session, file_session, timeout, time_between_commands)
1818
self.dumpert = Dependency("dumpert", "dumpert.exe")
1919

2020
def prepare(self, options):

lsassy/dumpmethod/dumpertdll.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ class DumpMethod(IDumpMethod):
1313
dump_share = "C$"
1414
dump_path = "\\Windows\\Temp\\"
1515

16-
def __init__(self, session, timeout, time_between_commands):
17-
super().__init__(session, timeout, time_between_commands)
16+
def __init__(self, exec_session, file_session, timeout, time_between_commands):
17+
super().__init__(exec_session, file_session, timeout, time_between_commands)
1818
self.dumpertdll = Dependency("dumpertdll", "dumpert.dll")
1919

2020
def prepare(self, options):

lsassy/dumpmethod/edrsandblast.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121

2222
class DumpMethod(IDumpMethod):
23-
def __init__(self, session, timeout, time_between_commands):
24-
super().__init__(session, timeout, time_between_commands)
23+
def __init__(self, exec_session, file_session, timeout, time_between_commands):
24+
super().__init__(exec_session, file_session, timeout, time_between_commands)
2525
self.edrsandblast = Dependency("edrsandblast", "EDRSandBlast.exe")
2626
self.RTCore64 = Dependency("RTCore64", "RTCore64.sys")
2727
self.ntoskrnl = Dependency("ntoskrnl", "NtoskrnlOffsets.csv")

lsassy/dumpmethod/mirrordump.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010

1111
class DumpMethod(IDumpMethod):
12-
def __init__(self, session, timeout, time_between_commands):
13-
super().__init__(session, timeout, time_between_commands)
12+
def __init__(self, exec_session, file_session, timeout, time_between_commands):
13+
super().__init__(exec_session, file_session, timeout, time_between_commands)
1414
self.mirrordump = Dependency("mirrordump", "Mirrordump.exe")
1515

1616
def prepare(self, options):

0 commit comments

Comments
 (0)