def getPage(self, pageNum): LOG.debug("Trying to fetch page %d (0x%x)" % (pageNum, (pageNum+1)*self.__pageSize)) self.__DB.seek((pageNum+1)*self.__pageSize, 0) data = self.__DB.read(self.__pageSize) while len(data) < self.__pageSize: remaining = self.__pageSize - len(data) data += self.__DB.read(remaining) # Special case for the first page if pageNum <= 0: return data else: return ESENT_PAGE(self.__DBHeader, data)
def getPage(self, pageNum): LOG.debug("Trying to fetch page %d (0x%x)" % (pageNum, (pageNum + 1) * self.__pageSize)) self.__DB.seek((pageNum + 1) * self.__pageSize, 0) data = self.__DB.read(self.__pageSize) while len(data) < self.__pageSize: remaining = self.__pageSize - len(data) data += self.__DB.read(remaining) # Special case for the first page if pageNum <= 0: return data else: return ESENT_PAGE(self.__DBHeader, data)
def __getBlock(self, offset): self.fd.seek(4096 + offset, 0) sizeBytes = self.fd.read(4) data = sizeBytes + self.fd.read(unpack("<l", sizeBytes)[0] * -1 - 4) if len(data) == 0: return None else: block = REG_HBINBLOCK(data) if StructMappings.has_key(block["Data"][:2]): return StructMappings[block["Data"][:2]](block["Data"]) else: LOG.debug("Unknown type 0x%s" % block["Data"][:2]) return block return None
def __getBlock(self, offset): self.fd.seek(4096 + offset, 0) sizeBytes = self.fd.read(4) data = sizeBytes + self.fd.read(unpack('<l', sizeBytes)[0] * -1 - 4) if len(data) == 0: return None else: block = REG_HBINBLOCK(data) if StructMappings.has_key(block['Data'][:2]): return StructMappings[block['Data'][:2]](block['Data']) else: LOG.debug("Unknown type 0x%s" % block['Data'][:2]) return block return None
def getKerberosType1(username, password, domain, lmhash, nthash, aesKey='', TGT=None, TGS=None, targetName='', kdcHost=None, useCache=True): if TGT is None and TGS is None: if useCache is True: try: ccache = CCache.loadFile(os.getenv('KRB5CCNAME')) except Exception, e: # No cache present pass else: LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME')) principal = 'host/%s@%s' % (targetName.upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is None: # Let's try for the TGT and go from there principal = 'krbtgt/%s@%s' % (domain.upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is not None: TGT = creds.toTGT() LOG.debug('Using TGT from cache') else: LOG.debug("No valid credentials found in cache. ") else: TGS = creds.toTGS()
def mountDB(self): LOG.debug("Mounting DB...") if self.__isRemote is True: self.__DB = self.__fileName self.__DB.open() else: self.__DB = open(self.__fileName,"rb") mainHeader = self.getPage(-1) self.__DBHeader = ESENT_DB_HEADER(mainHeader) self.__pageSize = self.__DBHeader['PageSize'] self.__DB.seek(0,2) self.__totalPages = (self.__DB.tell() / self.__pageSize) -2 LOG.debug("Database Version:0x%x, Revision:0x%x"% (self.__DBHeader['Version'], self.__DBHeader['FileFormatRevision'])) LOG.debug("Page Size: %d" % self.__pageSize) LOG.debug("Total Pages in file: %d" % self.__totalPages) self.parseCatalog(CATALOG_PAGE_NUMBER)
def mountDB(self): LOG.debug("Mounting DB...") if self.__isRemote is True: self.__DB = self.__fileName self.__DB.open() else: self.__DB = open(self.__fileName, "rb") mainHeader = self.getPage(-1) self.__DBHeader = ESENT_DB_HEADER(mainHeader) self.__pageSize = self.__DBHeader['PageSize'] self.__DB.seek(0, 2) self.__totalPages = (self.__DB.tell() / self.__pageSize) - 2 LOG.debug("Database Version:0x%x, Revision:0x%x" % (self.__DBHeader['Version'], self.__DBHeader['FileFormatRevision'])) LOG.debug("Page Size: %d" % self.__pageSize) LOG.debug("Total Pages in file: %d" % self.__totalPages) self.parseCatalog(CATALOG_PAGE_NUMBER)
def getKerberosType1(username, password, domain, lmhash, nthash, aesKey='', TGT = None, TGS = None, targetName='', kdcHost = None, useCache = True): if TGT is None and TGS is None: if useCache is True: try: ccache = CCache.loadFile(os.getenv('KRB5CCNAME')) except Exception, e: # No cache present pass else: LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME')) principal = 'host/%s@%s' % (targetName.upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is None: # Let's try for the TGT and go from there principal = 'krbtgt/%s@%s' % (domain.upper(),domain.upper()) creds = ccache.getCredential(principal) if creds is not None: TGT = creds.toTGT() LOG.debug('Using TGT from cache') else: LOG.debug("No valid credentials found in cache. ") else: TGS = creds.toTGS()
try: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT( userName, password, domain, lmhash, nthash, aesKey, kdcHost) except KerberosError, e: if e.getErrorCode( ) == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: # We might face this if the target does not support AES # So, if that's the case we'll force using RC4 by converting # the password to lm/nt hashes and hope for the best. If that's already # done, byebye. if lmhash is '' and nthash is '' and ( aesKey is '' or aesKey is None ) and TGT is None and TGS is None: from mitmflib.impacket.ntlm import compute_lmhash, compute_nthash LOG.debug( 'Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4') lmhash = compute_lmhash(password) nthash = compute_nthash(password) continue else: raise else: raise else: tgt = TGT['KDC_REP'] cipher = TGT['cipher'] sessionKey = TGT['sessionKey'] # Now that we have the TGT, we should ask for a TGS for cifs
# First of all, we need to get a TGT for the user userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) while True: if TGT is None: if TGS is None: try: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost) except KerberosError, e: if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: # We might face this if the target does not support AES # So, if that's the case we'll force using RC4 by converting # the password to lm/nt hashes and hope for the best. If that's already # done, byebye. if lmhash is '' and nthash is '' and (aesKey is '' or aesKey is None) and TGT is None and TGS is None: from mitmflib.impacket.ntlm import compute_lmhash, compute_nthash LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4') lmhash = compute_lmhash(password) nthash = compute_nthash(password) continue else: raise else: raise else: tgt = TGT['KDC_REP'] cipher = TGT['cipher'] sessionKey = TGT['sessionKey'] # Now that we have the TGT, we should ask for a TGS for cifs
def __tagToRecord(self, cursor, tag): # So my brain doesn't forget, the data record is composed of: # Header # Fixed Size Data (ID < 127) # The easiest to parse. Their size is fixed in the record. You can get its size # from the Column Record, field SpaceUsage # Variable Size Data (127 < ID < 255) # At VariableSizeOffset you get an array of two bytes per variable entry, pointing # to the length of the value. Values start at: # numEntries = LastVariableDataType - 127 # VariableSizeOffset + numEntries * 2 (bytes) # Tagged Data ( > 255 ) # After the Variable Size Value, there's more data for the tagged values. # Right at the beggining there's another array (taggedItems), pointing to the # values, size. # # The interesting thing about this DB records is there's no need for all the columns to be there, hence # saving space. That's why I got over all the columns, and if I find data (of any type), i assign it. If # not, the column's empty. # # There are a lot of caveats in the code, so take your time to explore it. # # ToDo: Better complete this description # record = OrderedDict() taggedItems = OrderedDict() taggedItemsParsed = False dataDefinitionHeader = ESENT_DATA_DEFINITION_HEADER(tag) #dataDefinitionHeader.dump() variableDataBytesProcessed = (dataDefinitionHeader['LastVariableDataType'] - 127) * 2 prevItemLen = 0 tagLen = len(tag) fixedSizeOffset = len(dataDefinitionHeader) variableSizeOffset = dataDefinitionHeader['VariableSizeOffset'] columns = cursor['TableData']['Columns'] for column in columns.keys(): columnRecord = columns[column]['Record'] #columnRecord.dump() if columnRecord['Identifier'] <= dataDefinitionHeader['LastFixedSize']: # Fixed Size column data type, still available data record[column] = tag[fixedSizeOffset:][:columnRecord['SpaceUsage']] fixedSizeOffset += columnRecord['SpaceUsage'] elif 127 < columnRecord['Identifier'] <= dataDefinitionHeader['LastVariableDataType']: # Variable data type index = columnRecord['Identifier'] - 127 - 1 itemLen = unpack('<H',tag[variableSizeOffset+index*2:][:2])[0] if itemLen & 0x8000: # Empty item itemLen = prevItemLen record[column] = None else: itemValue = tag[variableSizeOffset+variableDataBytesProcessed:][:itemLen-prevItemLen] record[column] = itemValue #if columnRecord['Identifier'] <= dataDefinitionHeader['LastVariableDataType']: variableDataBytesProcessed +=itemLen-prevItemLen prevItemLen = itemLen elif columnRecord['Identifier'] > 255: # Have we parsed the tagged items already? if taggedItemsParsed is False and (variableDataBytesProcessed+variableSizeOffset) < tagLen: index = variableDataBytesProcessed+variableSizeOffset #hexdump(tag[index:]) endOfVS = self.__pageSize firstOffsetTag = (unpack('<H', tag[index+2:][:2])[0] & 0x3fff) + variableDataBytesProcessed+variableSizeOffset while True: taggedIdentifier = unpack('<H', tag[index:][:2])[0] index += 2 taggedOffset = (unpack('<H', tag[index:][:2])[0] & 0x3fff) # As of Windows 7 and later ( version 0x620 revision 0x11) the # tagged data type flags are always present if self.__DBHeader['Version'] == 0x620 and self.__DBHeader['FileFormatRevision'] >= 17 and self.__DBHeader['PageSize'] > 8192: flagsPresent = 1 else: flagsPresent = (unpack('<H', tag[index:][:2])[0] & 0x4000) index += 2 if taggedOffset < endOfVS: endOfVS = taggedOffset taggedItems[taggedIdentifier] = (taggedOffset, tagLen, flagsPresent) #print "ID: %d, Offset:%d, firstOffset:%d, index:%d, flag: 0x%x" % (taggedIdentifier, taggedOffset,firstOffsetTag,index, flagsPresent) if index >= firstOffsetTag: # We reached the end of the variable size array break # Calculate length of variable items # Ugly.. should be redone prevKey = taggedItems.keys()[0] for i in range(1,len(taggedItems)): offset0, length, flags = taggedItems[prevKey] offset, _, _ = taggedItems.items()[i][1] taggedItems[prevKey] = (offset0, offset-offset0, flags) #print "ID: %d, Offset: %d, Len: %d, flags: %d" % (prevKey, offset0, offset-offset0, flags) prevKey = taggedItems.keys()[i] taggedItemsParsed = True # Tagged data type if taggedItems.has_key(columnRecord['Identifier']): offsetItem = variableDataBytesProcessed + variableSizeOffset + taggedItems[columnRecord['Identifier']][0] itemSize = taggedItems[columnRecord['Identifier']][1] # If item have flags, we should skip them if taggedItems[columnRecord['Identifier']][2] > 0: itemFlag = ord(tag[offsetItem:offsetItem+1]) offsetItem += 1 itemSize -= 1 else: itemFlag = 0 #print "ID: %d, itemFlag: 0x%x" %( columnRecord['Identifier'], itemFlag) if itemFlag & (TAGGED_DATA_TYPE_COMPRESSED ): LOG.error('Unsupported tag column: %s, flag:0x%x' % (column, itemFlag)) record[column] = None elif itemFlag & TAGGED_DATA_TYPE_MULTI_VALUE: # ToDo: Parse multi-values properly LOG.debug('Multivalue detected in column %s, returning raw results' % (column)) record[column] = (hexlify(tag[offsetItem:][:itemSize]),) else: record[column] = tag[offsetItem:][:itemSize] else: record[column] = None else: record[column] = None # If we understand the data type, we unpack it and cast it accordingly # otherwise, we just encode it in hex if type(record[column]) is tuple: # A multi value data, we won't decode it, just leave it this way record[column] = record[column][0] elif columnRecord['ColumnType'] == JET_coltypText or columnRecord['ColumnType'] == JET_coltypLongText: # Let's handle strings if record[column] is not None: if columnRecord['CodePage'] not in StringCodePages: LOG.error('Unknown codepage 0x%x'% columnRecord['CodePage']) raise stringDecoder = StringCodePages[columnRecord['CodePage']] record[column] = record[column].decode(stringDecoder) else: unpackData = ColumnTypeSize[columnRecord['ColumnType']] if record[column] is not None: if unpackData is None: record[column] = hexlify(record[column]) else: unpackStr = unpackData[1] unpackSize = unpackData[0] record[column] = unpack(unpackStr, record[column])[0] return record
def readHeader(self): LOG.debug("Reading Boot Sector for %s" % self.__volumeName)
def __tagToRecord(self, cursor, tag): # So my brain doesn't forget, the data record is composed of: # Header # Fixed Size Data (ID < 127) # The easiest to parse. Their size is fixed in the record. You can get its size # from the Column Record, field SpaceUsage # Variable Size Data (127 < ID < 255) # At VariableSizeOffset you get an array of two bytes per variable entry, pointing # to the length of the value. Values start at: # numEntries = LastVariableDataType - 127 # VariableSizeOffset + numEntries * 2 (bytes) # Tagged Data ( > 255 ) # After the Variable Size Value, there's more data for the tagged values. # Right at the beggining there's another array (taggedItems), pointing to the # values, size. # # The interesting thing about this DB records is there's no need for all the columns to be there, hence # saving space. That's why I got over all the columns, and if I find data (of any type), i assign it. If # not, the column's empty. # # There are a lot of caveats in the code, so take your time to explore it. # # ToDo: Better complete this description # record = OrderedDict() taggedItems = OrderedDict() taggedItemsParsed = False dataDefinitionHeader = ESENT_DATA_DEFINITION_HEADER(tag) #dataDefinitionHeader.dump() variableDataBytesProcessed = ( dataDefinitionHeader['LastVariableDataType'] - 127) * 2 prevItemLen = 0 tagLen = len(tag) fixedSizeOffset = len(dataDefinitionHeader) variableSizeOffset = dataDefinitionHeader['VariableSizeOffset'] columns = cursor['TableData']['Columns'] for column in columns.keys(): columnRecord = columns[column]['Record'] #columnRecord.dump() if columnRecord['Identifier'] <= dataDefinitionHeader[ 'LastFixedSize']: # Fixed Size column data type, still available data record[column] = tag[ fixedSizeOffset:][:columnRecord['SpaceUsage']] fixedSizeOffset += columnRecord['SpaceUsage'] elif 127 < columnRecord['Identifier'] <= dataDefinitionHeader[ 'LastVariableDataType']: # Variable data type index = columnRecord['Identifier'] - 127 - 1 itemLen = unpack('<H', tag[variableSizeOffset + index * 2:][:2])[0] if itemLen & 0x8000: # Empty item itemLen = prevItemLen record[column] = None else: itemValue = tag[variableSizeOffset + variableDataBytesProcessed:][:itemLen - prevItemLen] record[column] = itemValue #if columnRecord['Identifier'] <= dataDefinitionHeader['LastVariableDataType']: variableDataBytesProcessed += itemLen - prevItemLen prevItemLen = itemLen elif columnRecord['Identifier'] > 255: # Have we parsed the tagged items already? if taggedItemsParsed is False and ( variableDataBytesProcessed + variableSizeOffset) < tagLen: index = variableDataBytesProcessed + variableSizeOffset #hexdump(tag[index:]) endOfVS = self.__pageSize firstOffsetTag = ( unpack('<H', tag[index + 2:][:2])[0] & 0x3fff ) + variableDataBytesProcessed + variableSizeOffset while True: taggedIdentifier = unpack('<H', tag[index:][:2])[0] index += 2 taggedOffset = (unpack('<H', tag[index:][:2])[0] & 0x3fff) # As of Windows 7 and later ( version 0x620 revision 0x11) the # tagged data type flags are always present if self.__DBHeader['Version'] == 0x620 and self.__DBHeader[ 'FileFormatRevision'] >= 17 and self.__DBHeader[ 'PageSize'] > 8192: flagsPresent = 1 else: flagsPresent = (unpack('<H', tag[index:][:2])[0] & 0x4000) index += 2 if taggedOffset < endOfVS: endOfVS = taggedOffset taggedItems[taggedIdentifier] = (taggedOffset, tagLen, flagsPresent) #print "ID: %d, Offset:%d, firstOffset:%d, index:%d, flag: 0x%x" % (taggedIdentifier, taggedOffset,firstOffsetTag,index, flagsPresent) if index >= firstOffsetTag: # We reached the end of the variable size array break # Calculate length of variable items # Ugly.. should be redone prevKey = taggedItems.keys()[0] for i in range(1, len(taggedItems)): offset0, length, flags = taggedItems[prevKey] offset, _, _ = taggedItems.items()[i][1] taggedItems[prevKey] = (offset0, offset - offset0, flags) #print "ID: %d, Offset: %d, Len: %d, flags: %d" % (prevKey, offset0, offset-offset0, flags) prevKey = taggedItems.keys()[i] taggedItemsParsed = True # Tagged data type if taggedItems.has_key(columnRecord['Identifier']): offsetItem = variableDataBytesProcessed + variableSizeOffset + taggedItems[ columnRecord['Identifier']][0] itemSize = taggedItems[columnRecord['Identifier']][1] # If item have flags, we should skip them if taggedItems[columnRecord['Identifier']][2] > 0: itemFlag = ord(tag[offsetItem:offsetItem + 1]) offsetItem += 1 itemSize -= 1 else: itemFlag = 0 #print "ID: %d, itemFlag: 0x%x" %( columnRecord['Identifier'], itemFlag) if itemFlag & (TAGGED_DATA_TYPE_COMPRESSED): LOG.error('Unsupported tag column: %s, flag:0x%x' % (column, itemFlag)) record[column] = None elif itemFlag & TAGGED_DATA_TYPE_MULTI_VALUE: # ToDo: Parse multi-values properly LOG.debug( 'Multivalue detected in column %s, returning raw results' % (column)) record[column] = (hexlify( tag[offsetItem:][:itemSize]), ) else: record[column] = tag[offsetItem:][:itemSize] else: record[column] = None else: record[column] = None # If we understand the data type, we unpack it and cast it accordingly # otherwise, we just encode it in hex if type(record[column]) is tuple: # A multi value data, we won't decode it, just leave it this way record[column] = record[column][0] elif columnRecord['ColumnType'] == JET_coltypText or columnRecord[ 'ColumnType'] == JET_coltypLongText: # Let's handle strings if record[column] is not None: if columnRecord['CodePage'] not in StringCodePages: LOG.error('Unknown codepage 0x%x' % columnRecord['CodePage']) raise stringDecoder = StringCodePages[columnRecord['CodePage']] record[column] = record[column].decode(stringDecoder) else: unpackData = ColumnTypeSize[columnRecord['ColumnType']] if record[column] is not None: if unpackData is None: record[column] = hexlify(record[column]) else: unpackStr = unpackData[1] unpackSize = unpackData[0] record[column] = unpack(unpackStr, record[column])[0] return record
def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey = '', kdcHost=None, TGT=None, TGS=None, useCache = True): """ logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported. :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for (required) :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho) :param struct TGT: If there's a TGT available, send the structure here and it will be used :param struct TGS: same for TGS. See smb3.py for the format :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False :return: None, raises a Session Error if error. """ import os from mitmflib.impacket.krb5.ccache import CCache from mitmflib.impacket.krb5.kerberosv5 import KerberosError from mitmflib.impacket.krb5 import constants from mitmflib.impacket.ntlm import compute_lmhash, compute_nthash if TGT is not None or TGS is not None: useCache = False if useCache is True: try: ccache = CCache.loadFile(os.getenv('KRB5CCNAME')) except: # No cache present pass else: LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME')) principal = 'cifs/%s@%s' % (self.getRemoteHost().upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is None: # Let's try for the TGT and go from there principal = 'krbtgt/%s@%s' % (domain.upper(),domain.upper()) creds = ccache.getCredential(principal) if creds is not None: TGT = creds.toTGT() LOG.debug('Using TGT from cache') else: LOG.debug("No valid credentials found in cache. ") else: TGS = creds.toTGS() LOG.debug('Using TGS from cache') while True: try: if self.getDialect() == smb.SMB_DIALECT: return self._SMBConnection.kerberos_login(user, password, domain, lmhash, nthash, aesKey, kdcHost, TGT, TGS) return self._SMBConnection.kerberosLogin(user, password, domain, lmhash, nthash, aesKey, kdcHost, TGT, TGS) except (smb.SessionError, smb3.SessionError), e: raise SessionError(e.get_error_code()) except KerberosError, e: if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: # We might face this if the target does not support AES # So, if that's the case we'll force using RC4 by converting # the password to lm/nt hashes and hope for the best. If that's already # done, byebye. if lmhash is '' and nthash is '' and (aesKey is '' or aesKey is None) and TGT is None and TGS is None: from mitmflib.impacket.ntlm import compute_lmhash, compute_nthash lmhash = compute_lmhash(password) nthash = compute_nthash(password) else: raise e else: raise e