def olevba_trig(file): try: vbaparser = VBA_Parser(file) if vbaparser.detect_vba_macros(): for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): print('Filename :', filename) print('OLE stream :', stream_path) print('VBA filename:', vba_filename) print('- ' * 39) print(vba_code) print('- ' * 39) 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) print("\n") except: e = sys.exc_info()[0] print(f'{e} from {file}')
def EXTRACT_VBA_MACRO(s, buff): EXTRACT_MACRO = {} counter = 0 ### TODO: REMOVE THIS WORKAROUND ONCE MODULE AUTHOR FIXES CODE ### ### Reference: http://stackoverflow.com/questions/32261679/strange-issue-using-logging-module-in-python/32264445#32264445 ### Reference: https://bitbucket.org/decalage/oletools/issues/26/use-of-logger ### /dev/null used instead of NullHandler for 2.6 compatibility logging.getLogger('workaround').root.addHandler( logging.FileHandler('/dev/null')) ### vba = VBA_Parser('None', data=buff) if not vba.detect_vba_macros(): return EXTRACT_MACRO for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros(): CHILD_MACRO = OrderedDict([('OLE Stream', stream_path), ('VBA Filename', vba_filename.decode('ascii', 'ignore')), ('Scan', scan_macro(vba_code)), ('Buffer', vba_code)]) EXTRACT_MACRO['Object_%s' % counter] = CHILD_MACRO counter += 1 return EXTRACT_MACRO
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 EXTRACT_VBA_MACRO(s, buff): EXTRACT_MACRO = {} counter = 0 ### TODO: REMOVE THIS WORKAROUND ONCE MODULE AUTHOR FIXES CODE ### ### Reference: http://stackoverflow.com/questions/32261679/strange-issue-using-logging-module-in-python/32264445#32264445 ### Reference: https://bitbucket.org/decalage/oletools/issues/26/use-of-logger ### /dev/null used instead of NullHandler for 2.6 compatibility logging.getLogger('workaround').root.addHandler(logging.FileHandler('/dev/null')) ### vba = VBA_Parser('None', data=buff) if not vba.detect_vba_macros(): return EXTRACT_MACRO for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros(): CHILD_MACRO = OrderedDict([('OLE Stream', stream_path), ('VBA Filename', vba_filename.decode('ascii', 'ignore')), ('Scan', scan_macro(vba_code)), ('Buffer', vba_code)]) EXTRACT_MACRO['Object_%s' % counter] = CHILD_MACRO counter += 1 return EXTRACT_MACRO
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 process_file_scanexpr (container, filename, data): """ Process a single file :param container: str, path and filename of container if the file is within a zip archive, None otherwise. :param filename: str, path and filename of file on disk, or within the container. :param data: bytes, content of the file if it is in a container, None if it is a file on disk. """ #TODO: replace print by writing to a provided output file (sys.stdout by default) if container: display_filename = '%s in %s' % (filename, container) else: display_filename = filename print '='*79 print 'FILE:', display_filename all_code = '' try: #TODO: handle olefile errors, when an OLE file is malformed vba = VBA_Parser(filename, data) print 'Type:', vba.type if vba.detect_vba_macros(): #print 'Contains VBA Macros:' for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): # hide attribute lines: #TODO: option to disable attribute filtering vba_code_filtered = filter_vba(vba_code) print '-'*79 print 'VBA MACRO %s ' % vba_filename print 'in file: %s - OLE stream: %s' % (subfilename, repr(stream_path)) print '- '*39 # detect empty macros: if vba_code_filtered.strip() == '': print '(empty macro)' else: # TODO: option to display code print vba_code_filtered vba_code = vba_collapse_long_lines(vba_code) all_code += '\n' + vba_code print '-'*79 print 'EVALUATED VBA EXPRESSIONS:' t = prettytable.PrettyTable(('Obfuscated expression', 'Evaluated value')) t.align = 'l' t.max_width['Obfuscated expression'] = 36 t.max_width['Evaluated value'] = 36 for expression, expr_eval in scan_expressions(all_code): t.add_row((repr(expression), repr(expr_eval))) print t else: print 'No VBA macros found.' except: #TypeError: #raise #TODO: print more info if debug mode #print sys.exc_value # display the exception with full stack trace for debugging, but do not stop: traceback.print_exc() print ''
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 getVBA(self, myfile, source='filepath'): ''' Given a file, parses out the stream paths, vba code, and vba filenames for each. :param myfile: filename :param source: type of data being passed in. Either "filepath" to indicate we need to read from disk or "filecontents" meaning that the file contents are being passed as a parameter. :return: pandas Series that can be used in concert with the pandas DataFrame apply method ''' if source == 'filepath': filedata = open(myfile, 'rb').read() else: filedata = myfile entry = {} try: vbaparser = VBA_Parser('mmbot', data=filedata) allcode = '' pathnames = '' filenames = '' if vbaparser.detect_vba_macros(): filenameslist = [] pathnameslist = [] vbacodelist = [] for (filename, stream_path, filename_vba, extracted_vba) in vbaparser.extract_macros(): vbacodelist.append(extracted_vba.decode("ascii", "ignore")) #vbacodelist.append(extracted_vba.decode('utf8', 'ignore')) if pathnames is None: pathnameslist.append( stream_path.decode("ascii", "ignore")) filenameslist.append( filename_vba.decode("ascii", "ignore")) else: pathnameslist.append( stream_path.decode("ascii", "ignore")) filenameslist.append( filename_vba.decode("ascii", "ignore")) allcode = "\n\n\n\n".join(vbacodelist) filenames = ", ".join(filenameslist) pathnames = ", ".join(pathnameslist) else: pathnames = 'No VBA Macros found' filenames = 'No VBA Macros found' allcode = 'No VBA Macros found' except Exception as e: pathnames = 'Error:' + str(e) filenames = 'Error:' + str(e) allcode = 'Error:' + str(e) return pd.Series({ 'extracted_vba': allcode, 'stream_path': pathnames, 'filename_vba': filenames })
def get_macros(): extracted_macros = [] macro_analysis = [] tags = [] try: vbaparser = VBA_Parser('/sample') vbaparser.detect_vba_macros() for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): extracted_macros.append({ "stream_path": stream_path, "vba_filename": vba_filename, "vba_code": vba_code }) try: for kw_type, keyword, description in vbaparser.analyze_macros(): macro_analysis.append({ "kw_type": kw_type, "keyword": keyword, "description": description }) if keyword == 'Shell': tags.append('run-file') except TypeError: pass macro_suspicious_categories = { "nb_macros": vbaparser.nb_macros, "nb_autoexec": vbaparser.nb_autoexec, "nb_suspicious": vbaparser.nb_suspicious, "nb_iocs": vbaparser.nb_iocs, "nb_hexstrings": vbaparser.nb_hexstrings, "nb_base64strings": vbaparser.nb_base64strings, "nb_dridexstrings": vbaparser.nb_dridexstrings, "nb_vbastrings": vbaparser.nb_vbastrings, } if vbaparser.nb_macros: tags.append('macros') if vbaparser.nb_hexstrings or vbaparser.nb_base64strings: tags.append('obfuscated') except FileOpenError: return None, tags return { "extracted_macros": extracted_macros, "macro_analysis": macro_analysis, "macro_suspicious_categories": macro_suspicious_categories }, tags
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 vbaparsing(filename): vbafile = VBA_Parser(filename) results = "" for (filename, stream_path, vba_filename, vba_code) in vbafile.extract_macros(): results = results + vba_code result = vbafile.analyze_macros() for kw_type, keyword, description in result: results = results + 'type=%s - keyword=%s - description=%s' % ( kw_type, keyword, description) return results
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 extract_src_codes(file): dic = {} parser = VBA_Parser(file) for (_, sname, _, code) in parser.extract_macros(): sname = sname.split("/")[-1] if sname not in dic: lines = code.split("\n")[1: ] dic[sname] = "\n".join(lines) # remove the first line else: printError(f"Error in src_codes: repeat {sname}") return dic
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 POE(logdir, targetfile, logging, debug): if (logging == True): LOG = logger() newlogentry = '' macro_dump_data = '' FI = fileio() try: filedata = open(targetfile.filename, 'rb').read() vbaparser = VBA_Parser(targetfile.filename, data=filedata) if vbaparser.detect_vba_macros(): print '[*] VBA macros found - Extracting...\n' if (logging == True): newlogentry = 'VBA macros found - Extracting...' LOG.WriteLog(logdir, targetfile.filename, newlogentry) for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): macro_dump_data += '-' * 79 + '\n' try: macro_dump_data += 'Filename :' + filename.encode("ascii", "replace") + '\n' macro_dump_data += 'OLE stream :' + stream_path.encode("ascii", "replace") + '\n' macro_dump_data += 'VBA filename:' + vba_filename.encode("ascii", "replace") + '\n' except Exception, e: print '[x] Current macro - unable to print Filename, OLE stream or VBA filename due to encoding issue (Unicode?): ', e macro_dump_data += '-' * 79 + '\n' macro_dump_data += vba_code FI.WriteLogFile(logdir + vba_filename, macro_dump_data) try: print '[*] Macro ' + vba_filename.encode("ascii", "replace") + ' extracted to: ' + logdir + vba_filename.encode("ascii", "replace") targetfile.macros.append(logdir + vba_filename.encode("ascii", "replace")) if (logging == True): newlogentry = 'Macro ' + vba_filename.encode("ascii", "replace") + ' extracted to: <a href=\"' + logdir + vba_filename.encode("ascii", "replace") + '\">' + vba_filename.encode("ascii", "replace") + '</a>' LOG.WriteLog(logdir, targetfile.filename, newlogentry) if (debug == True): print '-'*79 print 'Filename :', filename.encode("ascii", "replace") print 'OLE stream :', stream_path.encode("ascii", "replace") print 'VBA filename:', vba_filename.encode("utf-8", "ignore") print '-'*79 except Exception, e: print '[x] Current macro - unable print Filename, OLE stream or VBA filename due to encoding issue: (Unicode?)', e if (debug == True): print vba_code macro_dump_data = '' print 'Macro List' for mlist in targetfile.macros: print mlist
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 get_vba_source_from_excel(filename): vbaparser = VBA_Parser(filename) if vbaparser.detect_vba_macros(): print('VBA Macros found') else: print('No VBA Macros found') for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): print('-' * 79) print('Filename :', filename) print('OLE stream :', stream_path) print('VBA filename:', vba_filename) print('- ' * 39) print(vba_code)
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 _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 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 detect(self, filename): return_list = [] vbaparser = VBA_Parser(filename) if vbaparser.detect_vba_macros(): for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): return_list.append({ 'filename': filename, 'ole_stream': stream_path, 'vba_filename': vba_filename, 'vba_code': vba_code }) results = vbaparser.analyze_macros() for kw_type, keyword, description in results: return_list.append({ 'type': kw_type, 'keyword': keyword, 'description': description }) return_list.append({'revealed_macro': vbaparser.reveal()}) return return_list else: return None
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 check_for_macros(self, filename, file_contents, request_hash): # noinspection PyBroadException try: vba_parser = VBA_Parser(filename=filename, data=file_contents) try: if vba_parser.detect_vba_macros(): self.ole_result.add_tag(TAG_TYPE.TECHNIQUE_MACROS, "Contains VBA Macro(s)", weight=TAG_WEIGHT.LOW, usage=TAG_USAGE.IDENTIFICATION) try: for (subfilename, stream_path, vba_filename, vba_code) in vba_parser.extract_macros(): if vba_code.strip() == '': continue vba_code_sha256 = hashlib.sha256(vba_code).hexdigest() if vba_code_sha256 == request_hash: continue self.all_vba.append(vba_code) macro_section = self.macro_section_builder(vba_code) toplevel_score = self.calculate_nested_scores(macro_section) self.all_macros.append(Macro(vba_code, vba_code_sha256, macro_section, toplevel_score)) except Exception as e: self.log.debug("OleVBA VBA_Parser.extract_macros failed: {}".format(str(e))) section = ResultSection(SCORE.NULL, "OleVBA : Error extracting macros") self.ole_result.add_section(section) except Exception as e: self.log.debug("OleVBA VBA_Parser.detect_vba_macros failed: {}".format(e)) section = ResultSection(SCORE.NULL, "OleVBA : Error parsing macros: {}".format(e)) self.ole_result.add_section(section) except: self.log.debug("OleVBA VBA_Parser constructor failed, may not be a supported OLE document")
def process_file(container, filename, data, altparser=False): """ Process a single file :param container: str, path and filename of container if the file is within a zip archive, None otherwise. :param filename: str, path and filename of file on disk, or within the container. :param data: bytes, content of the file if it is in a container, None if it is a file on disk. """ #TODO: replace print by writing to a provided output file (sys.stdout by default) if container: display_filename = '%s in %s' % (filename, container) else: display_filename = filename print '=' * 79 print 'FILE:', display_filename vm = ViperMonkey() try: #TODO: handle olefile errors, when an OLE file is malformed vba = VBA_Parser(filename, data) print 'Type:', vba.type if vba.detect_vba_macros(): #print 'Contains VBA Macros:' for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): # hide attribute lines: #TODO: option to disable attribute filtering vba_code_filtered = filter_vba(vba_code) print '-' * 79 print 'VBA MACRO %s ' % vba_filename print 'in file: %s - OLE stream: %s' % (subfilename, repr(stream_path)) print '- ' * 39 # detect empty macros: if vba_code_filtered.strip() == '': print '(empty macro)' else: # TODO: option to display code vba_code = vba_collapse_long_lines(vba_code) print '-' * 79 print 'VBA CODE (with long lines collapsed):' print vba_code print '-' * 79 print 'PARSING VBA CODE:' try: if altparser: vm.add_module2(vba_code) else: vm.add_module(vba_code) except ParseException as err: print err.line print " " * (err.column - 1) + "^" print err print '-' * 79 print 'TRACING VBA CODE (entrypoint = Auto*):' vm.trace() # print table of all recorded actions print('Recorded Actions:') print(vm.dump_actions()) else: print 'No VBA macros found.' except: #TypeError: #raise #TODO: print more info if debug mode #print sys.exc_value # display the exception with full stack trace for debugging, but do not stop: traceback.print_exc() print ''
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 _parse(self, filepath: str) -> Dict[str, Any]: """Parses an office document for static information. @param filepath: Path to the file to be analyzed. @return: results dict or None """ results = {} if not HAVE_OLETOOLS: return results vba = False if is_rtf(filepath): try: with open(filepath, "rb") as f: contents = f.read() temp_results = self._parse_rtf(contents) if temp_results: results["office_rtf"] = temp_results except Exception as e: log.error(e, exc_info=True) else: try: vba = VBA_Parser(filepath) except ValueError as e: log.error("Error VBA_Parser: %s", str(e)) except Exception: return results try: # extract DDE dde = extract_dde(filepath) if dde: results["office_dde"] = convert_to_printable(dde) except (csv_error, UnicodeDecodeError): pass except AttributeError: log.warning( "OleFile library bug: AttributeError! fix: pip3 install -U olefile" ) except Exception as e: log.error(e, exc_info=True) officeresults = {"Metadata": {}} macro_folder = os.path.join(CUCKOO_ROOT, "storage", "analyses", self.task_id, "macros") if olefile.isOleFile(filepath): with olefile.OleFileIO(filepath) as ole: meta = ole.get_metadata() # must be left this way or we won't see the results officeresults["Metadata"] = self._get_meta(meta) else: with contextlib.suppress(KeyError): officeresults["Metadata"] = self._get_xml_meta(filepath) if vba and vba.detect_vba_macros(): officeresults["Metadata"]["HasMacros"] = "Yes" # Create IOC and category vars. We do this before processing the # macro(s) to avoid overwriting data when there are multiple # macros in a single file. officeresults["Macro"] = {"Code": {}, "info": {}, "Analysis": {}} ctr = 0 try: for _, _, vba_filename, vba_code in vba.extract_macros(): vba_code = filter_vba(vba_code) if vba_code.strip() != "": # Handle all macros ctr += 1 outputname = f"Macro{ctr}" officeresults["Macro"]["Code"][outputname] = [ (convert_to_printable(vba_filename), convert_to_printable(vba_code)) ] if not os.path.exists(macro_folder): os.makedirs(macro_folder) macro_file = os.path.join(macro_folder, outputname) with open(macro_file, "w") as f: f.write(convert_to_printable(vba_code)) officeresults["Macro"]["info"][outputname] = { "yara_macro": File(macro_file).get_yara(category="macro") } officeresults["Macro"]["info"][outputname][ "yara_macro"].extend( File(macro_file).get_yara(category="CAPE")) try: iocs = vbadeobf.parse_macro(vba_code) for pattern, match in iocs: officeresults["Macro"]["Analysis"].setdefault( "IOCs", []).append((pattern, match)) except ValueError as e: log.error("Can't parse macros for %s - %s ", filepath, str(e)) except Exception as e: log.error(e, exc_info=True) for keyword, description in detect_autoexec(vba_code): officeresults["Macro"]["Analysis"].setdefault( "AutoExec", []).append( (keyword.replace(".", "_"), description)) for keyword, description in detect_suspicious( vba_code): officeresults["Macro"]["Analysis"].setdefault( "Suspicious", []).append( (keyword.replace(".", "_"), description)) for encoded, decoded in detect_hex_strings(vba_code): officeresults["Macro"]["Analysis"].setdefault( "HexStrings", []).append( (encoded, convert_to_printable(decoded))) except (AssertionError, UnexpectedDataError) as e: log.warning("Macros in static.py", e) if HAVE_VBA2GRAPH: vba2graph_func(filepath, self.task_id, self.sha256) else: officeresults["Metadata"]["HasMacros"] = "No" try: for indicator in OleID(filepath).check(): if indicator.value and indicator.name in { "Word Document", "Excel Workbook", "PowerPoint Presentation" }: officeresults["Metadata"]["DocumentType"] = indicator.name except Exception as e: log.error(e, exc_info=True) if HAVE_XLM_DEOBF: tmp_xlmmacro = xlmdeobfuscate(filepath, self.task_id, self.options.get("password", "")) if tmp_xlmmacro: officeresults["XLMMacroDeobfuscator"] = tmp_xlmmacro return officeresults
def process_file_scanexpr (container, filename, data): """Process a single file. @param container (str) Path and filename of container if the file is within a zip archive, None otherwise. @param filename (str) path and filename of file on disk, or within the container. @param data (bytes) Content of the file if it is in a container, None if it is a file on disk. """ #TODO: replace print by writing to a provided output file (sys.stdout by default) if container: display_filename = '%s in %s' % (filename, container) else: display_filename = filename safe_print('='*79) safe_print('FILE: ' + safe_str_convert(display_filename)) all_code = '' try: #TODO: handle olefile errors, when an OLE file is malformed import oletools oletools.olevba.enable_logging() if (log.getEffectiveLevel() == logging.DEBUG): log.debug('opening %r' % filename) vba = VBA_Parser(filename, data, relaxed=True) if vba.detect_vba_macros(): # Read in document metadata. vm = core.ViperMonkey(filename, data) ole = olefile.OleFileIO(filename) try: vm.set_metadata(ole.get_metadata()) except Exception as e: log.warning("Reading in metadata failed. Trying fallback. " + safe_str_convert(e)) vm.set_metadata(get_metadata_exif(filename)) #print 'Contains VBA Macros:' for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): # hide attribute lines: #TODO: option to disable attribute filtering vba_code = filter_vba(vba_code) safe_print('-'*79) safe_print('VBA MACRO %s ' % vba_filename) safe_print('in file: %s - OLE stream: %s' % (subfilename, repr(stream_path))) safe_print('- '*39) # detect empty macros: if vba_code.strip() == '': safe_print('(empty macro)') else: # TODO: option to display code safe_print(vba_code) vba_code = core.vba_collapse_long_lines(vba_code) all_code += '\n' + vba_code safe_print('-'*79) safe_print('EVALUATED VBA EXPRESSIONS:') t = prettytable.PrettyTable(('Obfuscated expression', 'Evaluated value')) t.align = 'l' t.max_width['Obfuscated expression'] = 36 t.max_width['Evaluated value'] = 36 for expression, expr_eval in core.scan_expressions(all_code): t.add_row((repr(expression), repr(expr_eval))) safe_print(t) else: safe_print('No VBA macros found.') except Exception as e: log.error("Caught exception. " + safe_str_convert(e)) if (log.getEffectiveLevel() == logging.DEBUG): traceback.print_exc() safe_print('')
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 POE(logdir, targetfile, logging, debug): if (logging == True): LOG = logger() newlogentry = '' macro_dump_data = '' FI = fileio() try: filedata = open(targetfile.filename, 'rb').read() vbaparser = VBA_Parser(targetfile.filename, data=filedata) if vbaparser.detect_vba_macros(): print '[*] VBA macros found - Extracting...\n' if (logging == True): newlogentry = 'VBA macros found - Extracting...' LOG.WriteLog(logdir, targetfile.filename, newlogentry) for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): macro_dump_data += '-' * 79 + '\n' try: macro_dump_data += 'Filename :' + filename.encode( "ascii", "replace") + '\n' macro_dump_data += 'OLE stream :' + stream_path.encode( "ascii", "replace") + '\n' macro_dump_data += 'VBA filename:' + vba_filename.encode( "ascii", "replace") + '\n' except Exception, e: print '[x] Current macro - unable to print Filename, OLE stream or VBA filename due to encoding issue (Unicode?): ', e macro_dump_data += '-' * 79 + '\n' macro_dump_data += vba_code FI.WriteLogFile(logdir + vba_filename, macro_dump_data) try: print '[*] Macro ' + vba_filename.encode( "ascii", "replace" ) + ' extracted to: ' + logdir + vba_filename.encode( "ascii", "replace") targetfile.macros.append( logdir + vba_filename.encode("ascii", "replace")) if (logging == True): newlogentry = 'Macro ' + vba_filename.encode( "ascii", "replace" ) + ' extracted to: <a href=\"' + logdir + vba_filename.encode( "ascii", "replace") + '\">' + vba_filename.encode( "ascii", "replace") + '</a>' LOG.WriteLog(logdir, targetfile.filename, newlogentry) if (debug == True): print '-' * 79 print 'Filename :', filename.encode( "ascii", "replace") print 'OLE stream :', stream_path.encode( "ascii", "replace") print 'VBA filename:', vba_filename.encode( "utf-8", "ignore") print '-' * 79 except Exception, e: print '[x] Current macro - unable print Filename, OLE stream or VBA filename due to encoding issue: (Unicode?)', e if (debug == True): print vba_code macro_dump_data = '' print 'Macro List' for mlist in targetfile.macros: print mlist
from oletools.olevba import VBA_Parser else: #python 3 from oletools.olevba3 import VBA_Parser # VBA 퍼져 온 vbaparser = VBA_Parser(sys.argv[1]) # 매크로 검사 if not vbaparser.detect_vba_macros(): print ('error', "macro not detected ") #경고모듈 넣는 위치 sys.exit(1) print ('info', "macro detected ! ") for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): print ("- "*79) print ("filename :", filename) print ("OLE stream :", stream_path) print ("VBA filename:", vba_filename) 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:
description='Analyze docx document (pretty limited for now)') parser.add_argument('FILE', help='Docx document') args = parser.parse_args() if not os.path.isfile(args.FILE): print("Invalid file path") sys.exit(1) # Check if any macro and extract it vbaparser = VBA_Parser(args.FILE) if vbaparser.detect_vba_macros(): print('VBA Macros found') mac_name = os.path.splitext(args.FILE)[0] + '.macro' with open(mac_name, 'w+') as f: for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros(): f.write(vba_code) f.write('\n') print("Macro dumped in {}".format(mac_name)) else: print('No VBA Macros found') # Show metadata input_zip = ZipFile(args.FILE) print("") print("Metadata:") for d in extract_metadata(input_zip): print("-{} : {}".format(*d)) # Analyze types of files print("")
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
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 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