def parse_vba(self, save_path): vba = VBA_Parser(__sessions__.current.file.path) # Check for Macros if not vba.detect_vba_macros(): self.log('error', "No Macro's Detected") return self.log('info', "Macro's Detected") try: for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros(): self.log('info', "Stream Details") self.log('item', "OLE Stream: {0}".format(stream_path)) self.log('item', "VBA Filename: {0}".format(vba_filename)) autoexec_keywords = detect_autoexec(vba_code) if autoexec_keywords: self.log('info', "AutoRun Macros Found") rows = [] for keyword, description in autoexec_keywords: rows.append([keyword, description]) self.log( 'table', dict(header=['KeyWord', 'Description'], rows=rows)) # Match Keyword Types suspicious_keywords = detect_suspicious(vba_code) if suspicious_keywords: self.log('info', "Suspicious Keywords Found") rows = [] for keyword, description in suspicious_keywords: rows.append([keyword, description]) self.log( 'table', dict(header=['KeyWord', 'Description'], rows=rows)) # Match IOCs patterns = detect_patterns(vba_code) if patterns: self.log('info', "Suspicious Keywords Found") rows = [] for pattern_type, value in patterns: rows.append([pattern_type, value]) self.log('table', dict(header=['Pattern', 'Value'], rows=rows)) # Save the code to external File if save_path: try: with open(save_path, 'w') as out: out.write(vba_code) self.log('info', "Writing VBA Code to {0}".format(save_path)) except: self.log('Error', "Unable to write to {0}".format(save_path)) return except: self.log('Error', "Unable to Process File") # Close the file vba.close()
def get_report(self): """ Return oletools report or create if not already cached. """ if self.sample.oletools_report is not None: return self.sample.oletools_report report = { 'autoexec': [], 'suspicious': [], } file_path = self.sample.file_path try: vbaparser = VBA_Parser(file_path) # VBA_Parser reports macros for office documents report['has_macros'] = vbaparser.detect_vba_macros( ) or vbaparser.detect_xlm_macros() try: report['vba'] = vbaparser.reveal() except TypeError: # office document with no macros pass all_macros = vbaparser.extract_all_macros() if (report['has_macros'] and len(all_macros) == 1 and isinstance(all_macros[0], tuple) and len(all_macros[0]) >= 3 and all_macros[0][2] == file_path): logger.warning( "Buggy oletools version detected, result overridden. May " "lead to false negatives, please update to fixed version") report['has_macros'] = False if vbaparser.detect_vba_macros(): vb_code = vbaparser.extract_all_macros() for (_, _, _, c) in vb_code: autoexec = detect_autoexec(c) if len(autoexec) >= 1: report['autoexec'].append(autoexec[0]) suspicious = detect_suspicious(c) if len(suspicious) >= 1: report['suspicious'].append(suspicious[0]) vbaparser.close() except IOError: raise except (TypeError, FileOpenError): # The given file is not an office document. pass except Exception as error: logger.exception(error) report = OletoolsReport(report) self.sample.register_oletools_report(report) return report
def parse_vba(self, save_path): save = False vba = VBA_Parser(__sessions__.current.file.path) # Check for Macros if not vba.detect_vba_macros(): self.log('error', "No Macro's Detected") return self.log('info', "Macro's Detected") try: run_rows = [] word_rows = [] pattern_rows = [] for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros(): self.log('info', "Stream Details") self.log('item', "OLE Stream: {0}".format(string_clean(stream_path))) self.log('item', "VBA Filename: {0}".format(string_clean(vba_filename))) autoexec_keywords = detect_autoexec(vba_code) if autoexec_keywords: for keyword, description in autoexec_keywords: run_rows.append([keyword, description]) # Match Keyword Types suspicious_keywords = detect_suspicious(vba_code) if suspicious_keywords: for keyword, description in suspicious_keywords: word_rows.append([keyword, description]) # Match IOCs patterns = detect_patterns(vba_code) if patterns: for pattern_type, value in patterns: pattern_rows.append([pattern_type, value]) # Save the code to external File if save_path: try: with open(save_path, 'a') as out: out.write(vba_code) save = True except: self.log('Error', "Unable to write to {0}".format(save_path)) return # Print all Tables together self.log('info', "AutoRun Macros Found") self.log('table', dict(header=['KeyWord', 'Description'], rows=run_rows)) self.log('info', "Suspicious Keywords Found") self.log('table', dict(header=['KeyWord', 'Description'], rows=word_rows)) self.log('info', "Suspicious Patterns Found") self.log('table', dict(header=['Pattern', 'Value'], rows=pattern_rows)) if save: self.log('success', "Writing VBA Code to {0}".format(save_path)) except: self.log('Error', "Unable to Process File") # Close the file vba.close()
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: print("Auto-executable Macro keyword is detected. :") for keyword, description in autoexec_keywords: print("%s: %s' % (keyword, description)") else: print ("Auto-executable Macro keywords is not detected.") results = vbaparser.analyze_macros() for kw_type, keyword, description in results: print ("type=%s - keyword=%s - description=%s" % (kw_type, keyword, description)) print ("AutoExec keywords: %d" % vbaparser.nb_autoexec) print ("Suspicious keywords: %d" % vbaparser.nb_suspicious) print ("IOCs: %d" % vbaparser.nb_iocs) print ("Hex obfuscated strings: %d" % vbaparser.nb_hexstrings) print ("Base64 obfuscated strings: %d" % vbaparser.nb_base64strings)
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 get_report(self): """ Return oletools report or create if not already cached. """ if self.sample.oletools_report is not None: return self.sample.oletools_report report = { 'autoexec': [], 'suspicious' : [], } filename = self.sample.filename try: vbaparser = VBA_Parser(filename, data=self.sample.content) # VBA_Parser reports macros for office documents report['has_macros'] = vbaparser.detect_vba_macros() or vbaparser.detect_xlm_macros() try: report['vba'] = vbaparser.reveal() except TypeError: # office document with no macros pass # When called on an empty or text document oletools will falsely # report that it contains macros and returns a one item list of # macros which contains only the filename again. # # Oletool assume the submitted file is the plain text macro if # it can not determine another file type. # # Add a workaround to detect this behaviour and override the # result. all_macros = vbaparser.extract_all_macros() if (report['has_macros'] and len(all_macros) == 1 and isinstance(all_macros[0], tuple) and len(all_macros[0]) >= 3 and all_macros[0][2] == filename): report['has_macros'] = False if vbaparser.detect_vba_macros(): vb_code = vbaparser.extract_all_macros() for (_, _, _, c) in vb_code: autoexec = detect_autoexec(c) if len(autoexec) >= 1: report['autoexec'].extend(autoexec) suspicious = detect_suspicious(c) if len(suspicious) >= 1: report['suspicious'].extend(suspicious) vbaparser.close() except IOError: raise except (TypeError, FileOpenError): # The given file is not an office document. pass except Exception as error: logger.exception(error) report = OletoolsReport(report) self.sample.register_oletools_report(report) return report