diff options
author | Felix Bauer <jack@ai4me.de> | 2019-10-01 17:33:39 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-01 17:33:39 +0200 |
commit | baab4937eff80e950db33a080790ad4b2a45b99d (patch) | |
tree | bb1c43e5c763e4829c7f200766357da0512a02c5 | |
parent | 778ff1931b793292c8fcdcc921dda76d92626524 (diff) |
Add olereport to expression context (#100)
olereport can now be used in expressions
Before oletools was only a separate rule.
Now has_office_macro and vba_code can be used in generic rule
expressions.
-rw-r--r-- | docs/source/ruleset.rst | 11 | ||||
-rw-r--r-- | peekaboo/locale/de/LC_MESSAGES/peekaboo.mo | bin | 4392 -> 4743 bytes | |||
-rw-r--r-- | peekaboo/locale/de/LC_MESSAGES/peekaboo.po | 77 | ||||
-rw-r--r-- | peekaboo/locale/peekaboo.pot | 73 | ||||
-rw-r--r-- | peekaboo/ruleset/rules.py | 29 | ||||
-rw-r--r-- | peekaboo/toolbox/ole.py | 19 | ||||
-rw-r--r-- | ruleset.conf.sample | 2 | ||||
-rwxr-xr-x | tests/test.py | 59 |
8 files changed, 194 insertions, 76 deletions
diff --git a/docs/source/ruleset.rst b/docs/source/ruleset.rst index 46d4bd9..bca63d8 100644 --- a/docs/source/ruleset.rst +++ b/docs/source/ruleset.rst @@ -60,6 +60,9 @@ Rules can then be constructed like: 'application/x-pkcs7-mime' } -> ignore expression.3 : /DDE/ in cuckooreport.signature_descriptions -> bad + expression.4 : /suspicious/ in olereport.vba_code -> bad + expression.5 : olereport.has_office_macros == True + and cuckooreport.score > 4 -> bad Attributes of sample -------------------- @@ -86,3 +89,11 @@ Attributes of cuckooreport score errors cuckoo_server_messages + +Attributes of olereport +----------------------- + +.. code-block:: shell + + has_office_macro + vba_code diff --git a/peekaboo/locale/de/LC_MESSAGES/peekaboo.mo b/peekaboo/locale/de/LC_MESSAGES/peekaboo.mo Binary files differindex af5a962..c7ba71f 100644 --- a/peekaboo/locale/de/LC_MESSAGES/peekaboo.mo +++ b/peekaboo/locale/de/LC_MESSAGES/peekaboo.mo diff --git a/peekaboo/locale/de/LC_MESSAGES/peekaboo.po b/peekaboo/locale/de/LC_MESSAGES/peekaboo.po index 77ab127..f5022ba 100644 --- a/peekaboo/locale/de/LC_MESSAGES/peekaboo.po +++ b/peekaboo/locale/de/LC_MESSAGES/peekaboo.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PeekabooAV 1.6.2\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2019-08-20 10:35+0200\n" +"POT-Creation-Date: 2019-09-09 11:18+0200\n" "PO-Revision-Date: 2019-02-14 22:02+0000\n" "Last-Translator: Michael Weiser <michael.weiser@gmx.de>\n" "Language: de\n" @@ -36,7 +36,7 @@ msgstr "Die Datei \"%s\" wird als \"%s\" betrachtet" msgid "File \"%s\": %s" msgstr "Datei \"%s\": %s" -#: peekaboo/sample.py:497 +#: peekaboo/sample.py:478 #, python-format msgid "Sample %s successfully submitted to Cuckoo as job %d" msgstr "Erfolgreich an Cuckoo gegeben %s als Job %d" @@ -100,65 +100,73 @@ msgstr "Ja" msgid "No" msgstr "Nein" -#: peekaboo/ruleset/engine.py:147 +#: peekaboo/ruleset/engine.py:148 msgid "Rule aborted with error" msgstr "Regel mit Fehler abgebrochen" -#: peekaboo/ruleset/rules.py:122 +#: peekaboo/ruleset/rules.py:128 peekaboo/ruleset/rules.py:470 +msgid "" +"Behavioral analysis by Cuckoo has produced an error and did not finish " +"successfully" +msgstr "" +"Die Verhaltensanalyse durch Cuckoo hat einen Fehler produziert und konnte" +" nicht erfolgreich abgeschlossen werden" + +#: peekaboo/ruleset/rules.py:164 msgid "File is not yet known to the system" msgstr "Datei ist dem System noch nicht bekannt" -#: peekaboo/ruleset/rules.py:143 +#: peekaboo/ruleset/rules.py:185 #, python-format msgid "Failure to determine sample file size: %s" msgstr "Ermittlung der Dateigröße fehlgeschlagen: %s" -#: peekaboo/ruleset/rules.py:148 +#: peekaboo/ruleset/rules.py:190 #, python-format msgid "File has more than %d bytes" msgstr "Datei hat mehr als %d bytes" -#: peekaboo/ruleset/rules.py:154 +#: peekaboo/ruleset/rules.py:196 #, python-format msgid "File is only %d bytes long" msgstr "Die Datei ist nur %d bytes groß" -#: peekaboo/ruleset/rules.py:176 +#: peekaboo/ruleset/rules.py:218 msgid "File type is on whitelist" msgstr "Dateityp ist auf Whitelist" -#: peekaboo/ruleset/rules.py:180 +#: peekaboo/ruleset/rules.py:222 msgid "File type is not on whitelist" msgstr "Dateityp ist nicht auf Whitelist" -#: peekaboo/ruleset/rules.py:202 +#: peekaboo/ruleset/rules.py:244 msgid "File type is on the list of types to analyze" msgstr "Dateityp ist auf der Liste der zu analysiserenden Typen" -#: peekaboo/ruleset/rules.py:207 +#: peekaboo/ruleset/rules.py:249 #, python-format msgid "File type is not on the list of types to analyse (%s)" msgstr "Dateityp ist nicht auf der Liste der zu analysierenden Typen (%s)" -#: peekaboo/ruleset/rules.py:223 +#: peekaboo/ruleset/rules.py:265 msgid "File is not an office document" msgstr "Die Datei ist kein Office Dokument" -#: peekaboo/ruleset/rules.py:247 +#: peekaboo/ruleset/rules.py:289 msgid "The file contains an Office macro" msgstr "Die Datei beinhaltet ein Office-Makro" -#: peekaboo/ruleset/rules.py:251 +#: peekaboo/ruleset/rules.py:293 msgid "The file does not contain a recognizable Office macro" msgstr "Die Datei beinhaltet kein erkennbares Office-Makro" -#: peekaboo/ruleset/rules.py:272 +#: peekaboo/ruleset/rules.py:314 msgid "The file contains an Office macro which runs at document open" msgstr "" "Die Datei beinhaltet ein Office Makro welches beim Öffnen der Datei " "ausgeführt wird" -#: peekaboo/ruleset/rules.py:277 +#: peekaboo/ruleset/rules.py:319 msgid "" "The file does not contain a recognizable Office macro that is run at " "document open" @@ -166,49 +174,54 @@ msgstr "" "Die Datei beinhaltet kein erkennbares Office Makro welches beim Öffnen " "ausgeführt wird" -#: peekaboo/ruleset/rules.py:307 peekaboo/ruleset/rules.py:445 -msgid "" -"Behavioral analysis by Cuckoo has produced an error and did not finish " -"successfully" -msgstr "" -"Die Verhaltensanalyse durch Cuckoo hat einen Fehler produziert und konnte" -" nicht erfolgreich abgeschlossen werden" - -#: peekaboo/ruleset/rules.py:365 +#: peekaboo/ruleset/rules.py:390 msgid "No signature suggesting malware detected" msgstr "Keine Signatur erkannt die auf Schadcode hindeutet" -#: peekaboo/ruleset/rules.py:370 +#: peekaboo/ruleset/rules.py:395 #, python-format msgid "The following signatures have been recognized: %s" msgstr "Folgende Signaturen wurden erkannt: %s" -#: peekaboo/ruleset/rules.py:389 +#: peekaboo/ruleset/rules.py:414 #, python-format msgid "Cuckoo score >= %s: %s" msgstr "" -#: peekaboo/ruleset/rules.py:394 +#: peekaboo/ruleset/rules.py:419 #, python-format msgid "Cuckoo score < %s: %s" msgstr "" -#: peekaboo/ruleset/rules.py:418 +#: peekaboo/ruleset/rules.py:443 #, python-format msgid "The file attempts to contact at least one domain on the blacklist (%s)" msgstr "" "Die Datei versucht mindestens eine Domain aus der Blacklist zu " "kontaktieren (%s)" -#: peekaboo/ruleset/rules.py:424 +#: peekaboo/ruleset/rules.py:449 msgid "File does not seem to attempt contact with domains on the blacklist" msgstr "Datei scheint keine Domains aus der Blacklist kontaktieren zu wollen" -#: peekaboo/ruleset/rules.py:461 +#: peekaboo/ruleset/rules.py:486 msgid "Behavioral analysis by Cuckoo completed successfully" msgstr "Die Verhaltensanalyse durch Cuckoo wurde erfolgreich abgeschlossen" -#: peekaboo/ruleset/rules.py:478 +#: peekaboo/ruleset/rules.py:539 +msgid "Evaluation of expression uses undefined identifier." +msgstr "Auswertung des Ausdrucks nutzt nicht definierten Bezeichner." + +#: peekaboo/ruleset/rules.py:544 +#, python-format +msgid "The rule (%s) classified the sample as %s" +msgstr "Die Regel (%s) klassifizierte die Datei als %s" + +#: peekaboo/ruleset/rules.py:550 +msgid "No rule classified the sample in any way." +msgstr "Keine Regel klassifizierte die Datei in irgendeiner Weise." + +#: peekaboo/ruleset/rules.py:562 msgid "File does not seem to exhibit recognizable malicious behaviour" msgstr "Datei scheint keine erkennbaren Schadroutinen zu starten" diff --git a/peekaboo/locale/peekaboo.pot b/peekaboo/locale/peekaboo.pot index 085c447..e1b3f34 100644 --- a/peekaboo/locale/peekaboo.pot +++ b/peekaboo/locale/peekaboo.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2019-08-20 10:35+0200\n" +"POT-Creation-Date: 2019-09-09 11:18+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -36,7 +36,7 @@ msgstr "" msgid "File \"%s\": %s" msgstr "" -#: peekaboo/sample.py:497 +#: peekaboo/sample.py:478 #, python-format msgid "Sample %s successfully submitted to Cuckoo as job %d" msgstr "" @@ -99,107 +99,120 @@ msgstr "" msgid "No" msgstr "" -#: peekaboo/ruleset/engine.py:147 +#: peekaboo/ruleset/engine.py:148 msgid "Rule aborted with error" msgstr "" -#: peekaboo/ruleset/rules.py:122 +#: peekaboo/ruleset/rules.py:128 peekaboo/ruleset/rules.py:470 +msgid "" +"Behavioral analysis by Cuckoo has produced an error and did not finish " +"successfully" +msgstr "" + +#: peekaboo/ruleset/rules.py:164 msgid "File is not yet known to the system" msgstr "" -#: peekaboo/ruleset/rules.py:143 +#: peekaboo/ruleset/rules.py:185 #, python-format msgid "Failure to determine sample file size: %s" msgstr "" -#: peekaboo/ruleset/rules.py:148 +#: peekaboo/ruleset/rules.py:190 #, python-format msgid "File has more than %d bytes" msgstr "" -#: peekaboo/ruleset/rules.py:154 +#: peekaboo/ruleset/rules.py:196 #, python-format msgid "File is only %d bytes long" msgstr "" -#: peekaboo/ruleset/rules.py:176 +#: peekaboo/ruleset/rules.py:218 msgid "File type is on whitelist" msgstr "" -#: peekaboo/ruleset/rules.py:180 +#: peekaboo/ruleset/rules.py:222 msgid "File type is not on whitelist" msgstr "" -#: peekaboo/ruleset/rules.py:202 +#: peekaboo/ruleset/rules.py:244 msgid "File type is on the list of types to analyze" msgstr "" -#: peekaboo/ruleset/rules.py:207 +#: peekaboo/ruleset/rules.py:249 #, python-format msgid "File type is not on the list of types to analyse (%s)" msgstr "" -#: peekaboo/ruleset/rules.py:223 +#: peekaboo/ruleset/rules.py:265 msgid "File is not an office document" msgstr "" -#: peekaboo/ruleset/rules.py:247 +#: peekaboo/ruleset/rules.py:289 msgid "The file contains an Office macro" msgstr "" -#: peekaboo/ruleset/rules.py:251 +#: peekaboo/ruleset/rules.py:293 msgid "The file does not contain a recognizable Office macro" msgstr "" -#: peekaboo/ruleset/rules.py:272 +#: peekaboo/ruleset/rules.py:314 msgid "The file contains an Office macro which runs at document open" msgstr "" -#: peekaboo/ruleset/rules.py:277 +#: peekaboo/ruleset/rules.py:319 msgid "" "The file does not contain a recognizable Office macro that is run at " "document open" msgstr "" -#: peekaboo/ruleset/rules.py:307 peekaboo/ruleset/rules.py:445 -msgid "" -"Behavioral analysis by Cuckoo has produced an error and did not finish " -"successfully" -msgstr "" - -#: peekaboo/ruleset/rules.py:365 +#: peekaboo/ruleset/rules.py:390 msgid "No signature suggesting malware detected" msgstr "" -#: peekaboo/ruleset/rules.py:370 +#: peekaboo/ruleset/rules.py:395 #, python-format msgid "The following signatures have been recognized: %s" msgstr "" -#: peekaboo/ruleset/rules.py:389 +#: peekaboo/ruleset/rules.py:414 #, python-format msgid "Cuckoo score >= %s: %s" msgstr "" -#: peekaboo/ruleset/rules.py:394 +#: peekaboo/ruleset/rules.py:419 #, python-format msgid "Cuckoo score < %s: %s" msgstr "" -#: peekaboo/ruleset/rules.py:418 +#: peekaboo/ruleset/rules.py:443 #, python-format msgid "The file attempts to contact at least one domain on the blacklist (%s)" msgstr "" -#: peekaboo/ruleset/rules.py:424 +#: peekaboo/ruleset/rules.py:449 msgid "File does not seem to attempt contact with domains on the blacklist" msgstr "" -#: peekaboo/ruleset/rules.py:461 +#: peekaboo/ruleset/rules.py:486 msgid "Behavioral analysis by Cuckoo completed successfully" msgstr "" -#: peekaboo/ruleset/rules.py:478 +#: peekaboo/ruleset/rules.py:539 +msgid "Evaluation of expression uses undefined identifier." +msgstr "" + +#: peekaboo/ruleset/rules.py:544 +#, python-format +msgid "The rule (%s) classified the sample as %s" +msgstr "" + +#: peekaboo/ruleset/rules.py:550 +msgid "No rule classified the sample in any way." +msgstr "" + +#: peekaboo/ruleset/rules.py:562 msgid "File does not seem to exhibit recognizable malicious behaviour" msgstr "" diff --git a/peekaboo/ruleset/rules.py b/peekaboo/ruleset/rules.py index e5e789d..9bfae33 100644 --- a/peekaboo/ruleset/rules.py +++ b/peekaboo/ruleset/rules.py @@ -133,6 +133,19 @@ class Rule(object): 'Sample: %s', job_id, sample) raise PeekabooAnalysisDeferred() + def get_oletools_report(self, sample): + """ Get the samples oletools_report or generate it. + + @returns: OleReport + """ + report = sample.oletools_report + if report is not None: + return report + + oletool = Oletools() + report = OletoolsReport(oletool.get_report(sample)) + return report + class KnownRule(Rule): """ A rule determining if a sample is known by looking at the database for @@ -271,7 +284,7 @@ class OfficeMacroRule(OleRule): def evaluate_report(self, report): """ Report the sample as bad if it contains a macro. """ - if report.has_office_macros(): + if report.has_office_macros: return self.result(Result.bad, _("The file contains an Office macro"), False) @@ -485,15 +498,15 @@ class ExpressionRule(Rule): rule_name = 'expressions' def get_config(self): - expressions = self.get_config_value('expression', []) - if not expressions: + self.expressions = self.get_config_value('expression', []) + if not self.expressions: raise PeekabooRulesetConfigError( "List of expressions empty, check %s rule config." % self.rule_name) self.rules = [] parser = ExpressionParser() - for expr in expressions: + for expr in self.expressions: try: rule = parser.parse(expr) logger.debug("EXPR: %s", expr) @@ -504,19 +517,21 @@ class ExpressionRule(Rule): def evaluate(self, sample): """ Match what rules report against our known result status names. """ - for rule in self.rules: + for i, rule in enumerate(self.rules): result = None context = {'variables': {'sample': sample}} while result is None: try: - result = rule.eval(context = context) + result = rule.eval(context=context) # otherwise this is an endless loop if result is None: break except IdentifierMissingException as error: - if "cuckooreport" == error.args[0]: + if error.args[0] == "cuckooreport": context['variables']['cuckooreport'] = self.get_cuckoo_report(sample) + elif error.args[0] == "olereport": + context['variables']['olereport'] = self.get_oletools_report(sample) # here elif for other reports else: return self.result( diff --git a/peekaboo/toolbox/ole.py b/peekaboo/toolbox/ole.py index e4a3ce0..b4d9eb6 100644 --- a/peekaboo/toolbox/ole.py +++ b/peekaboo/toolbox/ole.py @@ -77,7 +77,7 @@ class Oletools(object): pass except Exception as error: logger.exception(error) - sample.register_oletools_report(report) + sample.register_oletools_report(OletoolsReport(report)) return report @@ -86,6 +86,10 @@ class OletoolsReport(object): def __init__(self, report): self.report = report + def __str__(self): + return "<OletoolsReport('%s'>" % self.report + + @property def has_office_macros(self): """ Detects macros in Microsoft Office documents. @@ -93,12 +97,23 @@ class OletoolsReport(object): @return: True if macros where found, otherwise False. If VBA_Parser crashes it returns False too. """ - + try: return self.report['has_macros'] except KeyError: return False + @property + def vba_code(self): + """ + Extracts vba code from Microsoft Office documents. + @return: vba code if found, otherwise empty string. + """ + try: + return self.report['vba'] + except KeyError: + return "" + def has_office_macros_with_suspicious_keyword(self, suspicious_keywords): """ Detects macros with supplied suspicious keywords in Microsoft Office documents. diff --git a/ruleset.conf.sample b/ruleset.conf.sample index 6b7c956..e0b2ef2 100644 --- a/ruleset.conf.sample +++ b/ruleset.conf.sample @@ -90,6 +90,8 @@ expression.3 : sample.meta_info_name_declared == 'signature.asc' and sample.meta_info_type_declared in { 'application/pgp-signature' } -> ignore +expression.4 : olereport.has_office_macros == True + and cuckooreport.score > 4 -> bad [cuckoo_evil_sig] signature.1 : A potential heapspray has been detected. .* diff --git a/tests/test.py b/tests/test.py index bec3d97..6ab72b2 100755 --- a/tests/test.py +++ b/tests/test.py @@ -862,21 +862,21 @@ unknown : baz''' def test_rule_ignore_mail_signatures(self): """ Test rule to ignore cryptographic mail signatures. """ config = '''[expressions] - expression.4 : sample.meta_info_name_declared == 'smime.p7s' + expression.1 : sample.meta_info_name_declared == 'smime.p7s' and sample.meta_info_type_declared in { 'application/pkcs7-signature', 'application/x-pkcs7-signature', 'application/pkcs7-mime', 'application/x-pkcs7-mime' } -> ignore - expression.3 : sample.meta_info_name_declared == 'signature.asc' + expression.2 : sample.meta_info_name_declared == 'signature.asc' and sample.meta_info_type_declared in { 'application/pgp-signature' } -> ignore''' - part = { "full_name": "p001", - "name_declared": "smime.p7s", - "type_declared": "application/pkcs7-signature" + part = {"full_name": "p001", + "name_declared": "smime.p7s", + "type_declared": "application/pkcs7-signature" } factory = SampleFactory( @@ -928,6 +928,55 @@ unknown : baz''' result = rule.evaluate(sample) self.assertEqual(result.result, Result.bad) + def test_rule_expressions_cuckooreport_context(self): + """ Test generic rule cuckooreport context """ + config = '''[expressions] + expression.3 : "EVIL" in cuckooreport.signature_descriptions + and cuckooreport.score > 4 -> bad + ''' + + factory = CreatingSampleFactory( + cuckoo=None, base_dir=None, job_hash_regex=None, + keep_mail_data=False, processing_info_dir=None) + tests_data_dir = os.path.dirname(os.path.abspath(__file__))+"/test-data" + + report = { + "signatures": [ + {"description": "EVIL"} + ], + "info": {"score": 4.2} + } + cuckooreport = CuckooReport(report) + + sample = factory.make_sample(tests_data_dir+'/office/blank.doc') + sample.register_cuckoo_report(cuckooreport) + rule = ExpressionRule(CreatingConfigParser(config)) + result = rule.evaluate(sample) + self.assertEqual(result.result, Result.bad) + + def test_rule_expressions_olereport_context(self): + """ Test generic rule olereport context """ + config = '''[expressions] + expression.3 : olereport.has_office_macros == True -> bad + ''' + + factory = CreatingSampleFactory( + cuckoo=None, base_dir=None, job_hash_regex=None, + keep_mail_data=False, processing_info_dir=None) + tests_data_dir = os.path.dirname(os.path.abspath(__file__))+"/test-data" + + sample = factory.make_sample(tests_data_dir+'/office/suspiciousMacro.doc') + rule = ExpressionRule(CreatingConfigParser(config)) + result = rule.evaluate(sample) + self.assertEqual(result.result, Result.bad) + + config = '''[expressions] + expression.3 : /suspicious/ in olereport.vba_code -> bad + ''' + rule = ExpressionRule(CreatingConfigParser(config)) + result = rule.evaluate(sample) + self.assertEqual(result.result, Result.bad) + def test_config_file_type_on_whitelist(self): """ Test whitelist rule configuration. """ config = '''[file_type_on_whitelist] |