def from_buffer(buff): msg = FileFullDirectoryInformation() msg.NextEntryOffset = int.from_bytes(buff.read(4), byteorder='little', signed=False) msg.FileIndex = int.from_bytes(buff.read(4), byteorder='little', signed=False) msg.CreationTime = FILETIME.from_buffer(buff).datetime msg.LastAccessTime = FILETIME.from_buffer(buff).datetime msg.LastWriteTime = FILETIME.from_buffer(buff).datetime msg.ChangeTime = FILETIME.from_buffer(buff).datetime msg.EndOfFile = int.from_bytes(buff.read(8), byteorder='little', signed=True) msg.AllocationSize = int.from_bytes(buff.read(8), byteorder='little', signed=True) msg.FileAttributes = FileAttributes( int.from_bytes(buff.read(4), byteorder='little', signed=False)) msg.FileNameLength = int.from_bytes(buff.read(4), byteorder='little', signed=False) msg.EaSize = int.from_bytes(buff.read(4), byteorder='little', signed=False) msg.FileName = buff.read(msg.FileNameLength).decode('utf-16-le') return msg
def from_challenge(challenge): si = NTLMServerInfo() ti = challenge.TargetInfo for k in ti: if k == AVPAIRType.MsvAvNbDomainName: si.domainname = ti[k] elif k == AVPAIRType.MsvAvNbComputerName: si.computername = ti[k] elif k == AVPAIRType.MsvAvDnsDomainName: si.dnsdomainname = ti[k] elif k == AVPAIRType.MsvAvDnsComputerName: si.dnscomputername = ti[k] elif k == AVPAIRType.MsvAvDnsTreeName: si.dnsforestname = ti[k] elif k == AVPAIRType.MsvAvTimestamp: if isinstance(ti[k], bytes): si.local_time = FILETIME.from_bytes(ti[k]).datetime elif isinstance(ti[k], dateime): si.local_time = ti[k] if challenge.Version is not None: si.os_major_version = challenge.Version.ProductMajorVersion si.os_minor_version = challenge.Version.ProductMinorVersion si.os_build = challenge.Version.ProductBuild si.os_guess = challenge.Version.WindowsProduct return si
async def QueryInfoKey(self, key): try: key = await self.__get_rawhandle(key) res, err = await rrp.hBaseRegQueryInfoKey(self.dce, key) if err is not None: raise err subkey_cnt = res['lpcSubKeys'] values_cnt = res['lpcValues'] lastwrite_time = FILETIME.from_dict( res['lpftLastWriteTime']).datetime return subkey_cnt, values_cnt, lastwrite_time, None except Exception as e: if isinstance(e, rrp.DCERPCSessionError): return None, None, None, OSError( e.get_error_code(), system_errors.ERROR_MESSAGES[e.get_error_code()][1]) return None, None, None, e
def decode_val(data, data_type): #print('decode_val %s : %s' % (data_type, data)) if data_type == ValueType.NullType: return '' elif data_type == ValueType.StringType: return data.decode('utf-16-le') elif data_type == ValueType.AnsiStringType: return data.decode() elif data_type == ValueType.Int8Type: return int.from_bytes(data, byteorder = 'little', signed = True) elif data_type == ValueType.UInt8Type: return int.from_bytes(data, byteorder = 'little', signed = False) elif data_type == ValueType.Int16Type: return int.from_bytes(data, byteorder = 'little', signed = True) elif data_type == ValueType.UInt16Type: return int.from_bytes(data, byteorder = 'little', signed = False) elif data_type == ValueType.Int32Type: return int.from_bytes(data, byteorder = 'little', signed = True) elif data_type == ValueType.UInt32Type: return int.from_bytes(data, byteorder = 'little', signed = False) elif data_type == ValueType.Int64Type: return int.from_bytes(data, byteorder = 'little', signed = True) elif data_type == ValueType.UInt64Type: return int.from_bytes(data, byteorder = 'little', signed = False) elif data_type == ValueType.Real32Type: return struct.unpack('<f', data) elif data_type == ValueType.Real64Type: return struct.unpack('<d', data) elif data_type == ValueType.BoolType: return bool(int.from_bytes(data, byteorder = 'little', signed = False)) elif data_type == ValueType.BinaryType: return data elif data_type == ValueType.GuidType: return GUID.from_bytes(data) elif data_type == ValueType.SizeTType: return '0x' + data[::-1].hex() elif data_type == ValueType.FileTimeType: return FILETIME.from_bytes(data).datetime.isoformat() elif data_type == ValueType.SysTimeType: return FILETIME.from_bytes(data).datetime.isoformat() elif data_type == ValueType.SidType: return SID.from_bytes(data) elif data_type == ValueType.HexInt32Type: return '0x' + data[::-1].hex() elif data_type == ValueType.HexInt64Type: return '0x' + data[::-1].hex() elif data_type == ValueType.BinXmlType: return Fragment.from_bytes(data) elif data_type == ValueType.StringArrayType: sa = [] i = 0 t = b'' while i < len(data): t += data[i:i+1] if t[-3:] == b'\x00\x00\x00' or t == b'\x00\x00': sa.append(t.decode('utf-16-le')[:-1]) t = b'' i += 1 return sa else: raise Exception('Unknown format! %s' % data_type)
async def get_user_secrets(self, username): ra = { 'userPrincipalName': '1.2.840.113556.1.4.656', 'sAMAccountName': '1.2.840.113556.1.4.221', 'unicodePwd': '1.2.840.113556.1.4.90', 'dBCSPwd': '1.2.840.113556.1.4.55', 'ntPwdHistory': '1.2.840.113556.1.4.94', 'lmPwdHistory': '1.2.840.113556.1.4.160', 'supplementalCredentials': '1.2.840.113556.1.4.125', 'objectSid': '1.2.840.113556.1.4.146', 'pwdLastSet': '1.2.840.113556.1.4.96', 'userAccountControl': '1.2.840.113556.1.4.8' } formatOffered = drsuapi.DS_NT4_ACCOUNT_NAME_SANS_DOMAIN crackedName, _ = await rr( self.DRSCrackNames(formatOffered, drsuapi.DS_NAME_FORMAT.DS_UNIQUE_ID_NAME, name=username)) ###### TODO: CHECKS HERE #guid = GUID.from_string(crackedName['pmsgOut']['V1']['pResult']['rItems'][0]['pName'][:-1][1:-1]) guid = crackedName['pmsgOut']['V1']['pResult']['rItems'][0][ 'pName'][:-1][1:-1] userRecord, _ = await rr(self.DRSGetNCChanges(guid, ra)) replyVersion = 'V%d' % userRecord['pdwOutVersion'] if userRecord['pmsgOut'][replyVersion]['cNumObjects'] == 0: raise Exception('DRSGetNCChanges didn\'t return any object!') #print(userRecord.dump()) #print(userRecord['pmsgOut'][replyVersion]['PrefixTableSrc']['pPrefixEntry']) record = userRecord prefixTable = userRecord['pmsgOut'][replyVersion]['PrefixTableSrc'][ 'pPrefixEntry'] ##### decryption! logger.debug('Decrypting hash for user: %s' % record['pmsgOut'][replyVersion]['pNC']['StringName'][:-1]) us = SMBUserSecrets() user_properties = None rid = int.from_bytes(record['pmsgOut'][replyVersion]['pObjects'] ['Entinf']['pName']['Sid'][-4:], 'little', signed=False) for attr in record['pmsgOut'][replyVersion]['pObjects']['Entinf'][ 'AttrBlock']['pAttr']: try: attId = drsuapi.OidFromAttid(prefixTable, attr['attrTyp']) LOOKUP_TABLE = self.ATTRTYP_TO_ATTID except Exception as e: logger.error( 'Failed to execute OidFromAttid with error %s, fallbacking to fixed table' % e) logger.error('Exception', exc_info=True) # Fallbacking to fixed table and hope for the best attId = attr['attrTyp'] LOOKUP_TABLE = self.NAME_TO_ATTRTYP if attId == LOOKUP_TABLE['dBCSPwd']: if attr['AttrVal']['valCount'] > 0: encrypteddBCSPwd = b''.join( attr['AttrVal']['pAVal'][0]['pVal']) encryptedLMHash = drsuapi.DecryptAttributeValue( self.dce.get_session_key(), encrypteddBCSPwd) us.lm_hash = drsuapi.removeDESLayer(encryptedLMHash, rid) else: us.lm_hash = bytes.fromhex( 'aad3b435b51404eeaad3b435b51404ee') elif attId == LOOKUP_TABLE['unicodePwd']: if attr['AttrVal']['valCount'] > 0: encryptedUnicodePwd = b''.join( attr['AttrVal']['pAVal'][0]['pVal']) encryptedNTHash = drsuapi.DecryptAttributeValue( self.dce.get_session_key(), encryptedUnicodePwd) us.nt_hash = drsuapi.removeDESLayer(encryptedNTHash, rid) else: us.nt_hash = bytes.fromhex( '31d6cfe0d16ae931b73c59d7e0c089c0') elif attId == LOOKUP_TABLE['userPrincipalName']: if attr['AttrVal']['valCount'] > 0: try: us.domain = b''.join( attr['AttrVal']['pAVal'][0]['pVal']).decode( 'utf-16le').split('@')[-1] except: us.domain = None else: us.domain = None elif attId == LOOKUP_TABLE['sAMAccountName']: if attr['AttrVal']['valCount'] > 0: try: us.username = b''.join(attr['AttrVal']['pAVal'][0] ['pVal']).decode('utf-16le') except Exception as e: logger.error('Cannot get sAMAccountName for %s' % record['pmsgOut'][replyVersion]['pNC'] ['StringName'][:-1]) us.username = '******' else: logger.error('Cannot get sAMAccountName for %s' % record['pmsgOut'][replyVersion]['pNC'] ['StringName'][:-1]) us.username = '******' elif attId == LOOKUP_TABLE['objectSid']: if attr['AttrVal']['valCount'] > 0: us.object_sid = SID.from_bytes(b''.join( attr['AttrVal']['pAVal'][0]['pVal'])) else: logger.error('Cannot get objectSid for %s' % record['pmsgOut'][replyVersion]['pNC'] ['StringName'][:-1]) us.object_sid = rid elif attId == LOOKUP_TABLE['pwdLastSet']: if attr['AttrVal']['valCount'] > 0: try: us.pwd_last_set = FILETIME.from_bytes( b''.join(attr['AttrVal']['pAVal'][0] ['pVal'])).datetime.isoformat() except Exception as e: logger.error('Cannot get pwdLastSet for %s' % record['pmsgOut'][replyVersion]['pNC'] ['StringName'][:-1]) us.pwd_last_set = None elif attId == LOOKUP_TABLE['userAccountControl']: if attr['AttrVal']['valCount'] > 0: us.user_account_status = int.from_bytes(b''.join( attr['AttrVal']['pAVal'][0]['pVal']), 'little', signed=False) else: us.user_account_status = None if attId == LOOKUP_TABLE['lmPwdHistory']: if attr['AttrVal']['valCount'] > 0: encryptedLMHistory = b''.join( attr['AttrVal']['pAVal'][0]['pVal']) tmpLMHistory = drsuapi.DecryptAttributeValue( self.dce.get_session_key(), encryptedLMHistory) for i in range(0, len(tmpLMHistory) // 16): LMHashHistory = drsuapi.removeDESLayer( tmpLMHistory[i * 16:(i + 1) * 16], rid) us.lm_history.append(LMHashHistory) else: logger.debug('No lmPwdHistory for user %s' % record['pmsgOut'][replyVersion]['pNC'] ['StringName'][:-1]) elif attId == LOOKUP_TABLE['ntPwdHistory']: if attr['AttrVal']['valCount'] > 0: encryptedNTHistory = b''.join( attr['AttrVal']['pAVal'][0]['pVal']) tmpNTHistory = drsuapi.DecryptAttributeValue( self.dce.get_session_key(), encryptedNTHistory) for i in range(0, len(tmpNTHistory) // 16): NTHashHistory = drsuapi.removeDESLayer( tmpNTHistory[i * 16:(i + 1) * 16], rid) us.nt_history.append(NTHashHistory) else: logger.debug('No ntPwdHistory for user %s' % record['pmsgOut'][replyVersion]['pNC'] ['StringName'][:-1]) elif attId == LOOKUP_TABLE['supplementalCredentials']: if attr['AttrVal']['valCount'] > 0: blob = b''.join(attr['AttrVal']['pAVal'][0]['pVal']) supplementalCredentials = drsuapi.DecryptAttributeValue( self.dce.get_session_key(), blob) if len(supplementalCredentials) < 24: supplementalCredentials = None else: try: user_properties = samr.USER_PROPERTIES( supplementalCredentials) except Exception as e: # On some old w2k3 there might be user properties that don't # match [MS-SAMR] structure, discarding them pass if user_properties is not None: propertiesData = user_properties['UserProperties'] for propertyCount in range(user_properties['PropertyCount']): userProperty = samr.USER_PROPERTY(propertiesData) propertiesData = propertiesData[len(userProperty):] # For now, we will only process Newer Kerberos Keys and CLEARTEXT if userProperty['PropertyName'].decode( 'utf-16le') == 'Primary:Kerberos-Newer-Keys': propertyValueBuffer = bytes.fromhex( userProperty['PropertyValue'].decode()) kerbStoredCredentialNew = samr.KERB_STORED_CREDENTIAL_NEW( propertyValueBuffer) data = kerbStoredCredentialNew['Buffer'] for credential in range( kerbStoredCredentialNew['CredentialCount']): keyDataNew = samr.KERB_KEY_DATA_NEW(data) data = data[len(keyDataNew):] keyValue = propertyValueBuffer[ keyDataNew['KeyOffset']:][:keyDataNew['KeyLength']] if keyDataNew['KeyType'] in self.KERBEROS_TYPE: answer = ( self.KERBEROS_TYPE[keyDataNew['KeyType']], keyValue) else: answer = (hex(keyDataNew['KeyType']), keyValue) # We're just storing the keys, not printing them, to make the output more readable # This is kind of ugly... but it's what I came up with tonight to get an ordered # set :P. Better ideas welcomed ;) us.kerberos_keys.append(answer) elif userProperty['PropertyName'].decode( 'utf-16le') == 'Primary:CLEARTEXT': # [MS-SAMR] 3.1.1.8.11.5 Primary:CLEARTEXT Property # This credential type is the cleartext password. The value format is the UTF-16 encoded cleartext password. # SkelSec: well, almost. actually the property is the hex-encoded bytes of an UTF-16LE encoded plaintext string encoded_pw = bytes.fromhex( userProperty['PropertyValue'].decode('ascii')) try: answer = encoded_pw.decode('utf-16le') except UnicodeDecodeError: # This could be because we're decoding a machine password. Printing it hex answer = encoded_pw.decode('utf-8') us.cleartext_pwds.append(answer) return us, None