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 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 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 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 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 parseOLEDocument(f): """Parse an OLE document for VBA macros""" if not f or not useOLETools: return writeLog('DEBUG: Analyzing with oletools') try: v = VBA_Parser(f) except: writeLog("Not a supported file format: %s" % f) return writeLog('DEBUG: Detected file type: %s' % v.type) if v.detect_vba_macros(): writeLog('DEBUG: VBA Macros found') try: t = open("%s.analysis" % f, 'w') except IOError as e: writeLog("Cannot create analysis file %s.analysis: %s" % (f,e.strerror)) return for kw_type, keyword, description in v.analyze_macros(): t.write("%-12s | %-25s | %s\n" % (kw_type, keyword, description)) t.close() writeLog("DEBUG: Analysis dumped to %s.analysis" % f) else: writeLog('DEBUG: No VBA Macros found') return
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 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 parseOLEDocument(f): """Parse an OLE document for VBA macros""" if not f or not useOLETools: return writeLog('DEBUG: Analyzing with oletools') try: v = VBA_Parser(f) except: writeLog("Not a supported file format: %s" % f) return writeLog('DEBUG: Detected file type: %s' % v.type) # Hack: Search for a .js extension fname, fextension = os.path.splitext(f) if v.detect_vba_macros() or fextension == ".js": writeLog('DEBUG: VBA Macros/JScript found') try: t = open("%s.analysis" % f, 'w') except IOError as e: writeLog("Cannot create analysis file %s.analysis: %s" % (f,e.strerror)) return for kw_type, keyword, description in v.analyze_macros(): t.write("%-12s | %-25s | %s\n" % (kw_type, keyword, description)) t.close() writeLog("DEBUG: Analysis dumped to %s.analysis" % f) else: writeLog('DEBUG: No VBA Macros found') return
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 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 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 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 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 get_macro(self): """ Get Macros from an Office file and write them to a text file """ try: self.mk_tmp_dir() print "Getting Macros from {}".format(self.file) vb = VBA_Parser(self.file, relaxed=True) if vb.detect_vba_macros(): with open("{}{}macros.txt".format(self.tmp_dir, os.sep), "w") as macro_file: for (subfilename, stream_path, vba_filename, vba_code) in vb.extract_all_macros(): macro_file.write(vba_code) except Exception as e: print "get_macro Exception: {}".format(e)
def check_macros(doc_path, filename) -> dict: try: vbaparser = VBA_Parser(doc_path) if not vbaparser.detect_vba_macros(): return "" logging.warning("VBA macros in \"%s\"" % filename) details = "Macro Results\n" suspicious_count = 0 suspicious_list = [] ioc_list = [] ioc_count = 0 mal_score = 0 logging.disable(level=logging.CRITICAL) results = vbaparser.analyze_macros() logging.disable(level=logging.NOTSET) for kw_type, keyword, description in results: details += "\t - " + kw_type + " - (" + keyword + ") - " + description + "\n" if kw_type.lower() == "suspicious": suspicious_count += 1 suspicious_list.append(keyword) elif kw_type.lower() == "ioc": ioc_count += 1 ioc_list.append(keyword) if suspicious_count != 0: logging.warning("Found %d suspicious items in \"%s\"" % (suspicious_count, filename)) mal_score += suspicious_count if ioc_count != 0: logging.warning("Found %d IOCs: %s, in \"%s\"" % (ioc_count, ioc_list, filename)) mal_score += ioc_count * 2 return {'details': details, 'mal_score': mal_score} except Exception as e: logging.error("Failed to parse macros for: {} - {}".format( filename, e)) return
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 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 _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 has_office_macros(office_file): """ Detects macros in Microsoft Office documents. :param office_file: The MS Office document to check for macros. :return: True if macros where found, otherwise False. If VBA_Parser crashes it returns False too. """ file_extension = office_file.split('.')[-1] if file_extension not in ms_office_extensions: return False try: # VBA_Parser reports macros for office documents vbaparser = VBA_Parser(office_file) return vbaparser.detect_vba_macros() except TypeError: # The given file is not an office document. return False except Exception as e: logger.exception(e) return False
def get_office_file_startup_command(extension, file_path): start_command = ["cmd.exe", "/C", "start"] if is_office_word_file(extension): start_command.append("winword.exe") elif is_office_excel_file(extension): start_command.append("excel.exe") elif is_office_powerpoint_file(extension): start_command.append("powerpnt.exe") else: log.warning(f"Unknown office file extension {extension}.") return None start_command.extend(["/t", "%f"]) vbaparser = VBA_Parser(file_path) if vbaparser.detect_vba_macros(): outer_macros = get_outer_nodes_from_vba_file(file_path) if not outer_macros: outer_macros = [] for outer_macro in outer_macros: start_command.append(f"/m{outer_macro}") return subprocess.list2cmdline(start_command)
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 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 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 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_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)
import sys import os from oletools.olevba import VBA_Parser, TYPE_OLE, TYPE_OpenXML, TYPE_Word2003_XML, TYPE_MHTML, detect_autoexec, VBA_Scanner #PYTHON 2,3버전 모두 호환 if sys.version_info[0]<= 2: #python 2 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)
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 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 process_file(container, filename, data, altparser=False, strip_useless=False, entry_points=None): """ 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. :return A list of actions if actions found, an empty list if no actions found, and None if there was an error. """ #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() if (entry_points is not None): for entry_point in entry_points: vm.entry_points.append(entry_point) try: #TODO: handle olefile errors, when an OLE file is malformed vba = VBA_Parser(filename, data, relaxed=True) print 'Type:', vba.type if vba.detect_vba_macros(): # Read in document metadata. try: ole = olefile.OleFileIO(filename) vba_library.meta = ole.get_metadata() vba_object.meta = vba_library.meta except: log.error("Reading in metadata failed.") vba_library.meta = {} # Set the output directory in which to put dumped files generated by # the macros. out_dir = filename + "_artifacts" if ("/" in out_dir): start = out_dir.rindex("/") + 1 out_dir = out_dir[start:] out_dir = out_dir.replace(".", "").strip() out_dir = "./" + out_dir + "/" vba_library.out_dir = out_dir # Parse the VBA streams. comp_modules = parse_streams(vba, strip_useless) if (comp_modules is None): return None for m in comp_modules: if (m != "empty"): vm.add_compiled_module(m) # Pull out form variables. try: for (subfilename, stream_path, form_variables) in vba.extract_form_strings_extended(): if form_variables is not None: var_name = form_variables['name'] macro_name = stream_path if ("/" in macro_name): start = macro_name.rindex("/") + 1 macro_name = macro_name[start:] global_var_name = (macro_name + "." + var_name).encode( 'ascii', 'ignore') val = form_variables['value'] if (val is None): val = '' name = global_var_name.lower() vm.globals[name] = val log.debug( "Added VBA form variable %r = %r to globals." % (global_var_name, val)) vm.globals[name + ".tag"] = val log.debug( "Added VBA form variable %r = %r to globals." % (global_var_name + ".Tag", val)) vm.globals[name + ".text"] = val log.debug( "Added VBA form variable %r = %r to globals." % (global_var_name + ".Text", val)) except Exception as e: log.error("Cannot read form strings. " + str(e)) print '-' * 79 print 'TRACING VBA CODE (entrypoint = Auto*):' if (entry_points is not None): log.info("Starting emulation from function(s) " + str(entry_points)) vm.trace() # print table of all recorded actions print('Recorded Actions:') print(vm.dump_actions()) print '' return vm.actions else: print 'No VBA macros found.' print '' return [] except Exception as e: if ("SystemExit" not in str(e)): traceback.print_exc() return None
def process_file (container, filename, data, altparser=False, strip_useless=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, relaxed=True) print 'Type:', vba.type if vba.detect_vba_macros(): # Read in document metadata. try: ole = olefile.OleFileIO(filename) vba_library.meta = ole.get_metadata() except: vba_library.meta = {} # Parse the VBA streams. comp_modules = parse_streams(vba, strip_useless) for m in comp_modules: vm.add_compiled_module(m) # Pull out form variables. for (subfilename, stream_path, form_variables) in vba.extract_form_strings_extended(): if form_variables is not None: var_name = form_variables['name'] macro_name = stream_path if ("/" in macro_name): start = macro_name.rindex("/") + 1 macro_name = macro_name[start:] global_var_name = (macro_name + "." + var_name).encode('ascii', 'ignore') val = form_variables['value'] vm.globals[global_var_name.lower()] = val log.debug("Added VBA form variable %r = %r to globals." % (global_var_name, val)) 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 ''
return res if __name__ == '__main__': parser = argparse.ArgumentParser( 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:")