def extract_macros_from_office2003(fullpath, fileobj=None): ''' :return: [(host_fullpath, filename_from_host, data), ... ] ''' from oletools.olevba import VBA_Parser vp = VBA_Parser(fullpath, data=fileobj.read() if fileobj else None) r = [] try: if vp.detect_vba_macros(): macros = vp.extract_all_macros() assert (macros ) # macros detect, if cannot extact, must be error occured if macros: for (subfullpath, stream_path, vba_filename, vba_code) in macros: a = os.path.basename(fullpath) b = os.path.basename(subfullpath) vba_filename += u'.vba' sub = (io_text_arg(fullpath), io_text_arg(vba_filename if a == b else u'{0}_{1}'. format(b, vba_filename)), vba_code) r.append(sub) except: pass finally: vp.close() return r
def parse_vba(self, save_path): save = False vbaparser = VBA_Parser(__sessions__.current.file.path) # Check for Macros if not vbaparser.detect_vba_macros(): self.log('error', "No Macro's Detected") return self.log('info', "Macro's Detected") #try: if True: an_results = {'AutoExec':[], 'Suspicious':[], 'IOC':[], 'Hex String':[], 'Base64 String':[], 'Dridex String':[], 'VBA string':[]} for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): self.log('info', "Stream Details") self.log('item', "OLE Stream: {0}".format(string_clean(stream_path))) self.log('item', "VBA Filename: {0}".format(string_clean(vba_filename))) # Analyse the VBA Code vba_scanner = VBA_Scanner(vba_code) analysis = vba_scanner.scan(include_decoded_strings=True) for kw_type, keyword, description in analysis: an_results[kw_type].append([string_clean_hex(keyword), description]) # Save the code to external File if save_path: try: with open(save_path, 'a') as out: out.write(vba_code) save = True except: self.log('error', "Unable to write to {0}".format(save_path)) return # Print all Tables together self.log('info', "AutoRun Macros Found") self.log('table', dict(header=['Method', 'Description'], rows=an_results['AutoExec'])) self.log('info', "Suspicious Keywords Found") self.log('table', dict(header=['KeyWord', 'Description'], rows=an_results['Suspicious'])) self.log('info', "Possible IOC's") self.log('table', dict(header=['IOC', 'Type'], rows=an_results['IOC'])) self.log('info', "Hex Strings") self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Hex String'])) self.log('info', "Base64 Strings") self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Base64 String'])) self.log('info', "Dridex String") self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Dridex String'])) self.log('info', "VBA string") self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['VBA string'])) if save: self.log('success', "Writing VBA Code to {0}".format(save_path)) #except: #self.log('error', "Unable to Process File") # Close the file vbaparser.close()
def vba2graph_from_vba_object(filepath): """ vba2graph as library Args: filepath (string): path to file """ logger.info("Extracting macros from file") if HAVE_OLETOOLS: try: vba = VBA_Parser(filepath) except Exception as e: return False full_vba_code = "" for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): full_vba_code += 'VBA MACRO %s \n' % vba_filename full_vba_code += '- ' * 39 + '\n' # Temporary workaround. Change when oletools 0.56 will be released. if isinstance(vba_code, bytes): vba_code = vba_code.decode('utf8', errors='replace') full_vba_code += vba_code vba.close() if full_vba_code: input_vba_content = handle_olevba_input(full_vba_code) return input_vba_content return False
def doc_parsing(self, filename, filecontent): ''' Function to parse the given data in mail content ''' mil_attach = '' # reset var # send data to vba parser vbaparser = VBA_Parser(filename, data=filecontent) # if a macro is detected if vbaparser.detect_vba_macros(): results = vbaparser.analyze_macros() nr = 1 self.log("VBA Macros found") # generate report for log file for kw_type, keyword, description in results: if kw_type == 'Suspicious': mil_attach += 'Macro Number %i:\n Type: %s\n Keyword: %s\n Description: %s\n' % (nr, kw_type, keyword, description) nr += 1 mil_attach += '\nSummery:\nAutoExec keywords: %d\n' % vbaparser.nb_autoexec mil_attach += 'Suspicious keywords: %d\n' % vbaparser.nb_suspicious mil_attach += 'IOCs: %d\n' % vbaparser.nb_iocs mil_attach += 'Hex obfuscated strings: %d\n' % vbaparser.nb_hexstrings mil_attach += 'Base64 obfuscated strings: %d\n' % vbaparser.nb_base64strings mil_attach += 'Dridex obfuscated strings: %d\n' % vbaparser.nb_dridexstrings mil_attach += 'VBA obfuscated strings: %d' % vbaparser.nb_vbastrings r_level = vbaparser.nb_autoexec + vbaparser.nb_suspicious + vbaparser.nb_iocs + vbaparser.nb_hexstrings + vbaparser.nb_base64strings + vbaparser.nb_dridexstrings + vbaparser.nb_vbastrings # set reject level to global self.level = r_level vbaparser.close() return mil_attach # return the log to caller else: self.log("VBA no Macros found in file") vbaparser.close() return None # nothing found
def get_report(self, sample): """ Return oletools report or create if not already cached. """ if sample.oletools_report != None: return sample.oletools_report report = {} try: vbaparser = VBA_Parser(sample.file_path) # VBA_Parser reports macros for office documents report['has_macros'] = vbaparser.detect_vba_macros( ) or vbaparser.detect_xlm_macros() try: report['vba'] = vbaparser.reveal() except TypeError: # no macros pass vbaparser.close() except IOError: raise except (TypeError, FileOpenError): # The given file is not an office document. pass except Exception as error: logger.exception(error) sample.register_oletools_report(OletoolsReport(report)) return report
def is_file_has_VBA_macros(self): """Check if file has VBA macros.""" file_path = self.file_path vbaparser = VBA_Parser(file_path) print('The file type is "%s"' % (vbaparser.type)) report = vbaparser.detect_vba_macros() vbaparser.close() return report
def parse_vba(self, save_path): vba = VBA_Parser(__sessions__.current.file.path) # Check for Macros if not vba.detect_vba_macros(): self.log('error', "No Macro's Detected") return self.log('info', "Macro's Detected") try: for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros(): self.log('info', "Stream Details") self.log('item', "OLE Stream: {0}".format(stream_path)) self.log('item', "VBA Filename: {0}".format(vba_filename)) autoexec_keywords = detect_autoexec(vba_code) if autoexec_keywords: self.log('info', "AutoRun Macros Found") rows = [] for keyword, description in autoexec_keywords: rows.append([keyword, description]) self.log( 'table', dict(header=['KeyWord', 'Description'], rows=rows)) # Match Keyword Types suspicious_keywords = detect_suspicious(vba_code) if suspicious_keywords: self.log('info', "Suspicious Keywords Found") rows = [] for keyword, description in suspicious_keywords: rows.append([keyword, description]) self.log( 'table', dict(header=['KeyWord', 'Description'], rows=rows)) # Match IOCs patterns = detect_patterns(vba_code) if patterns: self.log('info', "Suspicious Keywords Found") rows = [] for pattern_type, value in patterns: rows.append([pattern_type, value]) self.log('table', dict(header=['Pattern', 'Value'], rows=rows)) # Save the code to external File if save_path: try: with open(save_path, 'w') as out: out.write(vba_code) self.log('info', "Writing VBA Code to {0}".format(save_path)) except: self.log('Error', "Unable to write to {0}".format(save_path)) return except: self.log('Error', "Unable to Process File") # Close the file vba.close()
def get_report(self): """ Return oletools report or create if not already cached. """ if self.sample.oletools_report is not None: return self.sample.oletools_report report = { 'autoexec': [], 'suspicious': [], } file_path = self.sample.file_path try: vbaparser = VBA_Parser(file_path) # VBA_Parser reports macros for office documents report['has_macros'] = vbaparser.detect_vba_macros( ) or vbaparser.detect_xlm_macros() try: report['vba'] = vbaparser.reveal() except TypeError: # office document with no macros pass all_macros = vbaparser.extract_all_macros() if (report['has_macros'] and len(all_macros) == 1 and isinstance(all_macros[0], tuple) and len(all_macros[0]) >= 3 and all_macros[0][2] == file_path): logger.warning( "Buggy oletools version detected, result overridden. May " "lead to false negatives, please update to fixed version") report['has_macros'] = False if vbaparser.detect_vba_macros(): vb_code = vbaparser.extract_all_macros() for (_, _, _, c) in vb_code: autoexec = detect_autoexec(c) if len(autoexec) >= 1: report['autoexec'].append(autoexec[0]) suspicious = detect_suspicious(c) if len(suspicious) >= 1: report['suspicious'].append(suspicious[0]) vbaparser.close() except IOError: raise except (TypeError, FileOpenError): # The given file is not an office document. pass except Exception as error: logger.exception(error) report = OletoolsReport(report) self.sample.register_oletools_report(report) return report
def parse_vba(self, save_path): save = False vba = VBA_Parser(__sessions__.current.file.path) # Check for Macros if not vba.detect_vba_macros(): self.log('error', "No Macro's Detected") return self.log('info', "Macro's Detected") try: run_rows = [] word_rows = [] pattern_rows = [] for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros(): self.log('info', "Stream Details") self.log('item', "OLE Stream: {0}".format(string_clean(stream_path))) self.log('item', "VBA Filename: {0}".format(string_clean(vba_filename))) autoexec_keywords = detect_autoexec(vba_code) if autoexec_keywords: for keyword, description in autoexec_keywords: run_rows.append([keyword, description]) # Match Keyword Types suspicious_keywords = detect_suspicious(vba_code) if suspicious_keywords: for keyword, description in suspicious_keywords: word_rows.append([keyword, description]) # Match IOCs patterns = detect_patterns(vba_code) if patterns: for pattern_type, value in patterns: pattern_rows.append([pattern_type, value]) # Save the code to external File if save_path: try: with open(save_path, 'a') as out: out.write(vba_code) save = True except: self.log('Error', "Unable to write to {0}".format(save_path)) return # Print all Tables together self.log('info', "AutoRun Macros Found") self.log('table', dict(header=['KeyWord', 'Description'], rows=run_rows)) self.log('info', "Suspicious Keywords Found") self.log('table', dict(header=['KeyWord', 'Description'], rows=word_rows)) self.log('info', "Suspicious Patterns Found") self.log('table', dict(header=['Pattern', 'Value'], rows=pattern_rows)) if save: self.log('success', "Writing VBA Code to {0}".format(save_path)) except: self.log('Error', "Unable to Process File") # Close the file vba.close()
def vba2graph_from_vba_object(filepath): logging.info("Extracting macros from file") full_vba_code = "" vba_parser = VBA_Parser(filepath) for (subfilename, stream_path, vba_filename, vba_code) in vba_parser.extract_macros(): # workaround till oletools version 0.56 if isinstance(vba_code, bytes): vba_code = vba_code.decode('latin1', errors='replace') full_vba_code += vba_code vba_parser.close() return full_vba_code
def parse(file_path): vba_folder = 'src' vba_parser = VBA_Parser(file_path) for _, _, vba_filename, vba_code in vba_parser.extract_macros(): if not os.path.isdir(vba_folder): os.makedirs(vba_folder) with open(os.path.join(vba_folder, vba_filename), 'w', encoding='utf-8', newline='\n') as file: file.write(vba_code) vba_parser.close()
def writeVBA(workbookName): vbaparser = VBA_Parser('./'+workbookName) VBA_path = './src/VBA' if os.path.exists(VBA_path): shutil.rmtree(VBA_path) os.mkdir(VBA_path) for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): f = open('./src/VBA/' + vba_filename, "w") f.write(vba_code) f.close() vbaparser.close()
def _ole_analysis(full_targ_path): # This function calls a number of tools / scripts to run against document samples containing OLE data and extracts data and/or performs analysis as needed. try: vba_parse_Obj = VBA_Parser(full_targ_path) except AttributeError: return ("ERROR_PARSING", "ERROR_PARSING", "ERROR_PARSING", "ERROR_PARSING") macro_analysis_over = [] macro_analysis_info = [] if vba_parse_Obj.detect_vba_macros(): vba_macro = "Present" # Utilizing oletools to perform analysis. # Grabbing info from each macro. MA_CNT = 1 for (file_name, ole_stream, vba_filename, vba_code) in vba_parse_Obj.extract_macros(): macro_analysis_over.append(str(MA_CNT) + ':' + str(full_targ_path)) macro_analysis_over.append( str(MA_CNT) + ":Filename :" + file_name) macro_analysis_over.append( str(MA_CNT) + ":OLE Stream :" + ole_stream) macro_analysis_over.append( str(MA_CNT) + ":VBA Filename :" + vba_filename) macro_analysis_over.append(str(MA_CNT) + ':' + vba_code) MA_CNT += 1 # Grabbing some overall VBA analysis info. macro_flag_types = [] macro_analysis_res = vba_parse_Obj.analyze_macros() if isinstance(macro_analysis_res, list): for iocType in macro_analysis_res: if str(iocType) not in macro_flag_types: macro_flag_types.append(str(iocType[0])) if len(macro_flag_types) > 0: iocs = ':'.join(list(set(macro_flag_types))) else: iocs = "None" else: vba_macro = "None" iocs = "None" macro_analysis_res = "None" vba_parse_Obj.close() return (vba_macro, macro_analysis_over, str(macro_analysis_res), iocs)
def _run(self, scanObject, result, depth, args): moduleResult = [] vbap_buffer = VBA_Parser(scanObject.buffer) try: if vbap_buffer.detect_vba_macros(): vbap_result = vbap_buffer.analyze_macros() for kw_type, keyword, description in vbap_result: kw = '%s - %s' % ( keyword,description ) scanObject.addMetadata(self.module_name,kw_type,kw) except (QuitScanException, GlobalScanTimeoutError, GlobalModuleTimeoutError): raise except: logging.debug("Failed to parse OLEVBA") vbap_buffer.close() return moduleResult
def _ole_analysis(full_targ_path): # This function calls a number of tools / scripts to run against document samples containing OLE data and extracts data and/or performs analysis as needed. try: vba_parse_Obj = VBA_Parser(full_targ_path) except AttributeError: return("ERROR_PARSING", "ERROR_PARSING", "ERROR_PARSING", "ERROR_PARSING") macro_analysis_over = [] macro_analysis_info = [] if vba_parse_Obj.detect_vba_macros(): vba_macro = "Present" # Utilizing oletools to perform analysis. # Grabbing info from each macro. MA_CNT = 1 for (file_name, ole_stream, vba_filename, vba_code) in vba_parse_Obj.extract_macros(): macro_analysis_over.append(str(MA_CNT)+':'+str(full_targ_path)) macro_analysis_over.append(str(MA_CNT)+":Filename :"+file_name) macro_analysis_over.append(str(MA_CNT)+":OLE Stream :"+ole_stream) macro_analysis_over.append(str(MA_CNT)+":VBA Filename :"+vba_filename) macro_analysis_over.append(str(MA_CNT)+':'+vba_code) MA_CNT+=1 # Grabbing some overall VBA analysis info. macro_flag_types = [] macro_analysis_res = vba_parse_Obj.analyze_macros() if isinstance(macro_analysis_res, list): for iocType in macro_analysis_res: if str(iocType) not in macro_flag_types: macro_flag_types.append(str(iocType[0])) if len(macro_flag_types) > 0: iocs = ':'.join(list(set(macro_flag_types))) else: iocs = "None" else: vba_macro = "None" iocs = "None" macro_analysis_res = "None" vba_parse_Obj.close() return(vba_macro, macro_analysis_over, str(macro_analysis_res), iocs)
def get_macros_infos(self): """Check file macroses for suspisious behaviour.""" if not self.has_macros: return None vbaparser = VBA_Parser(self.file_path) vbaparser.analyze_macros() # obfuscated vba and autoexec danger autoexec_and_vba = 0 if vbaparser.nb_autoexec > 0 and vbaparser.nb_vbastrings > 0: autoexec_and_vba = vbaparser.nb_vbastrings # obfuscated Base64 and autoexec danger autoexec_and_base64 = 0 if vbaparser.nb_autoexec > 0 and vbaparser.nb_base64strings > 0: autoexec_and_base64 = vbaparser.nb_base64strings # obfuscated HEX and autoexec danger autoexec_and_HEX = 0 if vbaparser.nb_autoexec > 0 and vbaparser.nb_hexstrings > 0: autoexec_and_HEX = vbaparser.nb_hexstrings macros_infos = [ {'number': vbaparser.nb_autoexec, 'description': 'Ключевые слова автоматического вызова', 'danger': False, 'function': olevba.detect_autoexec}, {'number': autoexec_and_HEX, 'description': 'Автоматический вызов шеснадцатиричных обфусцированных строк', 'danger': False, 'function': olevba.detect_hex_strings}, {'number': vbaparser.nb_vbastrings, 'description': 'VBA обфусцированные строки', 'danger': False, 'function': olevba.detect_vba_strings}, {'number': vbaparser.nb_suspicious, 'description': 'Подозрительные ключевые слова', 'danger': False, 'function': olevba.detect_suspicious}, {'number': autoexec_and_vba, 'description': 'Автоматический вызов обфусцированного кода', 'danger': True, 'function': olevba.detect_vba_strings}, {'number': vbaparser.nb_dridexstrings, 'description': 'Dridex обфусцированные строки', 'danger': True, 'function': olevba.detect_dridex_strings}, {'number': autoexec_and_base64, 'description': 'Автоматический вызов Base64 обфусцированных строк', 'danger': True, 'function': olevba.detect_base64_strings}, ] vbaparser.close() return macros_infos
def vba2graph_from_vba_object(filepath): """ vba2graph as library Args: filepath (string): path to file """ if HAVE_OLETOOLS: try: vba = VBA_Parser(filepath) except Exception as e: return False full_vba_code = "" for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): full_vba_code += 'VBA MACRO %s \n' % vba_filename full_vba_code += '- '*39 + '\n' full_vba_code += vba_code vba.close() if full_vba_code: input_vba_content = handle_olevba_input(full_vba_code) return input_vba_content return False
def get_report(self, sample): """ Return oletools report or create if not already cached. """ if sample.oletools_report != None: return sample.oletools_report report = {} if sample.file_extension not in self.MS_OFFICE_EXTENSIONS: raise OleNotAnOfficeDocumentException(sample.file_extension) try: vbaparser = VBA_Parser(sample.file_path) # List from oletools/olevba.py#L553 oletype = ('OLE', 'OpenXML', 'FlatOPC_XML', 'Word2003_XML', 'MHTML', 'PPT') # check if ole detects it as an office file if vbaparser.type not in oletype: raise OleNotAnOfficeDocumentException(sample.file_extension) # VBA_Parser reports macros for office documents report['has_macros'] = vbaparser.detect_vba_macros( ) or vbaparser.detect_xlm_macros() try: report['vba'] = vbaparser.reveal() except TypeError: # no macros pass vbaparser.close() except IOError: raise except TypeError: # The given file is not an office document. pass except Exception as error: logger.exception(error) sample.register_oletools_report(OletoolsReport(report)) return report
def inspect_vba_data(self, filename, filecontent): ''' Function to parse the given data in mail content ''' vbaparser_report_log = '' # reset var # send data to vba parser vbaparser = VBA_Parser(filename, data=filecontent) # if a macro is detected if not vbaparser.detect_vba_macros(): self.log("VBA no Macros found in file") vbaparser.close() return None # nothing found else: results = vbaparser.analyze_macros() nr = 1 self.log("VBA Macros found") # generate report for log file for kw_type, keyword, description in results: if kw_type == 'Suspicious': vbaparser_report_log += 'Macro Number %i:\n Type: %s\n Keyword: %s\n Description: %s\n' % ( nr, kw_type, keyword, description) nr += 1 vbaparser_report_log += '\nSummery:\nAutoExec keywords: %d\n' % vbaparser.nb_autoexec vbaparser_report_log += 'Suspicious keywords: %d\n' % vbaparser.nb_suspicious vbaparser_report_log += 'IOCs: %d\n' % vbaparser.nb_iocs vbaparser_report_log += 'Hex obfuscated strings: %d\n' % vbaparser.nb_hexstrings vbaparser_report_log += 'Base64 obfuscated strings: %d\n' % vbaparser.nb_base64strings vbaparser_report_log += 'Dridex obfuscated strings: %d\n' % vbaparser.nb_dridexstrings vbaparser_report_log += 'VBA obfuscated strings: %d' % vbaparser.nb_vbastrings # TBD: calculate a better level - add additional values to Base64 and Hex. No human writes such strings into code. r_level = vbaparser.nb_autoexec + vbaparser.nb_suspicious + vbaparser.nb_iocs + vbaparser.nb_hexstrings + vbaparser.nb_base64strings + vbaparser.nb_dridexstrings + vbaparser.nb_vbastrings # set reject level to global #self.level = r_level vbaparser.close() return [r_level, vbaparser_report_log] # return the log to caller
def get_vba_code(self): """Code analysis for malicious parts. Returns malicious code, if any.""" if not self.has_macros: return "Документ не содержит макросов!" vbaparser = VBA_Parser(self.file_path) vbaparser.detect_vba_macros() code_list = list() for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): code_list.append(vba_code) code = vba_code report_vba = code_list[0] macros_infos = self.macros_infos for i in range(len(macros_infos)): if macros_infos[i]['number'] > 0: macro_code = macros_infos[i]['function'](code) for i in macro_code: for j in i: report_vba += j vbaparser.close() return report_vba
def run(self): results = {} # olevba olevba_results = {} try: vbaparser = VBA_Parser(self.filepath) olevba_results["macro_found"] = ( True if vbaparser.detect_vba_macros() else False ) if olevba_results["macro_found"]: macro_data = [] for ( v_filename, stream_path, vba_filename, vba_code, ) in vbaparser.extract_macros(): extracted_macro = { "filename": v_filename, "ole_stream": stream_path, "vba_filename": vba_filename, "vba_code": vba_code, } macro_data.append(extracted_macro) olevba_results["macro_data"] = macro_data # example output """ {'description': 'Runs when the Word document is opened', 'keyword': 'AutoOpen', 'type': 'AutoExec'}, {'description': 'May run an executable file or a system command', 'keyword': 'Shell', 'type': 'Suspicious'}, {'description': 'May run an executable file or a system command', 'keyword': 'WScript.Shell', 'type': 'Suspicious'}, {'description': 'May run an executable file or a system command', 'keyword': 'Run', 'type': 'Suspicious'}, {'description': 'May run PowerShell commands', 'keyword': 'powershell', 'type': 'Suspicious'}, {'description': '9BA55BE5', 'keyword': 'xxx', 'type': 'Hex String'}, """ analyzer_results = vbaparser.analyze_macros(show_decoded_strings=True) # it gives None if it does not find anything if analyzer_results: analyze_macro_results = [] for kw_type, keyword, description in analyzer_results: if kw_type != "Hex String": analyze_macro_result = { "type": kw_type, "keyword": keyword, "description": description, } analyze_macro_results.append(analyze_macro_result) olevba_results["analyze_macro"] = analyze_macro_results olevba_results["reveal"] = vbaparser.reveal() vbaparser.close() except Exception as e: traceback.print_exc() error_message = f"job_id {self.job_id} vba parser failed. Error: {e}" logger.exception(error_message) self.report["errors"].append(error_message) results["olevba"] = olevba_results # mraptor macro_raptor = mraptor.MacroRaptor(olevba_results.get("reveal", None)) if macro_raptor: macro_raptor.scan() results["mraptor"] = "suspicious" if macro_raptor.suspicious else "ok" return results
def parse_vba(self, save_path): """ Parse VBA scripts. """ save = False vbaparser = VBA_Parser(__sessions__.current.file.path) # Check for Macros if not vbaparser.detect_vba_macros(): self.log("error", "No macros detected") return self.log("info", "Macros detected") # try: if True: an_results = { "AutoExec": [], "Suspicious": [], "IOC": [], "Hex String": [], "Base64 String": [], "Dridex string": [], "VBA string": [] } for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): self.log("info", "Stream Details") self.log("item", "OLE Stream: {0}".format(string_clean(stream_path))) self.log( "item", "VBA Filename: {0}".format(string_clean(vba_filename))) # Analyse the VBA Code vba_scanner = VBA_Scanner(vba_code) analysis = vba_scanner.scan(include_decoded_strings=True) for kw_type, keyword, description in analysis: an_results[kw_type].append( [string_clean_hex(keyword), description]) # Save the code to external File if save_path: try: with open(save_path, "a") as out: out.write(vba_code) save = True except Exception as e: self.log( "error", "Unable to write to {0}: {1}".format(save_path, e)) return # Print all tables together if an_results["AutoExec"]: self.log("info", "Autorun macros found") self.log( "table", dict(header=["Method", "Description"], rows=an_results["AutoExec"])) if an_results["Suspicious"]: self.log("info", "Suspicious keywords found") self.log( "table", dict(header=["Keyword", "Description"], rows=an_results["Suspicious"])) if an_results["IOC"]: self.log("info", "Possible IOCs") self.log("table", dict(header=["IOC", "Type"], rows=an_results["IOC"])) if an_results["Hex String"]: self.log("info", "Hex strings") self.log( "table", dict(header=["Decoded", "Raw"], rows=an_results["Hex String"])) if an_results["Base64 String"]: self.log("info", "Base64 strings") self.log( "table", dict(header=["Decoded", "Raw"], rows=an_results["Base64 String"])) if an_results["Dridex string"]: self.log("info", "Dridex strings") self.log( "table", dict(header=["Decoded", "Raw"], rows=an_results["Dridex string"])) if an_results["VBA string"]: self.log("info", "VBA strings") self.log( "table", dict(header=["Decoded", "Raw"], rows=an_results["VBA string"])) if save: self.log("success", "Writing VBA Code to {0}".format(save_path)) # Close the file vbaparser.close()
def ProcessFile(path): if not(os.path.isfile(path)): print '{0} not a file!'.format(path) return 2 try: data = {} data['valid'] = True oledata = {} vbaparser = VBA_Parser(path) oledata['has_macros'] = vbaparser.detect_vba_macros() # dump macros content macros = [] for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): macro = {} macro['filename'] = filename macro['stream'] = stream_path macro['vba'] = vba_filename macro['content'] = convert_to_printable_null_terminated(vba_code) macros.append(macro) oledata['macros'] = macros # macro analysis macros_warnings = [] results = vbaparser.analyze_macros() for kw_type, keyword, description in results: warning = {} warning['type'] = kw_type warning['keyword'] = keyword warning['description'] = description macros_warnings.append(warning) oledata['macros_warnings'] = macros_warnings # counters counters = {} counters['autoexec'] = vbaparser.nb_autoexec counters['suspicious'] = vbaparser.nb_suspicious counters['iocs'] = vbaparser.nb_iocs counters['hexstrings'] = vbaparser.nb_hexstrings counters['base64strings'] = vbaparser.nb_base64strings counters['dridexstrings'] = vbaparser.nb_dridexstrings counters['vbastrings'] = vbaparser.nb_vbastrings oledata['counters'] = counters # deobfuscation oledata['deobfuscated'] = convert_to_printable_null_terminated(vbaparser.reveal()) # close vbaparser.close() data['data'] = oledata encoded = json.dumps(data) print encoded except Exception as ex: data = {} data['valid'] = False data['error'] = str(ex) print json.dumps(data) return 1 return 0
def run(analyzer_name, job_id, filepath, filename, md5, additional_config_params): logger.info("started analyzer {} job_id {}" "".format(analyzer_name, job_id)) report = general.get_basic_report_template(analyzer_name) try: results = {} # olevba olevba_results = {} try: vbaparser = VBA_Parser(filepath) olevba_results[ 'macro_found'] = True if vbaparser.detect_vba_macros( ) else False if olevba_results['macro_found']: macro_data = [] for (v_filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): extracted_macro = { "filename": v_filename, "ole_stream": stream_path, "vba_filename": vba_filename, "vba_code": vba_code } macro_data.append(extracted_macro) olevba_results['macro_data'] = macro_data # example output ''' {'description': 'Runs when the Word document is opened', 'keyword': 'AutoOpen', 'type': 'AutoExec'}, {'description': 'May run an executable file or a system command', 'keyword': 'Shell', 'type': 'Suspicious'}, {'description': 'May run an executable file or a system command', 'keyword': 'WScript.Shell', 'type': 'Suspicious'}, {'description': 'May run an executable file or a system command', 'keyword': 'Run', 'type': 'Suspicious'}, {'description': 'May run PowerShell commands', 'keyword': 'powershell', 'type': 'Suspicious'}, {'description': '9BA55BE5', 'keyword': 'xxx', 'type': 'Hex String'}, ''' analyzer_results = vbaparser.analyze_macros( show_decoded_strings=True) # it gives None if it does not find anything if analyzer_results: analyze_macro_results = [] for kw_type, keyword, description in analyzer_results: if kw_type != 'Hex String': analyze_macro_result = { "type": kw_type, "keyword": keyword, "description": description } analyze_macro_results.append(analyze_macro_result) olevba_results['analyze_macro'] = analyze_macro_results olevba_results['reveal'] = vbaparser.reveal() vbaparser.close() except Exception as e: traceback.print_exc() error_message = "job_id {} vba parser failed. Error: {}".format( job_id, e) logger.exception(error_message) report['errors'].append(error_message) results['olevba'] = olevba_results # mraptor macro_raptor = mraptor.MacroRaptor(olevba_results.get('reveal', '')) if macro_raptor: macro_raptor.scan() results[ 'mraptor'] = "suspicious" if macro_raptor.suspicious else 'ok' # pprint.pprint(results) report['report'] = results except AnalyzerRunException as e: error_message = "job_id:{} analyzer:{} md5:{} filename: {} Analyzer Error {}" \ "".format(job_id, analyzer_name, md5, filename, e) logger.error(error_message) report['errors'].append(error_message) report['success'] = False except Exception as e: traceback.print_exc() error_message = "job_id:{} analyzer:{} md5:{} filename: {} Unexpected Error {}" \ "".format(job_id, analyzer_name, md5, filename, e) logger.exception(error_message) report['errors'].append(str(e)) report['success'] = False else: report['success'] = True general.set_report_and_cleanup(job_id, report, logger) logger.info("ended analyzer {} job_id {}" "".format(analyzer_name, job_id)) return report
class DocInfo(FileAnalyzer): def set_config(self, additional_config_params): self.olevba_results = {} self.vbaparser = None self.experimental = additional_config_params.get("experimental", False) self.passwords_to_check = [] # this is to extract the passwords for encryption requested by the client # you can use pyintelowl to send additional passwords to check for # example: # "additional_configuration": { # "Doc_Info_Experimental": { # "additional_passwords_to_check": ["testpassword"] # } # }, additional_passwords_to_check = additional_config_params.get( "additional_passwords_to_check", [] ) if isinstance(additional_passwords_to_check, list): self.passwords_to_check.extend(additional_passwords_to_check) def run(self): results = {} # olevba try: self.vbaparser = VBA_Parser(self.filepath) self.manage_encrypted_doc() if self.experimental: self.experimental_analysis() # go on with the normal oletools execution self.olevba_results["macro_found"] = self.vbaparser.detect_vba_macros() if self.olevba_results["macro_found"]: vba_code_all_modules = "" macro_data = [] for ( v_filename, stream_path, vba_filename, vba_code, ) in self.vbaparser.extract_macros(): extracted_macro = { "filename": v_filename, "ole_stream": stream_path, "vba_filename": vba_filename, "vba_code": vba_code, } macro_data.append(extracted_macro) vba_code_all_modules += vba_code + "\n" self.olevba_results["macro_data"] = macro_data # example output # # {'description': 'Runs when the Word document is opened', # 'keyword': 'AutoOpen', # 'type': 'AutoExec'}, # {'description': 'May run an executable file or a system command', # 'keyword': 'Shell', # 'type': 'Suspicious'}, # {'description': 'May run an executable file or a system command', # 'keyword': 'WScript.Shell', # 'type': 'Suspicious'}, # {'description': 'May run an executable file or a system command', # 'keyword': 'Run', # 'type': 'Suspicious'}, # {'description': 'May run PowerShell commands', # 'keyword': 'powershell', # 'type': 'Suspicious'}, # {'description': '9BA55BE5', 'keyword': 'xxx', 'type': 'Hex String'}, # mraptor macro_raptor = mraptor.MacroRaptor(vba_code_all_modules) if macro_raptor: macro_raptor.scan() results["mraptor"] = ( "suspicious" if macro_raptor.suspicious else "ok" ) # analyze macros analyzer_results = self.vbaparser.analyze_macros() # it gives None if it does not find anything if analyzer_results: analyze_macro_results = [] for kw_type, keyword, description in analyzer_results: if kw_type != "Hex String": analyze_macro_result = { "type": kw_type, "keyword": keyword, "description": description, } analyze_macro_results.append(analyze_macro_result) self.olevba_results["analyze_macro"] = analyze_macro_results except CannotDecryptException as e: logger.info(e) except Exception as e: error_message = f"job_id {self.job_id} vba parser failed. Error: {e}" logger.exception(error_message) self.report["errors"].append(error_message) finally: if self.vbaparser: self.vbaparser.close() results["olevba"] = self.olevba_results return results def manage_encrypted_doc(self): self.olevba_results["is_encrypted"] = False # checks if it is an OLE file. That could be encrypted if self.vbaparser.ole_file: # check if the ole file is encrypted is_encrypted = self.vbaparser.detect_is_encrypted() self.olevba_results["is_encrypted"] = is_encrypted # in the case the file is encrypted I try to decrypt it # with the default password and the most common ones if is_encrypted: # by default oletools contains some basic passwords # we just add some more guesses common_pwd_to_check = [] for num in range(10): common_pwd_to_check.append(f"{num}{num}{num}{num}") # https://twitter.com/JohnLaTwC/status/1265377724522131457 filename_without_spaces_and_numbers = sub("[-_\d\s]", "", self.filename) filename_without_extension = sub( "(\..+)", "", filename_without_spaces_and_numbers ) common_pwd_to_check.append(filename_without_extension) self.passwords_to_check.extend(common_pwd_to_check) decrypted_file_name = self.vbaparser.decrypt_file( self.passwords_to_check ) self.olevba_results[ "additional_passwords_tried" ] = self.passwords_to_check if decrypted_file_name: self.vbaparser = VBA_Parser(decrypted_file_name) else: self.olevba_results["cannot_decrypt"] = True raise CannotDecryptException( "cannot decrypt the file with the default password" ) def experimental_analysis(self): self.manage_xlm_macros() def manage_xlm_macros(self): self.olevba_results["xlm_macro"] = False # check if the file contains an XLM macro # and try an experimental parsing # credits to https://twitter.com/gabriele_pippi for the idea if self.vbaparser.detect_xlm_macros(): self.olevba_results["xlm_macro"] = True logger.debug("experimental XLM macro analysis start") parsed_file = b"" try: excel_doc = XLSWrapper2(self.filepath) ae_list = [ "auto_open", "auto_close", "auto_activate", "auto_deactivate", ] self.olevba_results["xlm_macro_autoexec"] = [] for ae in ae_list: auto_exec_labels = excel_doc.get_defined_name(ae, full_match=False) for label in auto_exec_labels: self.olevba_results["xlm_macro_autoexec"].append(label[0]) for i in show_cells(excel_doc): rec_str = "" if len(i) == 5: # rec_str = 'CELL:{:10}, {:20}, {}' # .format(i[0].get_local_address(), i[2], i[4]) if i[2] != "None": rec_str = "{:20}".format(i[2]) if rec_str: parsed_file += rec_str.encode() parsed_file += b"\n" except Exception as e: logger.info(f"experimental XLM macro analysis failed. Exception: {e}") else: logger.debug( f"experimental XLM macro analysis succeded. " f"Binary to analyze: {parsed_file}" ) if parsed_file: self.vbaparser = VBA_Parser(self.filename, data=parsed_file)
def get_report(self): """ Return oletools report or create if not already cached. """ if self.sample.oletools_report is not None: return self.sample.oletools_report report = { 'autoexec': [], 'suspicious' : [], } filename = self.sample.filename try: vbaparser = VBA_Parser(filename, data=self.sample.content) # VBA_Parser reports macros for office documents report['has_macros'] = vbaparser.detect_vba_macros() or vbaparser.detect_xlm_macros() try: report['vba'] = vbaparser.reveal() except TypeError: # office document with no macros pass # When called on an empty or text document oletools will falsely # report that it contains macros and returns a one item list of # macros which contains only the filename again. # # Oletool assume the submitted file is the plain text macro if # it can not determine another file type. # # Add a workaround to detect this behaviour and override the # result. all_macros = vbaparser.extract_all_macros() if (report['has_macros'] and len(all_macros) == 1 and isinstance(all_macros[0], tuple) and len(all_macros[0]) >= 3 and all_macros[0][2] == filename): report['has_macros'] = False if vbaparser.detect_vba_macros(): vb_code = vbaparser.extract_all_macros() for (_, _, _, c) in vb_code: autoexec = detect_autoexec(c) if len(autoexec) >= 1: report['autoexec'].extend(autoexec) suspicious = detect_suspicious(c) if len(suspicious) >= 1: report['suspicious'].extend(suspicious) vbaparser.close() except IOError: raise except (TypeError, FileOpenError): # The given file is not an office document. pass except Exception as error: logger.exception(error) report = OletoolsReport(report) self.sample.register_oletools_report(report) return report
def parse_vba(file): print_output(file, '\n\n-----------------------------------------\n[Analyzing with olevba]\n-----------------------------------------\n', 'text') ole_macro_result = 'no vb-macro' has_macros = False indicators = [] macro_indicators = [] vbaparser = VBA_Parser(filescanner_proc_dir+file) # Check for Macros if not vbaparser.detect_vba_macros(): print_output(file, '[-] No Macros Found', 'text') return has_macros, ole_macro_result, indicators if True: print_output(file, '[-] MACROS FOUND', 'text') has_macros = True ole_macro_result = 'VB-MACRO FOUND' # Variable to be passed to MacroRaptor vba_code_all_modules = '' for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_all_macros(): vba_code_all_modules += vba_code + '\n' for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): print_output(file, '\nOLE Stream: {0}'.format(string_clean(stream_path)), 'text') print_output(file, 'VBA Filename: {0}'.format(string_clean(vba_filename)), 'text') # Analyse the VBA Code results = vbaparser.analyze_macros(show_decoded_strings=True) for kw_type, keyword, description in results: # Add IOC detections to indicators list if kw_type == 'IOC': indicators.append(description+': '+keyword) print_output(file, '{} - {} - {}'.format(kw_type, keyword, description), 'shell') else: print_output(file, '{} - {} - {}'.format(kw_type, keyword, description), 'text') # Deobfusgate and return macro code # print_output(file, '\n'+vbaparser.reveal(), 'text') # Print number of items in each category and append to indicators list print_output(file, '', 'shell') print_output(file, 'AutoExec keywords: {}'.format(vbaparser.nb_autoexec), 'shell') if vbaparser.nb_autoexec != 0: indicators.append('AutoExec keywords: {}'.format(vbaparser.nb_autoexec)) print_output(file, 'Suspicious keywords: {}'.format(vbaparser.nb_suspicious), 'shell') if vbaparser.nb_suspicious != 0: indicators.append('Suspicious keywords: {}'.format(vbaparser.nb_suspicious)) print_output(file, 'IOCs: {}'.format(vbaparser.nb_iocs), 'shell') print_output(file, 'Hex obfuscated strings: {}'.format(vbaparser.nb_hexstrings), 'shell') if vbaparser.nb_hexstrings != 0: indicators.append('Hex obfuscated strings: {}'.format(vbaparser.nb_hexstrings)) print_output(file, 'Base64 obfuscated strings: {}'.format(vbaparser.nb_base64strings), 'shell') if vbaparser.nb_base64strings != 0: indicators.append('Base64 obfuscated strings: {}'.format(vbaparser.nb_base64strings)) print_output(file, 'Dridex obfuscated strings: {}'.format(vbaparser.nb_dridexstrings), 'shell') if vbaparser.nb_dridexstrings != 0: indicators.append('Dridex obfuscated strings: {}'.format(vbaparser.nb_dridexstrings)) print_output(file, 'VBA obfuscated strings: {}'.format(vbaparser.nb_vbastrings), 'shell') if vbaparser.nb_vbastrings != 0: indicators.append('VBA obfuscated strings: {}'.format(vbaparser.nb_vbastrings)) # Update indicators list with matches from MRaptor macro_indicators = scan_macro(file, vba_code_all_modules) indicators = indicators + macro_indicators # Use oledump to gather VBA code for archiving oledump_scan(file, '-p plugin_vba_summary.py') # Close the file vbaparser.close() return has_macros, ole_macro_result, indicators
print ("- "*39) print ("vba_code",vba_code) vba_scanner = VBA_Scanner(vba_code) results = vba_scanner.scan(include_decoded_strings=True) for kw_type, keyword, description in results: print ("type=%s - keyword=%s - description=%s" % (kw_type, keyword, description)) autoexec_keywords = detect_autoexec(vba_code) if autoexec_keywords: print("Auto-executable Macro keyword is detected. :") for keyword, description in autoexec_keywords: print("%s: %s' % (keyword, description)") else: print ("Auto-executable Macro keywords is not detected.") results = vbaparser.analyze_macros() for kw_type, keyword, description in results: print ("type=%s - keyword=%s - description=%s" % (kw_type, keyword, description)) print ("AutoExec keywords: %d" % vbaparser.nb_autoexec) print ("Suspicious keywords: %d" % vbaparser.nb_suspicious) print ("IOCs: %d" % vbaparser.nb_iocs) print ("Hex obfuscated strings: %d" % vbaparser.nb_hexstrings) print ("Base64 obfuscated strings: %d" % vbaparser.nb_base64strings) print ("Dridex obfuscated strings: %d" % vbaparser.nb_dridexstrings) print ("VBA obfuscated strings: %d" % vbaparser.nb_vbastrings) # 퍼져 닫기 vbaparser.close()
if indicator > 0: print(chr(27) + "[2J") print"\n\n\n\n\t\t\t\t==============================" print "\t\t\t\t\033[91m ALERT\033[0m" print "\t\t\t\t\033[91mMalicious Indicators Detected\033[0m" print "\t\t\t\t==============================\n\n\n\n" print "\n\n \033[91mPlease, take the time to understand that this file could be a potential risk for your organization\033[0m\n\n" else: print "We have NOT found VBA Macros, but proceed with caution concerning links" print "Please, check a link before clicking on it, especially, if the sender is unknown\n\n" sys.stdout.close() # DO NOT FORGET TO CLOSE THE VBAPARSER vbaparser.close() #GUI part top = Tkinter.Tk() output = open("output.txt", "r") con_out = output.read() output.close() def helloCallBack(): tx_tk = Tkinter.Tk() text = Tkinter.Text(tx_tk, height = 500, width = 500) text.pack() text.insert(Tkinter.INSERT, con_out) tx_tk.mainloop() #tkMessageBox.showinfo("Built for you by SIA GROUP") #, show_joint_messages()) B = Tkinter.Button( top,