def scan(filelist, conf=DEFAULTCONF): results = [] mmb = MaliciousMacroBot() mmb.mmb_init_model() for fname in filelist: # Ensure libmagic returns results if REQUIRES[0] is not None: # only run the analytic if it is an Office document if 'Microsoft' in _get_libmagicresults(REQUIRES[0][0], fname): result = mmb.mmb_predict(fname, datatype='filepath') prediction = result.iloc[0].get('prediction', None) confidence = result.iloc[0].get('result_dictionary', {}).get('confidence') result_dict = { 'Prediction': prediction, 'Confidence': confidence } results.append((fname, result_dict)) metadata = {} metadata["Name"] = NAME metadata["Type"] = TYPE return (results, metadata)
def run(self): self.key = "mmbot" results = dict() ftype = File(self.file_path).get_type() if self.task["category"] == "file": if not HAVE_MMBOT: log.error( "MaliciousMacroBot not installed, 'pip3 install mmbot', aborting mmbot analysis." ) return results package = "" if "info" in self.results and "package" in self.results["info"]: package = self.results["info"]["package"] if (package not in ("doc", "ppt", "xls", "pub") and ("Zip archive data, at least v2.0" not in ftype or "Composite Document File V2 Document" not in ftype or "Microsoft OOXML" not in ftype)): return results opts = dict() opts['benign_path'] = self.options.get( "benign_path", os.path.join(CUCKOO_ROOT, "data", "mmbot", "benign")) opts['malicious_path'] = self.options.get( "malicious_path", os.path.join(CUCKOO_ROOT, "data", "mmbot", "malicious")) opts['model_path'] = self.options.get( "model_path", os.path.join(CUCKOO_ROOT, "data", "mmbot", "model")) try: mmb = MaliciousMacroBot(opts["benign_path"], opts["malicious_path"], opts["model_path"], retain_sample_contents=False) mmb.mmb_init_model(modelRebuild=False) predresult = mmb.mmb_predict(self.file_path) results = mmb.mmb_prediction_to_json(predresult)[0] if "malicious" in results["prediction"]: link_path = os.path.join(opts["malicious_path"], os.path.basename(self.file_path)) if not os.path.isfile(link_path): os.symlink(self.file_path, link_path) elif "benign" in results["prediction"]: link_path = os.path.join(opts["benign_path"], os.path.basename(self.file_path)) if not os.path.isfile(link_path): os.symlink(self.file_path, link_path) except Exception as xcpt: log.error("Failed to run mmbot processing: %s", xcpt) return results
def run(self, obj, config): mmb = MaliciousMacroBot() mmb.mmb_init_model() mmb.set_model_paths(benign_path=None, malicious_path=None, model_path=self.model) fc =(obj.filedata.read()) result = mmb.mmb_predict(fc, datatype='filecontents') json = mmb.mmb_prediction_to_json(result)[0] for k,v in json.iteritems(): if k == 'prediction': self._add_result("Prediction", v, {"name": k}) for k,v in json.iteritems(): if k != 'prediction': self._add_result("Features", v, {"name": k})
def run(self, obj, config): mmb = MaliciousMacroBot() mmb.mmb_init_model() mmb.set_model_paths(benign_path=None, malicious_path=None, model_path=self.model) f = tempfile.NamedTemporaryFile() f.write(obj.filedata.read()) result = mmb.mmb_predict(f.name, datatype='filepath') f.close() json = mmb.mmb_prediction_to_json(result)[0] for k, v in json.iteritems(): self._add_result("Prediction", k, {"value": v})
def test_init_files_in_directories(): """ Test ensures the mmb_init function can build a model based on the samples provided. """ resetTest() mmb = MaliciousMacroBot(benign_path, malicious_path, model_path, retain_sample_contents=False) result = mmb.mmb_init_model(modelRebuild=True) os.remove(os.path.join(model_path, 'modeldata.pickle')) assert result
def test_init_files_in_directories_retain_contents(): """ Test ensures the mmb_init function can rebuild a model leveraging saved results without reprocessing all samples every time """ # Create model with a few samples resetTest() mmb = MaliciousMacroBot(benign_path, malicious_path, model_path, retain_sample_contents=True) result = mmb.mmb_init_model(modelRebuild=True) shutil.copy(origsample_path, os.path.join(malicious_path, sample)) # Add a file and rebuild mmb = MaliciousMacroBot(benign_path, malicious_path, model_path, retain_sample_contents=True) result = mmb.mmb_init_model(modelRebuild=True) assert result
def test_mmb_predict_sample_from_extracted_vba_df(): """ Test ensures the mmb_predict function can make a prediction from a single vba_sample. """ resetTest() mmb = MaliciousMacroBot(benign_path, malicious_path, model_path, retain_sample_contents=False) result = mmb.mmb_init_model(modelRebuild=True) samplevba = 'MsgBox "this is vba"' predresult = mmb.mmb_predict(samplevba, datatype='vba') predicted_label = predresult.iloc[0]['prediction'] logging.info('predicted label: {}'.format(predicted_label)) assert (predicted_label == 'benign' or predicted_label == 'malicious')
def test_mmb_predict_sample_on_disk(): """ Test ensures the mmb_predict function can make a prediction from a single sample on disk. """ resetTest() mmb = MaliciousMacroBot(benign_path, malicious_path, model_path, retain_sample_contents=False) result = mmb.mmb_init_model(modelRebuild=True) predresult = mmb.mmb_predict(origsample_path, datatype='filepath') predicted_label = predresult.iloc[0]['prediction'] logging.info('predicted label: {}'.format(predicted_label)) logging.info(mmb.mmb_prediction_to_json(predresult)) logging.info('predicted label: {}'.format(predicted_label)) assert (predicted_label == 'benign' or predicted_label == 'malicious')
def main(): mmb = MaliciousMacroBot() if not mmb.mmb_init_model(): return EXIT_FAILURE mime = os.environ['BROAPT_MIME'] path = os.environ['BROAPT_PATH'] name = os.path.split(path)[1] # run prediction prediction = mmb.mmb_predict(path, datatype='filepath') records = prediction.to_dict(orient='records') log_name = f'{os.path.splitext(name)[0]}.json' with open(os.path.join(MMB_LOG, log_name), 'w') as log: json.dump(records, log, indent=2) result = {'time': time.time(), 'path': path, 'mime': mime, 'rate': MMB_MAP[prediction.loc[0].prediction]} with open(os.path.join(LOGS_PATH, 'rate.log'), 'at', 1) as file: print(json.dumps(result), file=file) return EXIT_SUCCESS
#!/usr/bin/python import sys from mmbot import MaliciousMacroBot mmb = MaliciousMacroBot() mmb.mmb_init_model() result = mmb.mmb_predict(sys.argv[1], datatype='filepath') print result.iloc[0]
class MacroSampleTester(object): """ This class is aimed to test multiple documents from a given folder with MaliciousMacroBot. :param folder: path to the samples folder :param dump: dump the extracted VBA macros :param load: load previous results before starting :param save: save results after processing :param display: display report in terminal after processing :param filter_func: function for filtering files :param api_key: VirusTotal API key :param update: force updating VirusTotal result :param retry: retry VirusTotal request if previous result was None """ def __init__(self, folder, dump=True, load=False, save=False, display=False, filter_func=None, api_key=None, update=False, retry=False): assert hasattr(filter_func, '__call__') or filter_func is None self.__display = display self.__dump = dump self.__filter_func = filter_func self.__retry = retry self.__save = save self.__update = update self.folder = folder self.report = None self.results = None self.vt = VirusTotalClient(api_key) if load: self._load() if self.results is None: return else: if not load and not isdir(folder): logger.error( "'{}' isn't a valid samples folder".format(folder)) return logger.info("Initializing MaliciousMacroBot...") self.bot = MaliciousMacroBot() self.bot.mmb_init_model() self.process() def _load(self): """ Load results from a Pickle. """ fn = self.folder + ".pickle" try: with open(fn, 'rb') as f: logger.info("Loading previous results from pickle...") self.results = pickle.load(f) except IOError: logger.error("'{}' does not exist".format(fn)) self.results = None def _save(self): """ Save results as a Pickle. """ with open(self.folder + '.pickle', 'wb') as f: logger.info("Saving results to pickle...") pickle.dump(self.results, f) def process(self): """ Test all files with mmbot in a given folder and produce a report. """ assert isinstance(self.results, dict) or self.results is None logger.info("Processing samples...") # first, get the results of mmbot if self.results is None: self.results = {} for fn in os.listdir(self.folder): fp = os.path.abspath(os.path.join(self.folder, fn)) if os.path.isfile(fp): logger.debug("MMBot: classifying '{}'...".format(fn)) try: r = self.bot.mmb_predict(fp, datatype='filepath') \ .iloc[0] r = {k: v for k, v in r.iteritems() \ if k != 'result_dictionary'} r['sha256'] = hash_file(fp, "sha256") self.results[fn] = r except (TypeError, ValueError): logger.error("Failed to classify '{}'".format(fn)) self.results[fn] = None # second, if enabled, get the result from VirusTotal if self.vt.is_enabled: for k, v in self.results.items(): check = False f = "vt_detection_rate" if f not in v: check = not logger.debug( "> Getting VT information ({})...".format(k)) elif self.__update: check = not logger.debug( "> Updating VT information ({})...".format(k)) elif v.get(f) is None and self.__retry: check = not logger.debug( "> Retrying VT information ({})...".format(k)) if check: v["vt_detection_rate"] = self.vt.check(v['sha256']) # if flag '__save' was set, pickle results to a file if self.__save: self._save() # prepare folders for extracting the macros if self.__dump: vba = join(self.folder, 'vba') logger.warn("Macros will be saved to: {}".format(vba)) vba = abspath(vba) bf, mf = join(vba, 'benign'), join(vba, 'malicious') if not os.path.isdir(vba): os.makedirs(vba) if not os.path.isdir(bf): os.makedirs(bf) if not os.path.isdir(mf): os.makedirs(mf) # parse the results logger.info("Parsing results...") benign, c_all, c_vba = [], [0] * 4, [0] * 4 if self.vt.is_enabled: r = "{: <16} {: <16} {}\n".format("FILE", "PREDICTION", "VT DETECTION") else: r = "{: <16} {}\n".format("FILE", "PREDICTION") j = OrderedDict([ ('title', "Malicious Macro Detection Report"), ('statistics', None), ('results', []), ]) for k, v in sorted(self.results.items()): # filter according to the input lambda function 'filter_func' if self.__filter_func is not None and not self.__filter_func(k): continue # define shortnames drate = v.get('vt_detection_rate') macro = v['extracted_vba'] failed = macro.startswith("Error:'utf8'") \ or macro.startswith("Error:Failed") \ or macro == "No VBA Macros found" pred = v['prediction'] malicious = pred == "malicious" i = {'file': k, 'prediction': pred, 'sha256': v['sha256']} # save the VBA code to the samples folder in subfolder 'vba' if not failed: if self.__dump: dest = [bf, mf][malicious] vba_fn = join(dest, "{}.vba".format(k)) with open(vba_fn, 'w') as f: f.write(macro) # add stats line to report if it has a valid macro if self.vt.is_enabled: i['vt_detection'] = drate r += "{: <16} {: <16} {}\n".format(k, pred, drate) else: r += "{: <16} {}\n".format(k, pred) j['results'].append(i) # perform counts if malicious: c_all[0] += 1 else: benign.append(k) if drate is None: c_all[1] += 1 if malicious and drate is not None: c_all[2] += 1 c_all[-1] += 1 if not failed: if malicious: c_vba[0] += 1 if drate is None: c_vba[1] += 1 if malicious and drate is not None: c_vba[2] += 1 c_vba[-1] += 1 # make the report # - handle the whole list of files first j['statistics'] = {'all': {'malicious': c_all[0], 'total': c_all[-1]}} r += "\nAll files:" \ "\n Marked as malicious: {: >3}/{} ({}%)" \ .format(c_all[0], c_all[-1], 100 * c_all[0] / c_all[-1]) if self.vt.is_enabled: j['statistics']['all'].update({ 'vt_unknown': c_all[1], 'malicious_and_vt_known': c_all[2] }) r += "\n Unknown from VT: {: >3}/{} ({}%)" \ .format(c_all[1], c_all[-1], 100 * c_all[1] / c_all[-1]) + \ "\n Marked as malicious and known from VT:{: >3}/{} ({}%)" \ .format(c_all[2], c_all[-1], 100 * c_all[2] / c_all[-1]) # - only handle files with a successfully extracted VBA macro j['statistics']['vba'] = {'malicious': c_vba[0], 'total': c_vba[-1]} r += "\n\nOnly files for which the VBA macro could be extracted:" \ "\n Marked as malicious: {: >3}/{} ({}%)" \ .format(c_vba[0], c_vba[-1], 100 * c_vba[0] / c_vba[-1]) if self.vt.is_enabled: j['statistics']['vba'].update({ 'vt_unknown': c_vba[1], 'malicious_and_vt_known': c_vba[2] }) r += "\n Unknown from VT: {: >3}/{} ({}%)" \ .format(c_vba[1], c_vba[-1], 100 * c_vba[1] / c_vba[-1]) + \ "\n Marked as malicious and known from VT:{: >3}/{} ({}%)" \ .format(c_vba[2], c_vba[-1], 100 * c_vba[2] / c_vba[-1]) r += "\n\nBenign files:\n{}".format(", ".join(benign)) if self.__display: print(r) self.report = r self.json = j