Adding Kerberos support for CME #22

TODO
- aeskey
- dc-ip
- checkifadmin()
main
mpgn 2020-05-03 14:30:41 -04:00
parent c3c9b2f04a
commit 1308bc30c8
7 changed files with 140 additions and 106 deletions

View File

@ -47,6 +47,7 @@ def gen_cli_args():
std_parser.add_argument('-id', metavar="CRED_ID", nargs='+', default=[], type=str, dest='cred_id', help='database credential ID(s) to use for authentication')
std_parser.add_argument("-u", metavar="USERNAME", dest='username', nargs='+', default=[], help="username(s) or file(s) containing usernames")
std_parser.add_argument("-p", metavar="PASSWORD", dest='password', nargs='+', default=[], help="password(s) or file(s) containing passwords")
std_parser.add_argument("-k", "--kerberos", action='store_true', help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
fail_group = std_parser.add_mutually_exclusive_group()
fail_group.add_argument("--gfail-limit", metavar='LIMIT', type=int, help='max number of global failed login attempts')
fail_group.add_argument("--ufail-limit", metavar='LIMIT', type=int, help='max number of failed login attempts per username')

View File

@ -28,13 +28,18 @@ class connection(object):
self.conn = None
self.admin_privs = False
self.logger = None
self.password = None
self.username = None
self.password = ''
self.username = ''
self.kerberos = True if self.args.kerberos else False
self.aesKey = None
self.dc_ip = None
self.failed_logins = 0
self.local_ip = None
try:
self.host = gethostbyname(self.hostname)
if self.args.kerberos:
self.host = self.hostname
except Exception as e:
logging.debug('Error resolving hostname {}: {}'.format(self.hostname, e))
return
@ -60,6 +65,9 @@ class connection(object):
def check_if_admin(self):
return
def kerberos_login(self):
return
def plaintext_login(self, domain, username, password):
return
@ -133,110 +141,113 @@ class connection(object):
return False
def login(self):
for cred_id in self.args.cred_id:
with sem:
if cred_id.lower() == 'all':
creds = self.db.get_credentials()
else:
creds = self.db.get_credentials(filterTerm=int(cred_id))
if self.args.kerberos:
if self.kerberos_login(): return True
else:
for cred_id in self.args.cred_id:
with sem:
if cred_id.lower() == 'all':
creds = self.db.get_credentials()
else:
creds = self.db.get_credentials(filterTerm=int(cred_id))
for cred in creds:
logging.debug(cred)
try:
c_id, domain, username, password, credtype, pillaged_from = cred
for cred in creds:
logging.debug(cred)
try:
c_id, domain, username, password, credtype, pillaged_from = cred
if credtype and password:
if credtype and password:
if not domain: domain = self.domain
if not domain: domain = self.domain
if self.args.local_auth:
domain = self.domain
elif 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 True
if credtype == 'hash' and not self.over_fail_limit(username):
if self.hash_login(domain, username, password): return True
elif credtype == 'plaintext' and not self.over_fail_limit(username):
if self.plaintext_login(domain, username, password): return True
elif credtype == 'plaintext' and not self.over_fail_limit(username):
if self.plaintext_login(domain, username, password): return True
except IndexError:
self.logger.error("Invalid database credential ID!")
except IndexError:
self.logger.error("Invalid database credential ID!")
for user in self.args.username:
if not isinstance(user, str) and isfile(user.name):
for usr in user:
if "\\" in usr:
tmp = usr
usr = tmp.split('\\')[1].strip()
self.domain = tmp.split('\\')[0]
if self.args.hash:
with sem:
for ntlm_hash in self.args.hash:
if isinstance(ntlm_hash, str):
if not self.over_fail_limit(usr.strip()):
if self.hash_login(self.domain, usr.strip(), ntlm_hash): return True
elif not isinstance(ntlm_hash, str) and isfile(ntlm_hash.name) and self.args.no_bruteforce == False:
for f_hash in ntlm_hash:
for user in self.args.username:
if not isinstance(user, str) and isfile(user.name):
for usr in user:
if "\\" in usr:
tmp = usr
usr = tmp.split('\\')[1].strip()
self.domain = tmp.split('\\')[0]
if self.args.hash:
with sem:
for ntlm_hash in self.args.hash:
if isinstance(ntlm_hash, str):
if not self.over_fail_limit(usr.strip()):
if self.hash_login(self.domain, usr.strip(), f_hash.strip()): return True
ntlm_hash.seek(0)
if self.hash_login(self.domain, usr.strip(), ntlm_hash): return True
elif not isinstance(ntlm_hash, str) and isfile(ntlm_hash.name) and self.args.no_bruteforce == True:
user.seek(0)
for usr, f_pass in zip(user, ntlm_hash):
elif not isinstance(ntlm_hash, str) and isfile(ntlm_hash.name) and self.args.no_bruteforce == False:
for f_hash in ntlm_hash:
if not self.over_fail_limit(usr.strip()):
if self.hash_login(self.domain, usr.strip(), f_hash.strip()): return True
ntlm_hash.seek(0)
elif not isinstance(ntlm_hash, str) and isfile(ntlm_hash.name) and self.args.no_bruteforce == True:
user.seek(0)
for usr, f_pass in zip(user, ntlm_hash):
if not self.over_fail_limit(usr.strip()):
if self.plaintext_login(self.domain, usr.strip(), f_hash.strip()): return True
elif self.args.password:
with sem:
for password in self.args.password:
if isinstance(password, str):
if not self.over_fail_limit(usr.strip()):
if self.plaintext_login(self.domain, usr.strip(), f_hash.strip()): return True
if self.plaintext_login(self.domain, usr.strip(), password): return True
elif self.args.password:
with sem:
for password in self.args.password:
if isinstance(password, str):
if not self.over_fail_limit(usr.strip()):
if self.plaintext_login(self.domain, usr.strip(), password): return True
elif not isinstance(password, str) and isfile(password.name) and self.args.no_bruteforce == False:
for f_pass in password:
if not self.over_fail_limit(usr.strip()):
if self.plaintext_login(self.domain, usr.strip(), f_pass.strip()): return True
password.seek(0)
elif not isinstance(password, str) and isfile(password.name) and self.args.no_bruteforce == False:
for f_pass in password:
if not self.over_fail_limit(usr.strip()):
if self.plaintext_login(self.domain, usr.strip(), f_pass.strip()): return True
password.seek(0)
elif not isinstance(password, str) and isfile(password.name) and self.args.no_bruteforce == True:
user.seek(0)
for usr, f_pass in zip(user, password):
if not self.over_fail_limit(usr.strip()):
if self.plaintext_login(self.domain, usr.strip(), f_pass.strip()): return True
elif not isinstance(password, str) and isfile(password.name) and self.args.no_bruteforce == True:
user.seek(0)
for usr, f_pass in zip(user, password):
if not self.over_fail_limit(usr.strip()):
if self.plaintext_login(self.domain, usr.strip(), f_pass.strip()): return True
elif isinstance(user, str):
if hasattr(self.args, 'hash') and self.args.hash:
with sem:
for ntlm_hash in self.args.hash:
if isinstance(ntlm_hash, str):
if not self.over_fail_limit(user):
if self.hash_login(self.domain, user, ntlm_hash): return True
elif not isinstance(ntlm_hash, str) and isfile(ntlm_hash.name):
for f_hash in ntlm_hash:
elif isinstance(user, str):
if hasattr(self.args, 'hash') and self.args.hash:
with sem:
for ntlm_hash in self.args.hash:
if isinstance(ntlm_hash, str):
if not self.over_fail_limit(user):
if self.hash_login(self.domain, user, f_hash.strip()): return True
ntlm_hash.seek(0)
if self.hash_login(self.domain, user, ntlm_hash): return True
elif self.args.password:
with sem:
for password in self.args.password:
if isinstance(password, str):
if not self.over_fail_limit(user):
if hasattr(self.args, 'domain'):
if self.plaintext_login(self.domain, user, password): return True
else:
if self.plaintext_login(user, password): return True
elif not isinstance(ntlm_hash, str) and isfile(ntlm_hash.name):
for f_hash in ntlm_hash:
if not self.over_fail_limit(user):
if self.hash_login(self.domain, user, f_hash.strip()): return True
ntlm_hash.seek(0)
elif not isinstance(password, str) and isfile(password.name):
for f_pass in password:
elif self.args.password:
with sem:
for password in self.args.password:
if isinstance(password, str):
if not self.over_fail_limit(user):
if hasattr(self.args, 'domain'):
if self.plaintext_login(self.domain, user, f_pass.strip()): return True
if self.plaintext_login(self.domain, user, password): return True
else:
if self.plaintext_login(user, f_pass.strip()): return True
password.seek(0)
if self.plaintext_login(user, password): return True
elif not isinstance(password, str) and isfile(password.name):
for f_pass in password:
if not self.over_fail_limit(user):
if hasattr(self.args, 'domain'):
if self.plaintext_login(self.domain, user, f_pass.strip()): return True
else:
if self.plaintext_login(user, f_pass.strip()): return True
password.seek(0)

View File

@ -33,7 +33,7 @@ def called_from_cmd_args():
for stack in inspect.stack():
if stack[3] == 'print_host_info':
return True
if stack[3] == 'plaintext_login' or stack[3] == 'hash_login':
if stack[3] == 'plaintext_login' or stack[3] == 'hash_login' or stack[3] == 'kerberos_login':
return True
if stack[3] == 'call_cmd_args':
return True

View File

@ -11,6 +11,7 @@ from impacket.nmb import NetBIOSError
from impacket.dcerpc.v5 import transport, lsat, lsad
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5.transport import DCERPCTransportFactory
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.dcerpc.v5.epm import MSRPC_UUID_PORTMAP
from impacket.dcerpc.v5.dcom.wmi import WBEM_FLAG_FORWARD_ONLY
from impacket.dcerpc.v5.samr import SID_NAME_USE
@ -185,6 +186,8 @@ class smb(connection):
transport = DCERPCTransportFactory(stringBinding)
transport.set_connect_timeout(5)
dce = transport.get_dce_rpc()
if self._conn.kerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.connect()
try:
dce.bind(MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0'))
@ -221,6 +224,9 @@ class smb(connection):
if not self.domain:
self.domain = self.hostname
if self.args.kerberos:
self.domain = self.conn.getServerDNSDomainName()
self.db.add_computer(self.host, self.hostname, self.domain, self.server_os)
try:
@ -248,6 +254,15 @@ class smb(connection):
self.domain,
self.signing,
self.smbv1))
def kerberos_login(self):
self.conn.kerberosLogin('', '', self.domain, self.lmhash, self.nthash, self.aesKey, self.dc_ip)
# self.check_if_admin() # currently not working with kerberos so we set admin_privs to True
self.admin_privs = True
out = u'{}\\{} {}'.format(self.domain,
self.conn.getCredentials()[0],
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
self.logger.success(out)
return True
def plaintext_login(self, domain, username, password):
try:
@ -395,7 +410,7 @@ class smb(connection):
if method == 'wmiexec':
try:
exec_method = WMIEXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.hash, self.args.share)
exec_method = WMIEXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.kerberos, self.hash, self.args.share)
logging.debug('Executed command via wmiexec')
break
except:
@ -415,7 +430,7 @@ class smb(connection):
elif method == 'atexec':
try:
exec_method = TSCH_EXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.hash) #self.args.share)
exec_method = TSCH_EXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.kerberos, self.hash) #self.args.share)
logging.debug('Executed command via atexec')
break
except:
@ -425,7 +440,7 @@ class smb(connection):
elif method == 'smbexec':
try:
exec_method = SMBEXEC(self.host, self.smb_share_name, self.args.port, self.username, self.password, self.domain, self.hash, self.args.share)
exec_method = SMBEXEC(self.host, self.smb_share_name, self.args.port, self.username, self.password, self.domain, self.kerberos, self.hash, self.args.share)
logging.debug('Executed command via smbexec')
break
except:

