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
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
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)
Beispiel #5
0
    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