Skip to content
This repository has been archived by the owner on Dec 6, 2023. It is now read-only.

Commit

Permalink
This commit addresses a number of issues including #130 and #126
Browse files Browse the repository at this point in the history
  • Loading branch information
byt3bl33d3r committed Sep 21, 2016
1 parent 1468e25 commit 0787298
Show file tree
Hide file tree
Showing 19 changed files with 208 additions and 151 deletions.
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
clean:
rm --force --recursive build/
rm --force --recursive dist/
rm --force --recursive *.egg-info
find . -name '*.pyc' -exec rm --force {} +
find . -name '*.pyo' -exec rm --force {} +
find . -name '*~' -exec rm --force {} +
2 changes: 2 additions & 0 deletions cme/cmechainserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ssl
import os
import sys
import logging
from BaseHTTPServer import BaseHTTPRequestHandler
from logging import getLogger
from gevent import sleep
Expand Down Expand Up @@ -92,6 +93,7 @@ def __init__(self, chain_list, context, logger, srv_host, port, server_type='htt
self.server.log = context.log
self.cert_path = os.path.join(os.path.expanduser('~/.cme'), 'cme.pem')

logging.debug('CME chain server type: ' + server_type)
if server_type == 'https':
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.cert_path, server_side=True)

Expand Down
118 changes: 62 additions & 56 deletions cme/cmedb.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,54 +187,57 @@ def do_hosts(self, line):
else:
hosts = self.db.get_hosts(filterTerm=filterTerm)

print "\nHost(s):\n"
print " HostID IP Hostname Domain OS"
print " ------ -- -------- ------ --"

hostIDList = []

for host in hosts:
hostID = host[0]
hostIDList.append(hostID)

ip = host[1]
hostname = host[2]
domain = host[3]
os = host[4]

print u" {}{}{}{}{}".format('{:<8}'.format(hostID),
'{:<17}'.format(ip),
u'{:<25}'.format(hostname.decode('utf-8')),
u'{:<17}'.format(domain.decode('utf-8')),
'{:<17}'.format(os))

print ""

print "\nCredential(s) with Admin Access:\n"
print " CredID CredType Domain UserName Password"
print " ------ -------- ------ -------- --------"

for hostID in hostIDList:
links = self.db.get_links(hostID=hostID)

for link in links:
linkID, credID, hostID = link
creds = self.db.get_credentials(filterTerm=credID)

for cred in creds:
credID = cred[0]
credType = cred[1]
domain = cred[2]
username = cred[3]
password = cred[4]

print u" {}{}{}{}{}".format('{:<8}'.format(credID),
'{:<12}'.format(credType),
u'{:<17}'.format(domain.decode('utf-8')),
u'{:<21}'.format(username.decode('utf-8')),
u'{:<17}'.format(password.decode('utf-8')))

print ""
if len(hosts) > 1:
self.display_hosts(hosts)
elif len(hosts) == 1:
print "\nHost(s):\n"
print " HostID IP Hostname Domain OS"
print " ------ -- -------- ------ --"

hostIDList = []

for host in hosts:
hostID = host[0]
hostIDList.append(hostID)

ip = host[1]
hostname = host[2]
domain = host[3]
os = host[4]

print u" {}{}{}{}{}".format('{:<8}'.format(hostID),
'{:<17}'.format(ip),
u'{:<25}'.format(hostname.decode('utf-8')),
u'{:<17}'.format(domain.decode('utf-8')),
'{:<17}'.format(os))

print ""

print "\nCredential(s) with Admin Access:\n"
print " CredID CredType Domain UserName Password"
print " ------ -------- ------ -------- --------"

for hostID in hostIDList:
links = self.db.get_links(hostID=hostID)

for link in links:
linkID, credID, hostID = link
creds = self.db.get_credentials(filterTerm=credID)

for cred in creds:
credID = cred[0]
credType = cred[1]
domain = cred[2]
username = cred[3]
password = cred[4]

print u" {}{}{}{}{}".format('{:<8}'.format(credID),
'{:<12}'.format(credType),
u'{:<17}'.format(domain.decode('utf-8')),
u'{:<21}'.format(username.decode('utf-8')),
u'{:<17}'.format(password.decode('utf-8')))

