Skip to content

Commit b39c78f

Browse files
authored
Merge pull request #411 from target/yara-compiled-fix
Safely Account for a YARA Compilation Failure
2 parents 98c4070 + 09f5c1a commit b39c78f

File tree

3 files changed

+118
-30
lines changed

3 files changed

+118
-30
lines changed

src/python/strelka/scanners/scan_yara.py

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ def scan(self, data, file, options, expire_at):
5858
self.load_yara_rules(options)
5959
if not self.compiled_yara:
6060
self.flags.append("no_rules_loaded")
61-
return
6261

6362
# Set the total rules loaded
6463
self.event["rules_loaded"] = self.rules_loaded
@@ -79,35 +78,36 @@ def scan(self, data, file, options, expire_at):
7978
self.event["hex"] = []
8079

8180
# Match the data against the YARA rules.
82-
yara_matches = self.compiled_yara.match(data=data)
83-
for match in yara_matches:
84-
# Append rule matches and update tags.
85-
self.event["matches"].append(match.rule)
86-
self.event["tags"].extend(match.tags)
87-
88-
# Extract hex representation if configured to store offsets.
89-
if self.store_offset and self.offset_meta_key:
90-
if match.meta.get(self.offset_meta_key):
91-
for string_data in match.strings:
92-
for instance in string_data.instances:
93-
offset = instance.offset
94-
matched_string = instance.matched_data
95-
self.extract_match_hex(
96-
match.rule,
97-
offset,
98-
matched_string,
99-
data,
100-
self.offset_padding,
101-
)
102-
103-
# Append meta information if configured to do so.
104-
for k, v in match.meta.items():
105-
self.event["meta"].append(
106-
{"rule": match.rule, "identifier": k, "value": v}
107-
)
81+
if self.compiled_yara:
82+
yara_matches = self.compiled_yara.match(data=data)
83+
for match in yara_matches:
84+
# Append rule matches and update tags.
85+
self.event["matches"].append(match.rule)
86+
self.event["tags"].extend(match.tags)
87+
88+
# Extract hex representation if configured to store offsets.
89+
if self.store_offset and self.offset_meta_key:
90+
if match.meta.get(self.offset_meta_key):
91+
for string_data in match.strings:
92+
for instance in string_data.instances:
93+
offset = instance.offset
94+
matched_string = instance.matched_data
95+
self.extract_match_hex(
96+
match.rule,
97+
offset,
98+
matched_string,
99+
data,
100+
self.offset_padding,
101+
)
102+
103+
# Append meta information if configured to do so.
104+
for k, v in match.meta.items():
105+
self.event["meta"].append(
106+
{"rule": match.rule, "identifier": k, "value": v}
107+
)
108108

109-
# De-duplicate tags.
110-
self.event["tags"] = list(set(self.event["tags"]))
109+
# De-duplicate tags.
110+
self.event["tags"] = list(set(self.event["tags"]))
111111

112112
def load_yara_rules(self, options):
113113
"""Loads YARA rules based on the provided path.
@@ -159,7 +159,8 @@ def load_yara_rules(self, options):
159159
self.flags.append(f"compiling_error_syntax_{e}")
160160

161161
# Set the total rules loaded.
162-
self.rules_loaded = len(list(self.compiled_yara))
162+
if self.compiled_yara:
163+
self.rules_loaded = len(list(self.compiled_yara))
163164

164165
def extract_match_hex(self, rule, offset, matched_string, data, offset_padding=32):
165166
"""
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
This Yara ruleset is under the GNU-GPLv2 license (http://www.gnu.org/licenses/gpl-2.0.html) and open to any user or organization, as
3+
long as you use it under this license.
4+
*/
5+
6+
rule ELF_Linux_Torte : Linux ELF
7+
8+
{
9+
meta:
10+
author = "@mmorenog,@yararules"
11+
description = "Detects ELF Linux/Torte infection"
12+
ref = "http://blog.malwaremustdie.org/2016/01/mmd-0050-2016-incident-report-elf.html"
13+
hash1 = "1faf27f6b8e8a9cadb611f668a01cf73"
14+
hash2 = "cb0477445fef9c5f1a5b6689bbfb941e"
15+
16+
strings:
17+
$s0 = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)"
18+
$s1 = "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.7.6)"
19+
$s2 = "?sessd="
20+
$s3 = "&sessc="
21+
$s4 = "&sessk="
22+
$s5 = "3a08fe7b8c4da6ed09f21c3ef97efce2"
23+
$s6 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
24+
$s7 = "_ZN11CThreadPool10getBatchesERSt6vectorISt4pairISsiESaIS2_EE"
25+
$s8 = "_ZNSs4_Rep10_M_destroyERKSaIcE@@GLIBCXX_3.4"
26+
$s9 = "_ZNSt6vectorImSaImEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPmS1_EERKm"
27+
$s10 = "_ZNSt6vectorISt4pairISsiESaIS1_EE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPS1_S3_EERKS1_"
28+
$s11 = "_ZSt20__throw_out_of_rangePKc@@GLIBCXX_3.4"
29+
30+
condition:
31+
is__elf and all of ($s*)
32+
}
33+
34+
35+
rule ELF_Linux_Torte_domains {
36+
meta:
37+
author = "@mmorenog,@yararules"
38+
description = "Detects ELF Linux/Torte infection"
39+
ref1 = "http://blog.malwaremustdie.org/2016/01/mmd-0050-2016-incident-report-elf.html"
40+
strings:
41+
$1 = "pages.touchpadz.com" ascii wide nocase
42+
$2 = "bat.touchpadz.com" ascii wide nocase
43+
$3 = "stat.touchpadz.com" ascii wide nocase
44+
$4 = "sk2.touchpadz.com" ascii wide nocase
45+
46+
condition:
47+
any of them
48+
}

src/python/strelka/tests/test_scan_yara.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,45 @@ def test_scan_yara(mocker):
3838
TestCase().assertDictEqual(test_scan_event, scanner_event)
3939

4040

41+
def test_scan_bad_yara(mocker):
42+
"""
43+
This test was implemented to test a more complex and unsupported rule. A bug was observed that was
44+
not triggered by the basic YARA test.
45+
Src: https://github.com/target/strelka/issues/410
46+
Pass: Sample event matches output of scanner.
47+
Failure: Unable to load file or sample event fails to match.
48+
"""
49+
50+
test_scan_event = {
51+
"elapsed": mock.ANY,
52+
"flags": [
53+
'compiling_error_general_/strelka/strelka/tests/fixtures/test_elk_linux_torte.yara(31): undefined identifier "is__elf"',
54+
"no_rules_loaded",
55+
],
56+
"matches": [],
57+
"rules_loaded": 0,
58+
"meta": mock.ANY,
59+
"tags": [],
60+
"hex": [],
61+
}
62+
63+
scanner_event = run_test_scan(
64+
mocker=mocker,
65+
scan_class=ScanUnderTest,
66+
fixture_path=Path(__file__).parent / "fixtures/test.txt",
67+
options={
68+
"location": str(Path(Path(__file__).parent / "fixtures/")),
69+
"compiled": {
70+
"enabled": False,
71+
"filename": "rules.compiled",
72+
},
73+
},
74+
)
75+
76+
TestCase.maxDiff = None
77+
TestCase().assertDictEqual(test_scan_event, scanner_event)
78+
79+
4180
def test_scan_yara_hex_extraction(mocker):
4281
"""
4382
Pass: Sample event matches output of scanner.

0 commit comments

Comments
 (0)