def __addItem(self, entry): dataDefinitionHeader = ESENT_DATA_DEFINITION_HEADER(entry['EntryData']) catalogEntry = ESENT_CATALOG_DATA_DEFINITION_ENTRY( entry['EntryData'][len(dataDefinitionHeader):]) itemName = self.__parseItemName(entry) if catalogEntry['Type'] == CATALOG_TYPE_TABLE: self.__tables[itemName] = OrderedDict() self.__tables[itemName]['TableEntry'] = entry self.__tables[itemName]['Columns'] = OrderedDict() self.__tables[itemName]['Indexes'] = OrderedDict() self.__tables[itemName]['LongValues'] = OrderedDict() self.__currentTable = itemName elif catalogEntry['Type'] == CATALOG_TYPE_COLUMN: self.__tables[self.__currentTable]['Columns'][itemName] = entry self.__tables[self.__currentTable]['Columns'][itemName][ 'Header'] = dataDefinitionHeader self.__tables[self.__currentTable]['Columns'][itemName][ 'Record'] = catalogEntry elif catalogEntry['Type'] == CATALOG_TYPE_INDEX: self.__tables[self.__currentTable]['Indexes'][itemName] = entry elif catalogEntry['Type'] == CATALOG_TYPE_LONG_VALUE: self.__addLongValue(entry) else: LOG.error('Unknown type 0x%x' % catalogEntry['Type']) raise
def get_address(self): address = get_bytes(self.buffer, 5, self.get_address_length()) if self.get_protocol() == AddressDetails.PROTOCOL_IP: return socket.inet_ntoa(address) else: LOG.error("Address not IP") return address
def getTag(self, tagNum): if self.record['FirstAvailablePageTag'] < tagNum: LOG.error('Trying to grab an unknown tag 0x%x' % tagNum) raise tags = self.data[-4*self.record['FirstAvailablePageTag']:] baseOffset = len(self.record) for i in range(tagNum): tags = tags[:-4] tag = tags[-4:] if self.__DBHeader['Version'] == 0x620 and self.__DBHeader['FileFormatRevision'] >= 17 and self.__DBHeader['PageSize'] > 8192: valueSize = unpack('<H', tag[:2])[0] & 0x7fff valueOffset = unpack('<H',tag[2:])[0] & 0x7fff tmpData = list(self.data[baseOffset+valueOffset:][:valueSize]) pageFlags = ord(tmpData[1]) >> 5 tmpData[1] = chr(ord(tmpData[1]) & 0x1f) tagData = "".join(tmpData) else: valueSize = unpack('<H', tag[:2])[0] & 0x1fff pageFlags = (unpack('<H', tag[2:])[0] & 0xe000) >> 13 valueOffset = unpack('<H',tag[2:])[0] & 0x1fff tagData = self.data[baseOffset+valueOffset:][:valueSize] #return pageFlags, self.data[baseOffset+valueOffset:][:valueSize] return pageFlags, tagData
def getTag(self, tagNum): if self.record['FirstAvailablePageTag'] < tagNum: LOG.error('Trying to grab an unknown tag 0x%x' % tagNum) raise tags = self.data[-4 * self.record['FirstAvailablePageTag']:] baseOffset = len(self.record) for i in range(tagNum): tags = tags[:-4] tag = tags[-4:] if self.__DBHeader['Version'] == 0x620 and self.__DBHeader[ 'FileFormatRevision'] >= 17 and self.__DBHeader[ 'PageSize'] > 8192: valueSize = unpack('<H', tag[:2])[0] & 0x7fff valueOffset = unpack('<H', tag[2:])[0] & 0x7fff tmpData = list(self.data[baseOffset + valueOffset:][:valueSize]) pageFlags = ord(tmpData[1]) >> 5 tmpData[1] = chr(ord(tmpData[1]) & 0x1f) tagData = "".join(tmpData) else: valueSize = unpack('<H', tag[:2])[0] & 0x1fff pageFlags = (unpack('<H', tag[2:])[0] & 0xe000) >> 13 valueOffset = unpack('<H', tag[2:])[0] & 0x1fff tagData = self.data[baseOffset + valueOffset:][:valueSize] #return pageFlags, self.data[baseOffset+valueOffset:][:valueSize] return pageFlags, tagData
def createService(self, handle, share, path): LOG.info("Creating service %s on %s....." % (self.__service_name, self.connection.getRemoteHost())) # First we try to open the service in case it exists. If it does, we remove it. try: resp = scmr.hROpenServiceW(self.rpcsvc, handle, self.__service_name+'\x00') except Exception, e: if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') >= 0: # We're good, pass the exception pass else: raise e
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 createService(self, handle, share, path): LOG.info("Creating service %s on %s....." % (self.__service_name, self.connection.getRemoteHost())) # First we try to open the service in case it exists. If it does, we remove it. try: resp = scmr.hROpenServiceW(self.rpcsvc, handle, self.__service_name + '\x00') except Exception, e: if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') >= 0: # We're good, pass the exception pass else: raise e
def getShares(self): # Setup up a DCE SMBTransport with the connection already in place LOG.info("Requesting shares on %s....." % (self.connection.getRemoteHost())) try: self._rpctransport = transport.SMBTransport(self.connection.getRemoteHost(), self.connection.getRemoteHost(),filename = r'\srvsvc', smb_connection = self.connection) dce_srvs = self._rpctransport.get_dce_rpc() dce_srvs.connect() dce_srvs.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrShareEnum(dce_srvs, 1) return resp['InfoStruct']['ShareInfo']['Level1'] except: LOG.critical("Error requesting shares on %s, aborting....." % (self.connection.getRemoteHost())) raise
def openSvcManager(self): LOG.info("Opening SVCManager on %s....." % self.connection.getRemoteHost()) # Setup up a DCE SMBTransport with the connection already in place self._rpctransport = transport.SMBTransport(self.connection.getRemoteHost(), self.connection.getRemoteHost(),filename = r'\svcctl', smb_connection = self.connection) self.rpcsvc = self._rpctransport.get_dce_rpc() self.rpcsvc.connect() self.rpcsvc.bind(scmr.MSRPC_UUID_SCMR) try: resp = scmr.hROpenSCManagerW(self.rpcsvc) except: LOG.critical("Error opening SVCManager on %s....." % self.connection.getRemoteHost()) raise Exception('Unable to open SVCManager') else: return resp['lpScHandle']
def findWritableShare(self, shares): # Check we can write a file on the shares, stop in the first one for i in shares['Buffer']: if i['shi1_type'] == srvs.STYPE_DISKTREE or i['shi1_type'] == srvs.STYPE_SPECIAL: share = i['shi1_netname'][:-1] try: self.connection.createDirectory(share,'BETO') except: # Can't create, pass LOG.critical("share '%s' is not writable." % share) pass else: LOG.info('Found writable share %s' % share) self.connection.deleteDirectory(share,'BETO') return str(share) return None
def copy_file(self, src, tree, dst): LOG.info("Uploading file %s" % dst) if isinstance(src, str): # We have a filename fh = open(src, 'rb') else: # We have a class instance, it must have a read method fh = src f = dst pathname = string.replace(f,'/','\\') try: self.connection.putFile(tree, pathname, fh.read) except: LOG.critical("Error uploading file %s, aborting....." % dst) raise fh.close()
def copy_file(self, src, tree, dst): LOG.info("Uploading file %s" % dst) if isinstance(src, str): # We have a filename fh = open(src, 'rb') else: # We have a class instance, it must have a read method fh = src f = dst pathname = string.replace(f, '/', '\\') try: self.connection.putFile(tree, pathname, fh.read) except: LOG.critical("Error uploading file %s, aborting....." % dst) raise fh.close()
def findWritableShare(self, shares): # Check we can write a file on the shares, stop in the first one for i in shares['Buffer']: if i['shi1_type'] == srvs.STYPE_DISKTREE or i[ 'shi1_type'] == srvs.STYPE_SPECIAL: share = i['shi1_netname'][:-1] try: self.connection.createDirectory(share, 'BETO') except: # Can't create, pass LOG.critical("share '%s' is not writable." % share) pass else: LOG.info('Found writable share %s' % share) self.connection.deleteDirectory(share, 'BETO') return str(share) return None
def __init__(self, hive, isRemote=False): self.__hive = hive if isRemote is True: self.fd = self.__hive self.__hive.open() else: self.fd = open(hive, "rb") data = self.fd.read(4096) self.__regf = REG_REGF(data) self.indent = "" self.rootKey = self.__findRootKey() if self.rootKey is None: LOG.error("Can't find root key!") elif self.__regf["MajorVersion"] != 1 and self.__regf["MinorVersion"] > 5: LOG.warning( "Unsupported version (%d.%d) - things might not work!" % (self.__regf["MajorVersion"], self.__regf["MinorVersion"]) )
def __init__(self, hive, isRemote=False): self.__hive = hive if isRemote is True: self.fd = self.__hive self.__hive.open() else: self.fd = open(hive, 'rb') data = self.fd.read(4096) self.__regf = REG_REGF(data) self.indent = '' self.rootKey = self.__findRootKey() if self.rootKey is None: LOG.error("Can't find root key!") elif self.__regf['MajorVersion'] != 1 and self.__regf[ 'MinorVersion'] > 5: LOG.warning( "Unsupported version (%d.%d) - things might not work!" % (self.__regf['MajorVersion'], self.__regf['MinorVersion']))
def getShares(self): # Setup up a DCE SMBTransport with the connection already in place LOG.info("Requesting shares on %s....." % (self.connection.getRemoteHost())) try: self._rpctransport = transport.SMBTransport( self.connection.getRemoteHost(), self.connection.getRemoteHost(), filename=r'\srvsvc', smb_connection=self.connection) dce_srvs = self._rpctransport.get_dce_rpc() dce_srvs.connect() dce_srvs.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrShareEnum(dce_srvs, 1) return resp['InfoStruct']['ShareInfo']['Level1'] except: LOG.critical("Error requesting shares on %s, aborting....." % (self.connection.getRemoteHost())) raise
def openSvcManager(self): LOG.info("Opening SVCManager on %s....." % self.connection.getRemoteHost()) # Setup up a DCE SMBTransport with the connection already in place self._rpctransport = transport.SMBTransport( self.connection.getRemoteHost(), self.connection.getRemoteHost(), filename=r'\svcctl', smb_connection=self.connection) self.rpcsvc = self._rpctransport.get_dce_rpc() self.rpcsvc.connect() self.rpcsvc.bind(scmr.MSRPC_UUID_SCMR) try: resp = scmr.hROpenSCManagerW(self.rpcsvc) except: LOG.critical("Error opening SVCManager on %s....." % self.connection.getRemoteHost()) raise Exception('Unable to open SVCManager') else: return resp['lpScHandle']
def __compareHash(self, magic, hashData, key): if magic == "lf": hashRec = REG_HASH(hashData) if hashRec["KeyName"].strip("\x00") == key[:4]: return hashRec["OffsetNk"] elif magic == "lh": hashRec = REG_HASH(hashData) if unpack("<L", hashRec["KeyName"])[0] == self.__getLhHash(key): return hashRec["OffsetNk"] elif magic == "ri": # Special case here, don't know exactly why, an ri pointing to a NK :-o offset = unpack("<L", hashData[:4])[0] nk = self.__getBlock(offset) if nk["KeyName"] == key: return offset else: LOG.critical("UNKNOWN Magic %s" % magic) sys.exit(1) return None
def __compareHash(self, magic, hashData, key): if magic == 'lf': hashRec = REG_HASH(hashData) if hashRec['KeyName'].strip('\x00') == key[:4]: return hashRec['OffsetNk'] elif magic == 'lh': hashRec = REG_HASH(hashData) if unpack('<L', hashRec['KeyName'])[0] == self.__getLhHash(key): return hashRec['OffsetNk'] elif magic == 'ri': # Special case here, don't know exactly why, an ri pointing to a NK :-o offset = unpack('<L', hashData[:4])[0] nk = self.__getBlock(offset) if nk['KeyName'] == key: return offset else: LOG.critical("UNKNOWN Magic %s" % magic) sys.exit(1) 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 openFile(self, treeId, pathName, desiredAccess = FILE_READ_DATA | FILE_WRITE_DATA, shareMode = FILE_SHARE_READ, creationOption = FILE_NON_DIRECTORY_FILE, creationDisposition = FILE_OPEN, fileAttributes = FILE_ATTRIBUTE_NORMAL, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None): """ opens a remote file :param HANDLE treeId: a valid handle for the share where the file is to be opened :param string pathName: the path name to open :return: a valid file descriptor, if not raises a SessionError exception. """ if self.getDialect() == smb.SMB_DIALECT: _, flags2 = self._SMBConnection.get_flags() pathName = pathName.replace('/', '\\') pathName = pathName.encode('utf-16le') if flags2 & smb.SMB.FLAGS2_UNICODE else pathName ntCreate = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX) ntCreate['Parameters'] = smb.SMBNtCreateAndX_Parameters() ntCreate['Data'] = smb.SMBNtCreateAndX_Data(flags=flags2) ntCreate['Parameters']['FileNameLength']= len(pathName) ntCreate['Parameters']['AccessMask'] = desiredAccess ntCreate['Parameters']['FileAttributes']= fileAttributes ntCreate['Parameters']['ShareAccess'] = shareMode ntCreate['Parameters']['Disposition'] = creationDisposition ntCreate['Parameters']['CreateOptions'] = creationOption ntCreate['Parameters']['Impersonation'] = impersonationLevel ntCreate['Parameters']['SecurityFlags'] = securityFlags ntCreate['Parameters']['CreateFlags'] = 0x16 ntCreate['Data']['FileName'] = pathName if flags2 & smb.SMB.FLAGS2_UNICODE: ntCreate['Data']['Pad'] = 0x0 if createContexts is not None: LOG.error("CreateContexts not supported in SMB1") try: return self._SMBConnection.nt_create_andx(treeId, pathName, cmd = ntCreate) except (smb.SessionError, smb3.SessionError), e: raise SessionError(e.get_error_code())
def install(self): if self.connection.isGuestSession(): LOG.critical("Authenticated as Guest. Aborting") self.connection.logoff() del self.connection else: fileCopied = False serviceCreated = False # Do the stuff here try: # Let's get the shares shares = self.getShares() self.share = self.findWritableShare(shares) self.copy_file(self.__exeFile, self.share, self.__binary_service_name) fileCopied = True svcManager = self.openSvcManager() if svcManager != 0: serverName = self.connection.getServerName() if self.share.lower() == 'admin$': path = '%systemroot%' else: if serverName != '': path = '\\\\%s\\%s' % (serverName, self.share) else: path = '\\\\127.0.0.1\\' + self.share service = self.createService(svcManager, self.share, path) serviceCreated = True if service != 0: # Start service LOG.info('Starting service %s.....' % self.__service_name) try: scmr.hRStartServiceW(self.rpcsvc, service) except: pass scmr.hRCloseServiceHandle(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, svcManager) return True except Exception, e: LOG.critical( "Error performing the installation, cleaning up: %s" % e) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass if fileCopied is True: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: pass if serviceCreated is True: try: scmr.hRDeleteService(self.rpcsvc, service) except: pass return False
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 __addItem(self, entry): dataDefinitionHeader = ESENT_DATA_DEFINITION_HEADER(entry['EntryData']) catalogEntry = ESENT_CATALOG_DATA_DEFINITION_ENTRY(entry['EntryData'][len(dataDefinitionHeader):]) itemName = self.__parseItemName(entry) if catalogEntry['Type'] == CATALOG_TYPE_TABLE: self.__tables[itemName] = OrderedDict() self.__tables[itemName]['TableEntry'] = entry self.__tables[itemName]['Columns'] = OrderedDict() self.__tables[itemName]['Indexes'] = OrderedDict() self.__tables[itemName]['LongValues'] = OrderedDict() self.__currentTable = itemName elif catalogEntry['Type'] == CATALOG_TYPE_COLUMN: self.__tables[self.__currentTable]['Columns'][itemName] = entry self.__tables[self.__currentTable]['Columns'][itemName]['Header'] = dataDefinitionHeader self.__tables[self.__currentTable]['Columns'][itemName]['Record'] = catalogEntry elif catalogEntry['Type'] == CATALOG_TYPE_INDEX: self.__tables[self.__currentTable]['Indexes'][itemName] = entry elif catalogEntry['Type'] == CATALOG_TYPE_LONG_VALUE: self.__addLongValue(entry) else: LOG.error('Unknown type 0x%x' % catalogEntry['Type']) raise
def negotiateSession(self, preferredDialect=None, flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake :return: True, raises a Session Error if error. """ hostType = nmb.TYPE_SERVER if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(self._myName, self._remoteName, self._remoteHost, self._sess_port, self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session = self._nmbSession ) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session = self._nmbSession, negPacket = packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout) elif preferredDialect in [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]: self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, preferredDialect = preferredDialect) else: LOG.critical("Unknown dialect ", preferredDialect) raise # propagate flags to the smb sub-object # does not affect smb3 objects if isinstance(self._SMBConnection, smb.SMB): self._SMBConnection.set_flags(flags1=flags1, flags2=flags2) return True
def __init__(self,data): # Depending on the type of data we'll end up building a different struct dataType = unpack('<H', data[4:][:2])[0] self.structure = self.fixed if dataType == CATALOG_TYPE_TABLE: self.structure += self.other + self.table_stuff elif dataType == CATALOG_TYPE_COLUMN: self.structure += self.column_stuff elif dataType == CATALOG_TYPE_INDEX: self.structure += self.other + self.index_stuff elif dataType == CATALOG_TYPE_LONG_VALUE: self.structure += self.other + self.lv_stuff elif dataType == CATALOG_TYPE_CALLBACK: LOG.error('CallBack types not supported!') raise else: LOG.error('Unknown catalog type 0x%x' % dataType) self.structure = () Structure.__init__(self,data) self.structure += self.common Structure.__init__(self,data)
def uninstall(self): fileCopied = True serviceCreated = True # Do the stuff here try: # Let's get the shares svcManager = self.openSvcManager() if svcManager != 0: resp = scmr.hROpenServiceW(self.rpcsvc, svcManager, self.__service_name + '\x00') service = resp['lpServiceHandle'] LOG.info('Stoping service %s.....' % self.__service_name) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass LOG.info('Removing service %s.....' % self.__service_name) scmr.hRDeleteService(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, svcManager) LOG.info('Removing file %s.....' % self.__binary_service_name) self.connection.deleteFile(self.share, self.__binary_service_name) except Exception: LOG.critical("Error performing the uninstallation, cleaning up") try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass if fileCopied is True: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: pass pass if serviceCreated is True: try: scmr.hRDeleteService(self.rpcsvc, service) except: pass
def __init__(self, data): # Depending on the type of data we'll end up building a different struct dataType = unpack('<H', data[4:][:2])[0] self.structure = self.fixed if dataType == CATALOG_TYPE_TABLE: self.structure += self.other + self.table_stuff elif dataType == CATALOG_TYPE_COLUMN: self.structure += self.column_stuff elif dataType == CATALOG_TYPE_INDEX: self.structure += self.other + self.index_stuff elif dataType == CATALOG_TYPE_LONG_VALUE: self.structure += self.other + self.lv_stuff elif dataType == CATALOG_TYPE_CALLBACK: LOG.error('CallBack types not supported!') raise else: LOG.error('Unknown catalog type 0x%x' % dataType) self.structure = () Structure.__init__(self, data) self.structure += self.common Structure.__init__(self, data)
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 conf_impacket_logger(self, formatter): LOG.setLevel(logging.INFO) LOG.propagate = False fileHandler = logging.FileHandler("./logs/mitmf.log") streamHandler = logging.StreamHandler(sys.stdout) fileHandler.setFormatter(formatter) streamHandler.setFormatter(formatter) LOG.addHandler(fileHandler) LOG.addHandler(streamHandler)
def install(self): if self.connection.isGuestSession(): LOG.critical("Authenticated as Guest. Aborting") self.connection.logoff() del self.connection else: fileCopied = False serviceCreated = False # Do the stuff here try: # Let's get the shares shares = self.getShares() self.share = self.findWritableShare(shares) self.copy_file(self.__exeFile ,self.share,self.__binary_service_name) fileCopied = True svcManager = self.openSvcManager() if svcManager != 0: serverName = self.connection.getServerName() if self.share.lower() == 'admin$': path = '%systemroot%' else: if serverName != '': path = '\\\\%s\\%s' % (serverName, self.share) else: path = '\\\\127.0.0.1\\' + self.share service = self.createService(svcManager, self.share, path) serviceCreated = True if service != 0: # Start service LOG.info('Starting service %s.....' % self.__service_name) try: scmr.hRStartServiceW(self.rpcsvc, service) except: pass scmr.hRCloseServiceHandle(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, svcManager) return True except Exception, e: LOG.critical("Error performing the installation, cleaning up: %s" %e) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass if fileCopied is True: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: pass if serviceCreated is True: try: scmr.hRDeleteService(self.rpcsvc, service) except: pass return False
def uninstall(self): fileCopied = True serviceCreated = True # Do the stuff here try: # Let's get the shares svcManager = self.openSvcManager() if svcManager != 0: resp = scmr.hROpenServiceW(self.rpcsvc, svcManager, self.__service_name+'\x00') service = resp['lpServiceHandle'] LOG.info('Stoping service %s.....' % self.__service_name) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass LOG.info('Removing service %s.....' % self.__service_name) scmr.hRDeleteService(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, svcManager) LOG.info('Removing file %s.....' % self.__binary_service_name) self.connection.deleteFile(self.share, self.__binary_service_name) except Exception: LOG.critical("Error performing the uninstallation, cleaning up" ) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass if fileCopied is True: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: pass pass if serviceCreated is True: try: scmr.hRDeleteService(self.rpcsvc, service) except: pass
def configureLogging(self, formatter): #yes I know this looks awful, yuck LOG.setLevel(logging.INFO) LOG.propagate = False logging.getLogger('smbserver').setLevel(logging.INFO) logging.getLogger('impacket').setLevel(logging.INFO) fileHandler = logging.FileHandler("./logs/mitmf.log") streamHandler = logging.StreamHandler(sys.stdout) fileHandler.setFormatter(formatter) streamHandler.setFormatter(formatter) LOG.addHandler(fileHandler) LOG.addHandler(streamHandler)
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()
else: # It exists, remove it scmr.hRDeleteService(self.rpcsvc, resp['lpServiceHandle']) scmr.hRCloseServiceHandle(self.rpcsvc, resp['lpServiceHandle']) # Create the service command = '%s\\%s' % (path, self.__binary_service_name) try: resp = scmr.hRCreateServiceW(self.rpcsvc, handle, self.__service_name + '\x00', self.__service_name + '\x00', lpBinaryPathName=command + '\x00') except: LOG.critical( "Error creating service %s on %s" % (self.__service_name, self.connection.getRemoteHost())) raise else: return resp['lpServiceHandle'] def openSvcManager(self): LOG.info("Opening SVCManager on %s....." % self.connection.getRemoteHost()) # Setup up a DCE SMBTransport with the connection already in place self._rpctransport = transport.SMBTransport( self.connection.getRemoteHost(), self.connection.getRemoteHost(), filename=r'\svcctl', smb_connection=self.connection) self.rpcsvc = self._rpctransport.get_dce_rpc()
def dump(self): baseOffset = len(self.record) self.record.dump() tags = self.data[-4 * self.record['FirstAvailablePageTag']:] print "FLAGS: " self.printFlags() print for i in range(self.record['FirstAvailablePageTag']): tag = tags[-4:] if self.__DBHeader['Version'] == 0x620 and self.__DBHeader[ 'FileFormatRevision'] > 11 and self.__DBHeader[ 'PageSize'] > 8192: valueSize = unpack('<H', tag[:2])[0] & 0x7fff valueOffset = unpack('<H', tag[2:])[0] & 0x7fff hexdump((self.data[baseOffset + valueOffset:][:6])) pageFlags = ord(self.data[baseOffset + valueOffset:][1]) >> 5 #print "TAG FLAG: 0x%x " % (unpack('<L', self.data[baseOffset+valueOffset:][:4]) ) >> 5 #print "TAG FLAG: 0x " , ord(self.data[baseOffset+valueOffset:][0]) else: valueSize = unpack('<H', tag[:2])[0] & 0x1fff pageFlags = (unpack('<H', tag[2:])[0] & 0xe000) >> 13 valueOffset = unpack('<H', tag[2:])[0] & 0x1fff print "TAG %-8d offset:0x%-6x flags:0x%-4x valueSize:0x%x" % ( i, valueOffset, pageFlags, valueSize) #hexdump(self.getTag(i)[1]) tags = tags[:-4] if self.record['PageFlags'] & FLAGS_ROOT > 0: rootHeader = ESENT_ROOT_HEADER(self.getTag(0)[1]) rootHeader.dump() elif self.record['PageFlags'] & FLAGS_LEAF == 0: # Branch Header flags, data = self.getTag(0) branchHeader = ESENT_BRANCH_HEADER(data) branchHeader.dump() else: # Leaf Header flags, data = self.getTag(0) if self.record['PageFlags'] & FLAGS_SPACE_TREE > 0: # Space Tree spaceTreeHeader = ESENT_SPACE_TREE_HEADER(data) spaceTreeHeader.dump() else: leafHeader = ESENT_LEAF_HEADER(data) leafHeader.dump() # Print the leaf/branch tags for tagNum in range(1, self.record['FirstAvailablePageTag']): flags, data = self.getTag(tagNum) if self.record['PageFlags'] & FLAGS_LEAF == 0: # Branch page branchEntry = ESENT_BRANCH_ENTRY(flags, data) branchEntry.dump() elif self.record['PageFlags'] & FLAGS_LEAF > 0: # Leaf page if self.record['PageFlags'] & FLAGS_SPACE_TREE > 0: # Space Tree spaceTreeEntry = ESENT_SPACE_TREE_ENTRY(data) #spaceTreeEntry.dump() elif self.record['PageFlags'] & FLAGS_INDEX > 0: # Index Entry indexEntry = ESENT_INDEX_ENTRY(data) #indexEntry.dump() elif self.record['PageFlags'] & FLAGS_LONG_VALUE > 0: # Long Page Value LOG.error('Long value still not supported') raise else: # Table Value leafEntry = ESENT_LEAF_ENTRY(flags, data) dataDefinitionHeader = ESENT_DATA_DEFINITION_HEADER( leafEntry['EntryData']) dataDefinitionHeader.dump() catalogEntry = ESENT_CATALOG_DATA_DEFINITION_ENTRY( leafEntry['EntryData'][len(dataDefinitionHeader):]) catalogEntry.dump() hexdump(leafEntry['EntryData'])
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 readHeader(self): LOG.debug("Reading Boot Sector for %s" % self.__volumeName)
if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') >= 0: # We're good, pass the exception pass else: raise e else: # It exists, remove it scmr.hRDeleteService(self.rpcsvc, resp['lpServiceHandle']) scmr.hRCloseServiceHandle(self.rpcsvc, resp['lpServiceHandle']) # Create the service command = '%s\\%s' % (path, self.__binary_service_name) try: resp = scmr.hRCreateServiceW(self.rpcsvc, handle,self.__service_name + '\x00', self.__service_name + '\x00', lpBinaryPathName=command + '\x00') except: LOG.critical("Error creating service %s on %s" % (self.__service_name, self.connection.getRemoteHost())) raise else: return resp['lpServiceHandle'] def openSvcManager(self): LOG.info("Opening SVCManager on %s....." % self.connection.getRemoteHost()) # Setup up a DCE SMBTransport with the connection already in place self._rpctransport = transport.SMBTransport(self.connection.getRemoteHost(), self.connection.getRemoteHost(),filename = r'\svcctl', smb_connection = self.connection) self.rpcsvc = self._rpctransport.get_dce_rpc() self.rpcsvc.connect() self.rpcsvc.bind(scmr.MSRPC_UUID_SCMR) try: resp = scmr.hROpenSCManagerW(self.rpcsvc) except: LOG.critical("Error opening SVCManager on %s....." % self.connection.getRemoteHost())
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
def computeResponse(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash = '', nthash = '', use_ntlmv2 = USE_NTLMv2): if use_ntlmv2: return computeResponseNTLMv2(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2 = use_ntlmv2) else: return computeResponseNTLMv1(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2 = use_ntlmv2) try: POW = None from Crypto.Cipher import ARC4 from Crypto.Cipher import DES from Crypto.Hash import MD4 except Exception: try: import POW except Exception: LOG.critical("Warning: You don't have any crypto installed. You need PyCrypto") LOG.critical("See http://www.pycrypto.org/") NTLM_AUTH_NONE = 1 NTLM_AUTH_CONNECT = 2 NTLM_AUTH_CALL = 3 NTLM_AUTH_PKT = 4 NTLM_AUTH_PKT_INTEGRITY = 5 NTLM_AUTH_PKT_PRIVACY = 6 NTLMSSP_KEY_56 = 0x80000000 NTLMSSP_KEY_EXCHANGE = 0x40000000 NTLMSSP_KEY_128 = 0x20000000 # NTLMSSP_ = 0x10000000 # NTLMSSP_ = 0x08000000 # NTLMSSP_ = 0x04000000
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 __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 dump(self): baseOffset = len(self.record) self.record.dump() tags = self.data[-4*self.record['FirstAvailablePageTag']:] print "FLAGS: " self.printFlags() print for i in range(self.record['FirstAvailablePageTag']): tag = tags[-4:] if self.__DBHeader['Version'] == 0x620 and self.__DBHeader['FileFormatRevision'] > 11 and self.__DBHeader['PageSize'] > 8192: valueSize = unpack('<H', tag[:2])[0] & 0x7fff valueOffset = unpack('<H',tag[2:])[0] & 0x7fff hexdump((self.data[baseOffset+valueOffset:][:6])) pageFlags = ord(self.data[baseOffset+valueOffset:][1]) >> 5 #print "TAG FLAG: 0x%x " % (unpack('<L', self.data[baseOffset+valueOffset:][:4]) ) >> 5 #print "TAG FLAG: 0x " , ord(self.data[baseOffset+valueOffset:][0]) else: valueSize = unpack('<H', tag[:2])[0] & 0x1fff pageFlags = (unpack('<H', tag[2:])[0] & 0xe000) >> 13 valueOffset = unpack('<H',tag[2:])[0] & 0x1fff print "TAG %-8d offset:0x%-6x flags:0x%-4x valueSize:0x%x" % (i,valueOffset,pageFlags,valueSize) #hexdump(self.getTag(i)[1]) tags = tags[:-4] if self.record['PageFlags'] & FLAGS_ROOT > 0: rootHeader = ESENT_ROOT_HEADER(self.getTag(0)[1]) rootHeader.dump() elif self.record['PageFlags'] & FLAGS_LEAF == 0: # Branch Header flags, data = self.getTag(0) branchHeader = ESENT_BRANCH_HEADER(data) branchHeader.dump() else: # Leaf Header flags, data = self.getTag(0) if self.record['PageFlags'] & FLAGS_SPACE_TREE > 0: # Space Tree spaceTreeHeader = ESENT_SPACE_TREE_HEADER(data) spaceTreeHeader.dump() else: leafHeader = ESENT_LEAF_HEADER(data) leafHeader.dump() # Print the leaf/branch tags for tagNum in range(1,self.record['FirstAvailablePageTag']): flags, data = self.getTag(tagNum) if self.record['PageFlags'] & FLAGS_LEAF == 0: # Branch page branchEntry = ESENT_BRANCH_ENTRY(flags, data) branchEntry.dump() elif self.record['PageFlags'] & FLAGS_LEAF > 0: # Leaf page if self.record['PageFlags'] & FLAGS_SPACE_TREE > 0: # Space Tree spaceTreeEntry = ESENT_SPACE_TREE_ENTRY(data) #spaceTreeEntry.dump() elif self.record['PageFlags'] & FLAGS_INDEX > 0: # Index Entry indexEntry = ESENT_INDEX_ENTRY(data) #indexEntry.dump() elif self.record['PageFlags'] & FLAGS_LONG_VALUE > 0: # Long Page Value LOG.error('Long value still not supported') raise else: # Table Value leafEntry = ESENT_LEAF_ENTRY(flags, data) dataDefinitionHeader = ESENT_DATA_DEFINITION_HEADER(leafEntry['EntryData']) dataDefinitionHeader.dump() catalogEntry = ESENT_CATALOG_DATA_DEFINITION_ENTRY(leafEntry['EntryData'][len(dataDefinitionHeader):]) catalogEntry.dump() hexdump(leafEntry['EntryData'])
password, lmhash, nthash, use_ntlmv2=use_ntlmv2) try: POW = None from Crypto.Cipher import ARC4 from Crypto.Cipher import DES from Crypto.Hash import MD4 except Exception: try: import POW except Exception: LOG.critical( "Warning: You don't have any crypto installed. You need PyCrypto") LOG.critical("See http://www.pycrypto.org/") NTLM_AUTH_NONE = 1 NTLM_AUTH_CONNECT = 2 NTLM_AUTH_CALL = 3 NTLM_AUTH_PKT = 4 NTLM_AUTH_PKT_INTEGRITY = 5 NTLM_AUTH_PKT_PRIVACY = 6 NTLMSSP_KEY_56 = 0x80000000 NTLMSSP_KEY_EXCHANGE = 0x40000000 NTLMSSP_KEY_128 = 0x20000000 # NTLMSSP_ = 0x10000000 # NTLMSSP_ = 0x08000000 # NTLMSSP_ = 0x04000000