print ""

def do_creds(self, line):

Expand Down Expand Up @@ -282,8 +285,8 @@ def do_creds(self, line):
creds = self.db.get_credentials(filterTerm=filterTerm)

print "\nCredential(s):\n"
print " CredID CredType Domain UserName Password"
print " ------ -------- ------ -------- --------"
print " CredID CredType Pillaged From HostID Domain UserName Password"
print " ------ -------- -------------------- ------ -------- --------"

credIDList = []

Expand All @@ -295,12 +298,15 @@ def do_creds(self, line):
domain = cred[2]
username = cred[3]
password = cred[4]

print u" {}{}{}{}{}".format('{:<8}'.format(credID),
'{:<12}'.format(credType),
u'{:<17}'.format(domain.decode('utf-8')),
u'{:<21}'.format(username.decode('utf-8')),
u'{:<17}'.format(password.decode('utf-8')))
pillaged_from = cred[5]

print u" {}{}{}{}{}{}".format('{:<8}'.format(credID),
'{:<12}'.format(credType),
'{:<22}'.format(pillaged_from),
u'{:<17}'.format(domain.decode('utf-8')),
u'{:<21}'.format(username.decode('utf-8')),
u'{:<17}'.format(password.decode('utf-8'))
)

print ""

Expand Down
2 changes: 2 additions & 0 deletions cme/cmeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ssl
import os
import sys
import logging
from BaseHTTPServer import BaseHTTPRequestHandler
from logging import getLogger
from gevent import sleep
Expand Down Expand Up @@ -54,6 +55,7 @@ def __init__(self, module, context, logger, srv_host, port, server_type='https')
self.server.log = context.log
self.cert_path = os.path.join(os.path.expanduser('~/.cme'), 'cme.pem')

logging.debug('CME server type: ' + server_type)
if server_type == 'https':
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.cert_path, server_side=True)

Expand Down
24 changes: 11 additions & 13 deletions cme/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,11 @@ def __init__(self, args, db, host, module, chain_list, cmeserver, share_name):

try:
'''
DC's seem to want us to logoff first
Windows workstations sometimes reset the connection, so we handle both cases here
DC's seem to want us to logoff first, windows workstations sometimes reset the connection
(go home Windows, you're drunk)
'''
smb.logoff()
except NetBIOSError:
pass
except socket.error:
pass
except SessionError:
except:
pass

if self.args.mssql:
Expand Down Expand Up @@ -278,7 +273,7 @@ def check_if_admin(self):
def plaintext_login(self, domain, username, password):
try:
if self.args.mssql:
res = self.conn.login(None, username, password, domain, None, True)
res = self.conn.login(None, username, password, domain, None, True if self.args.mssql_auth == 'windows' else False)
if res is not True:
self.conn.printReplies()
return False
Expand Down Expand Up @@ -334,7 +329,7 @@ def hash_login(self, domain, username, ntlm_hash):

try:
if self.args.mssql:
res = self.conn.login(None, username, '', domain, ntlm_hash, True)
res = self.conn.login(None, username, '', domain, ':' + nthash if not lmhash else ntlm_hash, True if self.args.mssql_auth == 'windows' else False)
if res is not True:
self.conn.printReplies()
return False
Expand Down Expand Up @@ -385,7 +380,11 @@ def login(self):
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 self.args.local_auth:
domain = self.domain
elif self.args.domain:
domain = self.args.domain

if credtype == 'hash' and not self.over_fail_limit(username):
if self.hash_login(domain, username, password): return
Expand Down Expand Up @@ -537,9 +536,8 @@ def lsa(self):

@requires_admin
def ntds(self):
#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()
#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(self.args.ntds, self.args.ntds_pwdLastSet, self.args.ntds_history)

