def contains_peekabooyar(config, s): """ Checks the given sample for the PeekabooYar (EICAR like) malicious string. :param s: sample to check :return: RueleResult """ tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) rules = yara.compile(source=''' rule peekabooyar { strings: $peekabooyar1 = "X5O!P%@AP-/_(:)_/-X22x8cz2$PeekabooAV-STD-ANTIVIRUS-TEST-FILE!$H+H*" condition: $peekabooyar1 }''') with open(s.get_file_path(), 'rb') as f: matches = rules.match(data=f.read()) if matches != []: return RuleResult(position, result=Result.bad, reason="Die Datei beinhaltet Peekabooyar.", further_analysis=False) return RuleResult( position, result=Result.unknown, reason="Die Datei beinhaltet kein erkennbares Peekabooyar.", further_analysis=True)
def fetch_rule_result(self, sample): """ Gets the sample information from the database as a RuleResult object. :param sample: The Sample object to get the rule result from. :return: Returns a RuleResult object containing the sample information. """ with self.__lock: session = self.__Session() sample_info = PeekabooDatabase.__get( session, SampleInfo, sha256sum=sample.sha256sum, file_extension=sample.file_extension) if sample_info: result = RuleResult('db', result=Result.from_string( sample_info.result.name), reason=sample_info.reason, further_analysis=True) else: result = RuleResult( 'db', result=Result.unknown, reason="Datei ist dem System noch nicht bekannt", further_analysis=True) session.close() return result
def __exec_rule(self, config, sample, rule_function): """ rule wrapper for in/out logging and reporting """ rule_name = rule_function.func_name logger.debug("Processing rule '%s' for %s" % (rule_name, sample)) try: # skip disabled rules. if rule_name in config.keys() and \ 'enabled' in config[rule_name].keys() and \ config[rule_name]['enabled'] == 'no': logger.debug("Rule '%s' is disabled." % rule_name) result = RuleResult(rule_name, result=Result.unchecked, reason="Regel '%s' ist deaktiviert." % rule_name, further_analysis=True) else: result = rule_function(config, sample) sample.add_rule_result(result) except CuckooReportPendingException as e: # in case the Sample is requesting the Cuckoo report raise # catch all other exceptions for this rule except Exception as e: logger.warning("Unexpected error in '%s' for %s" % (rule_name, sample)) logger.exception(e) # create "fake" RuleResult result = RuleResult("RulesetEngine", result=Result.unknown, reason="Regel mit Fehler abgebrochen", further_analysis=True) sample.add_rule_result(result) logger.info("Rule '%s' processed for %s" % (rule_name, sample)) return result
def cuckoo_evil_sig(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) # signatures that if matched mark a sample as bad # list all installed signatures # grep -o "description.*" -R . ~/cuckoo2.0/modules/signatures/ bad_sigs = config['cuckoo_evil_sig']['signature'] sigs = [] # look through matched signatures for descr in s.cuckoo_report.signatures: logger.debug(descr['description']) sigs.append(descr['description']) # check if there is a "bad" signatures and return bad matched_bad_sigs = [] for sig in bad_sigs: match = re.search(sig, str(sigs)) if match: matched_bad_sigs.append(sig) if len(matched_bad_sigs) == 0: return RuleResult(position, result=Result.unknown, reason="Keine Signatur erkannt die auf Schadcode hindeutet", further_analysis=True) return RuleResult(position, result=Result.bad, reason="Folgende Signaturen wurden erkannt: %s" % ''.ljust(8).join(["%s\n" % s for s in matched_bad_sigs]), further_analysis=False)
def office_macro(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) if s.office_macros: return RuleResult(position, result=Result.bad, reason="Die Datei beinhaltet ein Office-Makro", further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Die Datei beinhaltet kein erkennbares Office-Makro", further_analysis=True)
def cuckoo_analysis_failed(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) if s.cuckoo_analysis_failed: return RuleResult(position, result=Result.bad, reason="Die Verhaltensanalyse durch Cuckoo hat einen Fehler Produziert und konnte nicht erfolgreich abgeschlossen werden", further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Die Verhaltensanalyse durch Cuckoo wurde erfolgreich abgeschlossen", further_analysis=True)
def known(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) if s.known_to_db: sample_info = s.info_from_db return RuleResult(position, result=sample_info.get_result(), reason=sample_info.reason, further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Datei ist dem System noch nicht bekannt", further_analysis=True)
def run(self, sample): """ Run all the rules in the ruleset against a given sample @param sample: sample to evaluate ruleset against @returns: Nothing, all state is recorded in the sample """ for rule in self.rules: rule_name = rule.rule_name logger.debug("%d: Processing rule '%s'", sample.id, rule_name) try: result = rule.evaluate(sample) sample.add_rule_result(result) except PeekabooAnalysisDeferred: # in case the Sample is requesting the Cuckoo report raise # catch all other exceptions for this rule except Exception as error: logger.warning( "%d: Unexpected error in '%s'", sample.id, rule_name) logger.exception(error) # create "fake" RuleResult result = RuleResult("RulesetEngine", result=Result.failed, reason=_("Rule aborted with error"), further_analysis=False) sample.add_rule_result(result) logger.info("%d: Rule '%s' processed", sample.id, rule_name) if not result.further_analysis: return logger.info("%d: Rules evaluated", sample.id)
def file_larger_than(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) size = int(config['file_larger_than']['bytes']) if s.file_size > size: return RuleResult(position, result=Result.unknown, reason="Datei hat mehr als %d bytes" % size, further_analysis=True) return RuleResult(position, result=Result.ignored, reason="Datei ist nur %d bytes lang" % s.file_size, further_analysis=False)
def __exec_rule(self, sample, rule_class): """ rule wrapper for in/out logging and reporting """ rule_name = rule_class.rule_name logger.debug("Processing rule '%s' for %s", rule_name, sample) try: rule = rule_class(config=self.config, db_con=self.db_con) result = rule.evaluate(sample) sample.add_rule_result(result) except PeekabooAnalysisDeferred: # in case the Sample is requesting the Cuckoo report raise # catch all other exceptions for this rule except Exception as e: logger.warning("Unexpected error in '%s' for %s", rule_name, sample) logger.exception(e) # create "fake" RuleResult result = RuleResult("RulesetEngine", result=Result.failed, reason=_("Rule aborted with error"), further_analysis=False) sample.add_rule_result(result) logger.info("Rule '%s' processed for %s", rule_name, sample) return result
def run(self): while not self.shutdown_requested.is_set(): logger.debug('Worker %d: Ready', self.worker_id) try: # wait blocking for next job (thread safe) with timeout sample = self.job_queue.dequeue() except queue.Empty: continue if sample is None: # we just got pinged continue logger.info('Worker %d: Processing sample %s', self.worker_id, sample) # The following used to be one big try/except block catching any # exception. This got complicated because in the case of # CuckooReportPending we use exceptions for control flow as well # (which might be questionable in itself). Instead of catching, # logging and ignoring errors here if workers start to die again # because of uncaught exceptions we should improve error handling # in the subroutines causing it. if not sample.init(): logger.error('Sample initialization failed') sample.add_rule_result( RuleResult("Worker", result=Result.failed, reason=_("Sample initialization failed"), further_analysis=False)) self.job_queue.done(sample.sha256sum) continue try: self.ruleset_engine.run(sample) except PeekabooAnalysisDeferred: logger.debug("Report for sample %s still pending", sample) continue if sample.result >= Result.failed: sample.dump_processing_info() if sample.result != Result.failed: logger.debug('Saving results to database') try: self.db_con.analysis_save(sample) except PeekabooDatabaseError as dberr: logger.error( 'Failed to save analysis result to ' 'database: %s', dberr) # no showstopper, we can limp on without caching in DB else: logger.debug('Not saving results of failed analysis') sample.cleanup() self.job_queue.done(sample) logger.info('Worker %d: Stopped', self.worker_id)
def file_type_on_whitelist(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) whitelist = config['file_type_on_whitelist']['whitelist'] if set(s.mimetypes).issubset(set(whitelist)): return RuleResult(position, result=Result.ignored, reason="Dateityp ist auf Whitelist", further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Dateityp ist nicht auf Whitelist", further_analysis=True)
def known(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) db = get_config().get_db_con() if db.known(s): sample_info = db.sample_info_fetch(s) return RuleResult(position, result=sample_info.get_result(), reason=sample_info.reason, further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Datei ist dem System noch nicht bekannt", further_analysis=True)
def requests_evil_domain(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) evil_domains = config['requests_evil_domain']['domain'] for d in s.requested_domains: if d in evil_domains: return RuleResult(position, result=Result.bad, reason="Die Datei versucht mindestens eine Domain aus der Blacklist zu kontaktieren (%s)" % d, further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Datei scheint keine Domains aus der Blacklist kontaktieren zu wollen", further_analysis=True)
def final_rule(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) return RuleResult(position, result=Result.unknown, reason="Datei scheint keine erkennbaren Schadroutinen zu starten", further_analysis=True)
def cuckoo_score(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) threshold = float(config['cuckoo_score']['higher_than']) if s.cuckoo_report.score >= threshold: return RuleResult(position, result=Result.bad, reason="Cuckoo score >= %s: %s" % (threshold, s.cuckoo_report.score), further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Cuckoo score < %s: %s" % (threshold, s.cuckoo_report.score), further_analysis=True)
def file_type_on_greylist(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) greylist = config['file_type_on_greylist']['greylist'] if set(s.mimetypes).issubset(set(greylist)): return RuleResult(position, result=Result.unknown, reason="Dateityp ist auf der Liste der zu analysiserenden Typen", further_analysis=True) return RuleResult(position, result=Result.unknown, reason="Dateityp ist nicht auf der Liste der zu analysierenden Typen (%s)" % (str(s.mimetypes)), further_analysis=False)
def test_6_add_rule_result(self): """ Test the adding of a rule result. """ reason = 'This is just a test case.' result = RuleResult('Unittest', Result.failed, reason, further_analysis=False) self.sample.add_rule_result(result) self.assertEqual(self.sample.result, Result.failed) self.assertEqual(self.sample.reason, reason)
def requests_evil_domain(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) evil_domains = config.get('domain', ()) if len(evil_domains) == 0: logger.warn("Empty evil domain list, check ruleset config.") for d in s.cuckoo_report.requested_domains: if d in evil_domains: return RuleResult(position, result=Result.bad, reason="Die Datei versucht mindestens eine Domain aus der Blacklist zu kontaktieren (%s)" % d, further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Datei scheint keine Domains aus der Blacklist kontaktieren zu wollen", further_analysis=True)
def test_3_sample_info_update(self): result = RuleResult('Unittest', Result.checked, 'This is another test case.', further_analysis=False) self.sample.add_rule_result(result) self.sample.determine_result() self.conf.db_con.sample_info_update(self.sample) rule_result = self.conf.db_con.fetch_rule_result(self.sample) self.assertEqual(rule_result.result, Result.checked) self.assertEqual(rule_result.reason, 'This is another test case.')
def file_type_on_whitelist(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) whitelist = config.get('whitelist', ()) if len(whitelist) == 0: logger.warn("Empty whitelist, check ruleset config.") # ignore the file only if *all* of its mime types are on the whitelist if s.mimetypes.issubset(set(whitelist)): return RuleResult(position, result=Result.ignored, reason="Dateityp ist auf Whitelist", further_analysis=False) return RuleResult(position, result=Result.unknown, reason="Dateityp ist nicht auf Whitelist", further_analysis=True)
def file_type_on_greylist(config, s): tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) greylist = config.get('greylist', ()) if len(greylist) == 0: logger.warn("Empty greylist, check ruleset config.") # continue analysis if any of the sample's mime types are on the greylist if len(s.mimetypes.intersection(set(greylist))) > 0: return RuleResult(position, result=Result.unknown, reason="Dateityp ist auf der Liste der zu analysiserenden Typen", further_analysis=True) return RuleResult(position, result=Result.unknown, reason="Dateityp ist nicht auf der Liste der zu analysierenden Typen (%s)" % (str(s.mimetypes)), further_analysis=False)
def setUpClass(cls): cls.test_db = os.path.abspath('./test.db') cls.conf = PeekabooDummyConfig() db_con = PeekabooDatabase('sqlite:///' + cls.test_db) cls.conf.set_db_con(db_con) _set_config(cls.conf) cls.sample = Sample(os.path.realpath(__file__)) result = RuleResult('Unittest', Result.unknown, 'This is just a test case.', further_analysis=True) cls.sample.add_rule_result(result) cls.sample.determine_result()
def already_in_progress(self, config, s): with self.__in_use: logger.debug("enter already_in_progress") tb = traceback.extract_stack() tb = tb[-1] position = "%s:%s" % (tb[2], tb[1]) if s.has_attr('pending'): s.set_attr('pending', False) return RuleResult(position, result=s.get_result(), reason='Datei wird jetzt Analysiert', further_analysis=True) l = [] for sample in ConnectionMap.get_samples_by_sha256(s.sha256sum): if sample != s: if not sample.has_attr('pending') or not sample.get_attr( 'pending') is True: l.append(sample) if len(l) == 0: s.set_attr("pending", False) logger.debug("no second analysis present") return RuleResult(position, result=s.get_result(), reason='Datei wird jetzt Analysiert', further_analysis=True) logger.debug("there is another same sample") logger.debug("I'll be off until needed") s.set_attr("pending", True) # stop worker sys.stdout.flush() logger.debug("leave already_in_progress") raise CuckooReportPendingException()
def setUpClass(cls): cls.test_db = os.path.abspath('./test.db') cls.conf = PeekabooDummyConfig() cls.db_con = PeekabooDatabase('sqlite:///' + cls.test_db) cls.factory = SampleFactory(cuckoo = None, db_con = cls.db_con, connection_map = None, base_dir = cls.conf.sample_base_dir, job_hash_regex = cls.conf.job_hash_regex, keep_mail_data = False) cls.sample = cls.factory.make_sample(os.path.realpath(__file__)) result = RuleResult('Unittest', Result.unknown, 'This is just a test case.', further_analysis=True) cls.sample.add_rule_result(result) cls.sample.determine_result()
def setUpClass(cls): """ Set up common test case resources. """ cls.test_db = os.path.abspath('./test.db') cls.conf = CreatingPeekabooConfig() cls.db_con = PeekabooDatabase('sqlite:///' + cls.test_db, instance_id=1, stale_in_flight_threshold=10) cls.no_cluster_db = PeekabooDatabase('sqlite:///' + cls.test_db, instance_id=0) cls.factory = CreatingSampleFactory( cuckoo=None, base_dir=cls.conf.sample_base_dir, job_hash_regex=cls.conf.job_hash_regex, keep_mail_data=False, processing_info_dir=None) cls.sample = cls.factory.create_sample('test.py', 'test') result = RuleResult('Unittest', Result.failed, 'This is just a test case.', further_analysis=False) cls.sample.add_rule_result(result)
def result(self, result, reason, further_analysis): """ Construct a RuleResult for returning to the engine. """ return RuleResult(self.rule_name, result=result, reason=reason, further_analysis=further_analysis)
def fetch_rule_result(self, sha256): return RuleResult('fake_db', result=Result.checked, reason='Test Case', further_analysis=True)