View File

@ -2,11 +2,12 @@ import os
import logging
from impacket.dcerpc.v5 import tsch, transport
from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from cme.helpers.misc import gen_random_string
from gevent import sleep
class TSCH_EXEC:
def __init__(self, target, share_name, username, password, domain, hashes=None):
def __init__(self, target, share_name, username, password, domain, doKerberos=False, hashes=None):
self.__target = target
self.__username = username
self.__password = password
@ -16,8 +17,9 @@ class TSCH_EXEC:
self.__nthash = ''
self.__outputBuffer = b''
self.__retOutput = False
#self.__aesKey = aesKey
#self.__doKerberos = doKerberos
# self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__kdcHost = None
if hashes is not None:
#This checks to see if we didn't provide the LM Hash
@ -35,7 +37,7 @@ class TSCH_EXEC:
if hasattr(self.__rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
self.__rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
#rpctransport.set_kerberos(self.__doKerberos)
self.__rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
def execute(self, command, output=False):
self.__retOutput = output
@ -124,6 +126,8 @@ class TSCH_EXEC:
def doStuff(self, command, fileless=False):
dce = self.__rpctransport.get_dce_rpc()
if self.__doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.set_credentials(*self.__rpctransport.get_credentials())
dce.connect()

View File

@ -4,10 +4,11 @@ from gevent import sleep
from impacket.dcerpc.v5 import transport, scmr
from impacket.smbconnection import *
from cme.helpers.misc import gen_random_string
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
class SMBEXEC:
def __init__(self, host, share_name, protocol, username = '', password = '', domain = '', hashes = None, share = None, port=445):
def __init__(self, host, share_name, protocol, username = '', password = '', domain = '', doKerberos=False, hashes = None, share = None, port=445):
self.__host = host
self.__share_name = share_name
self.__port = port
@ -26,9 +27,10 @@ class SMBEXEC:
self.__rpctransport = None
self.__scmr = None
self.__conn = None
#self.__mode = mode
#self.__aesKey = aesKey
#self.__doKerberos = doKerberos
# self.__mode = mode
# self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__kdcHost = None
if hashes is not None:
#This checks to see if we didn't provide the LM Hash
@ -50,9 +52,11 @@ class SMBEXEC:
if hasattr(self.__rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
self.__rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
#rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
self.__rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
self.__scmr = self.__rpctransport.get_dce_rpc()
if self.__doKerberos:
self.__scmr.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
self.__scmr.connect()
s = self.__rpctransport.get_smb_connection()
# We don't wanna deal with timeouts from now on.

View File

@ -8,7 +8,7 @@ from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL
class WMIEXEC:
def __init__(self, target, share_name, username, password, domain, smbconnection, hashes=None, share=None):
def __init__(self, target, share_name, username, password, domain, smbconnection, doKerberos=False, hashes=None, share=None):
self.__target = target
self.__username = username
self.__password = password
@ -23,7 +23,7 @@ class WMIEXEC:
self.__shell = 'cmd.exe /Q /c '
self.__pwd = 'C:\\'
self.__aesKey = None
self.__doKerberos = False
self.__doKerberos = doKerberos
self.__retOutput = True
if hashes is not None:
@ -35,8 +35,7 @@ class WMIEXEC:
if self.__password is None:
self.__password = ''
self.__dcom = DCOMConnection(self.__target, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, oxidResolver = True, doKerberos=self.__doKerberos)
self.__dcom = DCOMConnection(self.__target, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos)
iInterface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)