@requires_admin
def wdigest(self):
Expand Down
77 changes: 42 additions & 35 deletions cme/crackmapexec.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,18 @@ 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=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")
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")
ddgroup.add_argument("--local-auth", action='store_true', help='Authenticate locally to each target')
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')
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')
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('-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')
parser.add_argument("--share", metavar="SHARE", default="C$", help="Specify a share (default: C$)")
Expand Down Expand Up @@ -112,8 +112,8 @@ def main():
sgroup.add_argument("--content", action='store_true', help='Enable file content searching')
sgroup.add_argument("--exclude-dirs", type=str, metavar='DIR_LIST', default='', help='Directories to exclude from spidering')
esgroup = sgroup.add_mutually_exclusive_group()
esgroup.add_argument("--pattern", nargs='*', help='Pattern(s) to search for in folders, filenames and file content')
esgroup.add_argument("--regex", nargs='*', help='Regex(s) to search for in folders, filenames and file content')
esgroup.add_argument("--pattern", nargs='+', help='Pattern(s) to search for in folders, filenames and file content')
esgroup.add_argument("--regex", nargs='+', help='Regex(s) to search for in folders, filenames and file content')
sgroup.add_argument("--depth", type=int, default=10, help='Spider recursion depth (default: 10)')

cgroup = parser.add_argument_group("Command Execution", "Options for executing commands")
Expand All @@ -127,6 +127,7 @@ def main():
mgroup = parser.add_argument_group("MSSQL Interaction", "Options for interacting with MSSQL DBs")
mgroup.add_argument("--mssql", action='store_true', help='Switches CME into MSSQL Mode. If credentials are provided will authenticate against all discovered MSSQL DBs')
mgroup.add_argument("--mssql-query", metavar='QUERY', type=str, help='Execute the specifed query against the MSSQL DB')
mgroup.add_argument("--mssql-auth", choices={'windows', 'normal'}, default='windows', help='MSSQL authentication type to use (default: windows)')

logger = CMEAdapter(setup_logger())
first_run_setup(logger)
Expand All @@ -144,22 +145,14 @@ def main():
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('Passed args:\n' + pformat(vars(args)))

if not args.server_port:
args.server_port = server_port_dict[args.server]

db_path = os.path.join(cme_path, 'cme.db')
# set the database connection to autocommit w/ isolation level
db_connection = sqlite3.connect(db_path, check_same_thread=False)
Expand Down Expand Up @@ -189,9 +182,13 @@ def main():
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)
try:
for n in range(int(start_id), int(end_id) + 1):
args.cred_id.append(n)
args.cred_id.remove(cred_id)
except Exception as e:
logger.error('Error parsing database credential id: {}'.format(e))
sys.exit(1)

for target in args.target:
if os.path.exists(target):
Expand All @@ -201,33 +198,43 @@ def main():
else:
targets.extend(parse_targets(target))

if args.module or args.list_modules:
if args.list_modules:
loader = ModuleLoader(args, db, logger)
modules = loader.get_modules()

if args.list_modules:
for m in modules:
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:
for m in modules.keys():
if args.module.lower() == m.lower():
logger.info('{} module options:\n{}'.format(m, modules[m]['options']))
sys.exit(0)
for m in modules:
logger.info('{:<25} Chainable: {:<10} {}'.format(m, str(modules[m]['chain_support']), modules[m]['description']))
sys.exit(0)

elif args.module:
for m in modules.keys():
if args.module.lower() == m.lower():
module, context, server = loader.init_module(modules[m]['path'])
elif args.module and args.show_options:
loader = ModuleLoader(args, db, logger)
modules = loader.get_modules()

elif args.module_chain:
chain_list, server = ModuleChainLoader(args, db, logger).init_module_chain()
for m in modules.keys():
if args.module.lower() == m.lower():
logger.info('{} module options:\n{}'.format(m, modules[m]['options']))
sys.exit(0)

if args.execute or args.ps_execute or args.module or args.module_chain:

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)

loader = ModuleLoader(args, db, logger)
modules = loader.get_modules()

smb_server = CMESMBServer(logger, share_name, args.verbose)
smb_server.start()

if args.module:
for m in modules.keys():
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()

try:
'''
Open all the greenlet (as supposed to redlet??) threads
Expand Down
Loading

0 comments on commit 0787298

Please sign in to comment.