From 749b9e8405e4db57930028a7d83fb38cd9eb5aa9 Mon Sep 17 00:00:00 2001 From: ziad hany Date: Mon, 24 Nov 2025 23:04:21 +0200 Subject: [PATCH 1/4] Add a pipeline to collect Sigma rules Signed-off-by: ziad hany --- vulnerabilities/improvers/__init__.py | 2 + .../migrations/0104_advisorydetectionrule.py | 59 ++++++++++++ vulnerabilities/models.py | 28 ++++++ .../pipelines/v2_improvers/sigma_rules.py | 96 +++++++++++++++++++ .../v2_improvers/test_sigma_rules.py | 72 ++++++++++++++ ...web_cve_2010_5278_exploitation_attempt.yml | 26 +++++ ...oc_creation_win_exploit_cve_2025_10035.yml | 78 +++++++++++++++ .../image_load_win_exploit_cve_2025_33053.yml | 45 +++++++++ 8 files changed, 406 insertions(+) create mode 100644 vulnerabilities/migrations/0104_advisorydetectionrule.py create mode 100644 vulnerabilities/pipelines/v2_improvers/sigma_rules.py create mode 100644 vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py create mode 100644 vulnerabilities/tests/test_data/sigma/CVE-2010-5278/web_cve_2010_5278_exploitation_attempt.yml create mode 100644 vulnerabilities/tests/test_data/sigma/CVE-2025-10035/proc_creation_win_exploit_cve_2025_10035.yml create mode 100644 vulnerabilities/tests/test_data/sigma/CVE-2025-33053/image_load_win_exploit_cve_2025_33053.yml diff --git a/vulnerabilities/improvers/__init__.py b/vulnerabilities/improvers/__init__.py index aa9312ec1..7318e8eba 100644 --- a/vulnerabilities/improvers/__init__.py +++ b/vulnerabilities/improvers/__init__.py @@ -30,6 +30,7 @@ enhance_with_metasploit as enhance_with_metasploit_v2, ) from vulnerabilities.pipelines.v2_improvers import flag_ghost_packages as flag_ghost_packages_v2 +from vulnerabilities.pipelines.v2_improvers import sigma_rules from vulnerabilities.pipelines.v2_improvers import unfurl_version_range as unfurl_version_range_v2 from vulnerabilities.utils import create_registry @@ -70,5 +71,6 @@ compute_advisory_todo_v2.ComputeToDo, unfurl_version_range_v2.UnfurlVersionRangePipeline, compute_advisory_todo.ComputeToDo, + sigma_rules.SigmaRulesImproverPipeline, ] ) diff --git a/vulnerabilities/migrations/0104_advisorydetectionrule.py b/vulnerabilities/migrations/0104_advisorydetectionrule.py new file mode 100644 index 000000000..09f0757ab --- /dev/null +++ b/vulnerabilities/migrations/0104_advisorydetectionrule.py @@ -0,0 +1,59 @@ +# Generated by Django 4.2.25 on 2025-11-24 19:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("vulnerabilities", "0103_codecommit_impactedpackage_affecting_commits_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="AdvisoryDetectionRule", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ( + "rule_text", + models.TextField( + help_text="Full text of the detection rule, script, or signature." + ), + ), + ( + "rule_type", + models.CharField( + blank=True, + choices=[ + ("yara", "YARA"), + ("sigma", "Sigma Detection Rule"), + ("clamav", "ClamAV Signature"), + ], + max_length=100, + ), + ), + ( + "source_url", + models.URLField( + blank=True, + help_text="URL or reference to the source of the rule (vendor feed, GitHub repo, etc.).", + null=True, + ), + ), + ( + "advisory", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="detection_rules", + to="vulnerabilities.advisoryv2", + ), + ), + ], + ), + ] diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index e1c4ddc6b..e975f7526 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -3414,3 +3414,31 @@ class CodeCommit(models.Model): class Meta: unique_together = ("commit_hash", "vcs_url") + + +class AdvisoryDetectionRule(models.Model): + """ + A detection rule (YARA, Sigma, ClamAV) linked to an advisory. + """ + + RULE_TYPES = [ + ("yara", "YARA"), + ("sigma", "Sigma Detection Rule"), + ("clamav", "ClamAV Signature"), + ] + + advisory = models.ForeignKey( + AdvisoryV2, + related_name="detection_rules", + on_delete=models.CASCADE, + ) + + rule_text = models.TextField(help_text="Full text of the detection rule, script, or signature.") + + rule_type = models.CharField(max_length=100, choices=RULE_TYPES, blank=True) + + source_url = models.URLField( + null=True, + blank=True, + help_text="URL or reference to the source of the rule (vendor feed, GitHub repo, etc.).", + ) diff --git a/vulnerabilities/pipelines/v2_improvers/sigma_rules.py b/vulnerabilities/pipelines/v2_improvers/sigma_rules.py new file mode 100644 index 000000000..e82ac8a86 --- /dev/null +++ b/vulnerabilities/pipelines/v2_improvers/sigma_rules.py @@ -0,0 +1,96 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/aboutcode-org/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +from pathlib import Path + +import saneyaml +from aboutcode.pipeline import LoopProgress +from fetchcode.vcs import fetch_via_vcs +from yaml import YAMLError + +from vulnerabilities.models import AdvisoryAlias +from vulnerabilities.models import AdvisoryDetectionRule +from vulnerabilities.pipelines import VulnerableCodePipeline +from vulnerabilities.utils import find_all_cve + + +class SigmaRulesImproverPipeline(VulnerableCodePipeline): + pipeline_id = "sigma_rules" + repo_url = "git+https://github.com/SigmaHQ/sigma" + license_url = "https://github.com/SigmaHQ/Detection-Rule-License" + + @classmethod + def steps(cls): + return ( + cls.clone_repo, + cls.collect_and_store_rules, + cls.clean_downloads, + ) + + def clone_repo(self): + self.log(f"Cloning `{self.repo_url}`") + self.vcs_response = fetch_via_vcs(self.repo_url) + + def collect_and_store_rules(self): + """ + Collect Sigma YAML rules from the destination directory and store/update + them as AdvisoryDetectionRule objects. + """ + + base_directory = Path(self.vcs_response.dest_dir) + yaml_files = list(base_directory.rglob("**/*.yml")) + rules_count = len(yaml_files) + + self.log(f"Enhancing the vulnerability with {rules_count:,d} rule records") + progress = LoopProgress(total_iterations=rules_count, logger=self.log) + for file_path in progress.iter(yaml_files): + cve_ids = find_all_cve(str(file_path)) + if not cve_ids or len(cve_ids) > 1: + continue + + cve_id = cve_ids[0] + + with open(file_path, "r") as f: + try: + rule_data = saneyaml.load(f) + except YAMLError as err: + self.log(f"Invalid YAML in {file_path}: {err}. Skipping.") + continue + + advisories = set() + try: + if alias := AdvisoryAlias.objects.get(alias=cve_id): + for adv in alias.advisories.all(): + advisories.add(adv) + except AdvisoryAlias.DoesNotExist: + self.log(f"Advisory {file_path.name} not found.") + continue + + rule_text = saneyaml.dump(rule_data) + rule_url = f"https://raw.githubusercontent.com/SigmaHQ/sigma/refs/heads/master/{file_path.relative_to(base_directory)}" + + for advisory in advisories: + AdvisoryDetectionRule.objects.update_or_create( + advisory=advisory, + rule_type="sigma", + defaults={ + "rule_text": rule_text, + "source_url": rule_url, + }, + ) + + self.log(f"Successfully added {rules_count:,d} rules advisory") + + def clean_downloads(self): + if self.vcs_response: + self.log(f"Removing cloned repository") + self.vcs_response.delete() + + def on_failure(self): + self.clean_downloads() diff --git a/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py b/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py new file mode 100644 index 000000000..417a1ac2a --- /dev/null +++ b/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py @@ -0,0 +1,72 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/aboutcode-org/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +import os +from datetime import datetime +from unittest import mock +from unittest.mock import MagicMock + +import pytest + +from vulnerabilities.models import AdvisoryAlias +from vulnerabilities.models import AdvisoryDetectionRule +from vulnerabilities.models import AdvisoryV2 +from vulnerabilities.pipelines.v2_improvers.sigma_rules import SigmaRulesImproverPipeline + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + +TEST_REPO_DIR = os.path.join(BASE_DIR, "../../test_data/sigma") + + +@pytest.mark.django_db +@mock.patch("vulnerabilities.pipelines.v2_improvers.sigma_rules.fetch_via_vcs") +def test_sigma_rules_db_improver(mock_fetch_via_vcs): + mock_vcs = MagicMock() + mock_vcs.dest_dir = TEST_REPO_DIR + mock_vcs.delete = MagicMock() + mock_fetch_via_vcs.return_value = mock_vcs + + adv1 = AdvisoryV2.objects.create( + advisory_id="VCIO-123-0001", + datasource_id="ds", + avid="ds/VCIO-123-0001", + unique_content_id="sgsdg45", + url="https://test.com", + date_collected=datetime.now(), + ) + adv2 = AdvisoryV2.objects.create( + advisory_id="VCIO-123-1002", + datasource_id="ds", + avid="ds/VCIO-123-1002", + unique_content_id="6hd4d6f", + url="https://test.com", + date_collected=datetime.now(), + ) + adv3 = AdvisoryV2.objects.create( + advisory_id="VCIO-123-1003", + datasource_id="ds", + avid="ds/VCIO-123-1003", + unique_content_id="sd6h4sh", + url="https://test.com", + date_collected=datetime.now(), + ) + + alias1 = AdvisoryAlias.objects.create(alias="CVE-2025-33053") + alias2 = AdvisoryAlias.objects.create(alias="CVE-2025-10035") + alias3 = AdvisoryAlias.objects.create(alias="CVE-2010-5278") + adv1.aliases.add(alias1) + adv2.aliases.add(alias2) + adv3.aliases.add(alias3) + + improver = SigmaRulesImproverPipeline() + improver.execute() + + assert len(AdvisoryDetectionRule.objects.all()) == 3 + sigma_rule = AdvisoryDetectionRule.objects.first() + assert sigma_rule.rule_type == "sigma" diff --git a/vulnerabilities/tests/test_data/sigma/CVE-2010-5278/web_cve_2010_5278_exploitation_attempt.yml b/vulnerabilities/tests/test_data/sigma/CVE-2010-5278/web_cve_2010_5278_exploitation_attempt.yml new file mode 100644 index 000000000..69f889e4d --- /dev/null +++ b/vulnerabilities/tests/test_data/sigma/CVE-2010-5278/web_cve_2010_5278_exploitation_attempt.yml @@ -0,0 +1,26 @@ +title: CVE-2010-5278 Exploitation Attempt +id: a4a899e8-fd7a-49dd-b5a8-7044def72d61 +status: test +description: | + MODx manager - Local File Inclusion:Directory traversal vulnerability in manager/controllers/default/resource/tvs.php in MODx Revolution 2.0.2-pl, and possibly earlier, + when magic_quotes_gpc is disabled, allows remote attackers to read arbitrary files via a .. (dot dot) in the class_key parameter. +references: + - https://github.com/projectdiscovery/nuclei-templates +author: Subhash Popuri (@pbssubhash) +date: 2021-08-25 +modified: 2023-01-02 +tags: + - attack.initial-access + - attack.t1190 + - cve.2010-5278 + - detection.emerging-threats +logsource: + category: webserver +detection: + selection: + cs-uri-query|contains: /manager/controllers/default/resource/tvs.php?class_key=../../../../../../../../../../windows/win.ini%00 + condition: selection +falsepositives: + - Scanning from Nuclei + - Unknown +level: critical \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/sigma/CVE-2025-10035/proc_creation_win_exploit_cve_2025_10035.yml b/vulnerabilities/tests/test_data/sigma/CVE-2025-10035/proc_creation_win_exploit_cve_2025_10035.yml new file mode 100644 index 000000000..65ed3fc01 --- /dev/null +++ b/vulnerabilities/tests/test_data/sigma/CVE-2025-10035/proc_creation_win_exploit_cve_2025_10035.yml @@ -0,0 +1,78 @@ +title: Potential Exploitation of GoAnywhere MFT Vulnerability +id: 6c76b3d0-afe4-4870-9443-ffe6773c5fef +status: experimental +description: | + Detects suspicious command execution by child processes of the GoAnywhere Managed File Transfer (MFT) application, which may indicate exploitation such as CVE-2025-10035. + This behavior is indicative of post-exploitation activity related to CVE-2025-10035, as observed in campaigns by the threat actor Storm-1175. +references: + - https://www.microsoft.com/en-us/security/blog/2025/10/06/investigating-active-exploitation-of-cve-2025-10035-goanywhere-managed-file-transfer-vulnerability/ +author: MSFT (idea), Swachchhanda Shrawan Poudel (Nextron Systems) +date: 2025-10-07 +tags: + - attack.initial-access + - attack.t1190 + - attack.execution + - attack.t1059.001 + - attack.persistence + - attack.t1133 + - detection.emerging-threats + - cve.2025-10035 +logsource: + category: process_creation + product: windows +detection: + # Detects the GoAnywhere Tomcat parent process based on path and command line arguments + selection_parent: + ParentImage|contains: '\GoAnywhere\tomcat\' + selection_powershell_img: + Image|endswith: + - '\powershell.exe' + - '\powershell_ise.exe' + - '\pwsh.exe' + selection_powershell_cmd: + - CommandLine|contains|all: + - 'IEX' + - 'enc' + - 'Hidden' + - 'bypass' + - CommandLine|re: + - 'net\s+user' + - 'net\s+group' + - 'query\s+session' + - CommandLine|contains: + - 'whoami' + - 'systeminfo' + - 'dsquery' + - 'localgroup administrators' + - 'nltest' + - 'samaccountname=' + - 'adscredentials' + - 'o365accountconfiguration' + - '.DownloadString(' + - '.DownloadFile(' + - 'FromBase64String(' + - 'System.IO.Compression' + - 'System.IO.MemoryStream' + - 'curl' + selection_child_cmd: + Image|endswith: '\cmd.exe' + CommandLine|contains: + - 'powershell' + - 'whoami' + - 'net.exe' + - 'net1.exe' + - 'rundll32' + - 'quser' + - 'nltest' + - 'curl' + selection_child_others: + CommandLine|contains: + - 'bitsadmin' + - 'certutil' + - 'mshta' + - 'cscript' + - 'wscript' + condition: selection_parent and (all of selection_powershell_* or 1 of selection_child_*) +falsepositives: + - Legitimate administrative scripts or built-in GoAnywhere functions could potentially trigger this rule. Tuning may be required based on normal activity in your environment. +level: high \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/sigma/CVE-2025-33053/image_load_win_exploit_cve_2025_33053.yml b/vulnerabilities/tests/test_data/sigma/CVE-2025-33053/image_load_win_exploit_cve_2025_33053.yml new file mode 100644 index 000000000..1be01da05 --- /dev/null +++ b/vulnerabilities/tests/test_data/sigma/CVE-2025-33053/image_load_win_exploit_cve_2025_33053.yml @@ -0,0 +1,45 @@ +title: Potential Exploitation of RCE Vulnerability CVE-2025-33053 - Image Load +id: 04fc4b22-91a6-495a-879d-0144fec5ec03 +related: + - id: abe06362-a5b9-4371-8724-ebd00cd48a04 + type: similar + - id: 9a2d8b3e-f5a1-4c68-9e21-7d9e1cf8a123 + type: similar +status: experimental +description: | + Detects potential exploitation of remote code execution vulnerability CVE-2025-33053 + by monitoring suspicious image loads from WebDAV paths. The exploit involves malicious executables from + attacker-controlled WebDAV servers loading the Windows system DLLs like gdi32.dll, netapi32.dll, etc. +references: + - https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2025-33053 + - https://research.checkpoint.com/2025/stealth-falcon-zero-day/ +author: Swachchhanda Shrawan Poudel (Nextron Systems) +date: 2025-06-13 +tags: + - attack.command-and-control + - attack.execution + - attack.defense-evasion + - attack.t1218 + - attack.lateral-movement + - attack.t1105 + - detection.emerging-threats + - cve.2025-33053 +logsource: + category: image_load + product: windows +detection: + selection_img_path: + Image|startswith: '\\\\' + Image|contains: '\DavWWWRoot\' + selection_img_bin: + Image|endswith: + - '\route.exe' + - '\netsh.exe' + - '\makecab.exe' + - '\dxdiag.exe' + - '\ipconfig.exe' + - '\explorer.exe' + condition: all of selection_* +falsepositives: + - Unknown +level: high \ No newline at end of file From 470fc75392fac88f674e0434487256f61d106f07 Mon Sep 17 00:00:00 2001 From: ziad hany Date: Wed, 3 Dec 2025 04:55:40 +0200 Subject: [PATCH 2/4] Rename and update DetectionRule model Update sigma rules improver Signed-off-by: ziad hany --- ...ectionrule_delete_advisorydetectionrule.py | 71 +++++++++++++++++++ vulnerabilities/models.py | 44 ++++++++---- .../pipelines/v2_improvers/sigma_rules.py | 52 ++++++++------ .../v2_improvers/test_sigma_rules.py | 6 +- 4 files changed, 132 insertions(+), 41 deletions(-) create mode 100644 vulnerabilities/migrations/0105_detectionrule_delete_advisorydetectionrule.py diff --git a/vulnerabilities/migrations/0105_detectionrule_delete_advisorydetectionrule.py b/vulnerabilities/migrations/0105_detectionrule_delete_advisorydetectionrule.py new file mode 100644 index 000000000..cf1e96b32 --- /dev/null +++ b/vulnerabilities/migrations/0105_detectionrule_delete_advisorydetectionrule.py @@ -0,0 +1,71 @@ +# Generated by Django 4.2.25 on 2025-12-03 02:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("vulnerabilities", "0104_advisorydetectionrule"), + ] + + operations = [ + migrations.CreateModel( + name="DetectionRule", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ( + "rule_type", + models.CharField( + choices=[ + ("yara", "Yara"), + ("yara-x", "Yara-X"), + ("sigma", "Sigma"), + ("clamav", "ClamAV"), + ("suricata", "Suricata"), + ], + help_text="The type of the detection rule content (e.g., YARA, Sigma).", + max_length=50, + ), + ), + ( + "source_url", + models.URLField( + help_text="URL to the original source or reference for this rule.", + max_length=1024, + ), + ), + ( + "rule_metadata", + models.JSONField( + blank=True, + help_text="Additional structured data such as tags, or author information.", + null=True, + ), + ), + ( + "rule_text", + models.TextField(help_text="The content of the detection signature."), + ), + ( + "advisory", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="detection_rules", + to="vulnerabilities.advisoryv2", + ), + ), + ], + ), + migrations.DeleteModel( + name="AdvisoryDetectionRule", + ), + ] diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index e975f7526..4d646a5df 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -3416,29 +3416,43 @@ class Meta: unique_together = ("commit_hash", "vcs_url") -class AdvisoryDetectionRule(models.Model): +class DetectionRuleTypes(models.TextChoices): + """Defines the supported formats for security detection rules.""" + + YARA = "yara", "Yara" + YARA_X = "yara-x", "Yara-X" + SIGMA = "sigma", "Sigma" + CLAMAV = "clamav", "ClamAV" + SURICATA = "suricata", "Suricata" + + +class DetectionRule(models.Model): """ - A detection rule (YARA, Sigma, ClamAV) linked to an advisory. + A Detection Rule is code used to identify malicious activity or security threats. """ - RULE_TYPES = [ - ("yara", "YARA"), - ("sigma", "Sigma Detection Rule"), - ("clamav", "ClamAV Signature"), - ] + rule_type = models.CharField( + max_length=50, + choices=DetectionRuleTypes.choices, + help_text="The type of the detection rule content (e.g., YARA, Sigma).", + ) - advisory = models.ForeignKey( - AdvisoryV2, - related_name="detection_rules", - on_delete=models.CASCADE, + source_url = models.URLField( + max_length=1024, help_text="URL to the original source or reference for this rule." ) - rule_text = models.TextField(help_text="Full text of the detection rule, script, or signature.") + rule_metadata = models.JSONField( + null=True, + blank=True, + help_text="Additional structured data such as tags, or author information.", + ) - rule_type = models.CharField(max_length=100, choices=RULE_TYPES, blank=True) + rule_text = models.TextField(help_text="The content of the detection signature.") - source_url = models.URLField( + advisory = models.ForeignKey( + AdvisoryV2, + related_name="detection_rules", + on_delete=models.SET_NULL, null=True, blank=True, - help_text="URL or reference to the source of the rule (vendor feed, GitHub repo, etc.).", ) diff --git a/vulnerabilities/pipelines/v2_improvers/sigma_rules.py b/vulnerabilities/pipelines/v2_improvers/sigma_rules.py index e82ac8a86..a22527aa0 100644 --- a/vulnerabilities/pipelines/v2_improvers/sigma_rules.py +++ b/vulnerabilities/pipelines/v2_improvers/sigma_rules.py @@ -9,13 +9,13 @@ from pathlib import Path -import saneyaml from aboutcode.pipeline import LoopProgress from fetchcode.vcs import fetch_via_vcs from yaml import YAMLError from vulnerabilities.models import AdvisoryAlias -from vulnerabilities.models import AdvisoryDetectionRule +from vulnerabilities.models import DetectionRule +from vulnerabilities.models import DetectionRuleTypes from vulnerabilities.pipelines import VulnerableCodePipeline from vulnerabilities.utils import find_all_cve @@ -50,42 +50,48 @@ def collect_and_store_rules(self): self.log(f"Enhancing the vulnerability with {rules_count:,d} rule records") progress = LoopProgress(total_iterations=rules_count, logger=self.log) for file_path in progress.iter(yaml_files): - cve_ids = find_all_cve(str(file_path)) - if not cve_ids or len(cve_ids) > 1: + if any(part in [".github", "images", "documentation"] for part in file_path.parts): continue - cve_id = cve_ids[0] - with open(file_path, "r") as f: try: - rule_data = saneyaml.load(f) + rule_data = f.read() except YAMLError as err: self.log(f"Invalid YAML in {file_path}: {err}. Skipping.") continue - advisories = set() - try: - if alias := AdvisoryAlias.objects.get(alias=cve_id): - for adv in alias.advisories.all(): - advisories.add(adv) - except AdvisoryAlias.DoesNotExist: - self.log(f"Advisory {file_path.name} not found.") - continue - - rule_text = saneyaml.dump(rule_data) rule_url = f"https://raw.githubusercontent.com/SigmaHQ/sigma/refs/heads/master/{file_path.relative_to(base_directory)}" + cve_ids = find_all_cve(str(file_path)) + found_advisories = set() + for cve_id in cve_ids: + try: + alias = AdvisoryAlias.objects.get(alias=cve_id) + for adv in alias.advisories.all(): + found_advisories.add(adv) + except AdvisoryAlias.DoesNotExist: + self.log(f"Advisory {file_path.name} not found.") + continue - for advisory in advisories: - AdvisoryDetectionRule.objects.update_or_create( - advisory=advisory, - rule_type="sigma", + for adv in found_advisories: + DetectionRule.objects.update_or_create( + rule_text=rule_data, + advisory=adv, defaults={ - "rule_text": rule_text, + "rule_type": DetectionRuleTypes.SIGMA, "source_url": rule_url, }, ) - self.log(f"Successfully added {rules_count:,d} rules advisory") + if not found_advisories: + DetectionRule.objects.update_or_create( + rule_text=rule_data, + advisory=None, + defaults={ + "rule_type": DetectionRuleTypes.SIGMA, + "source_url": rule_url, + }, + ) + self.log(f"Successfully processed rules.") def clean_downloads(self): if self.vcs_response: diff --git a/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py b/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py index 417a1ac2a..42abc00f9 100644 --- a/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py +++ b/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py @@ -15,8 +15,8 @@ import pytest from vulnerabilities.models import AdvisoryAlias -from vulnerabilities.models import AdvisoryDetectionRule from vulnerabilities.models import AdvisoryV2 +from vulnerabilities.models import DetectionRule from vulnerabilities.pipelines.v2_improvers.sigma_rules import SigmaRulesImproverPipeline BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -67,6 +67,6 @@ def test_sigma_rules_db_improver(mock_fetch_via_vcs): improver = SigmaRulesImproverPipeline() improver.execute() - assert len(AdvisoryDetectionRule.objects.all()) == 3 - sigma_rule = AdvisoryDetectionRule.objects.first() + assert len(DetectionRule.objects.all()) == 3 + sigma_rule = DetectionRule.objects.first() assert sigma_rule.rule_type == "sigma" From 7f1c1f802e5f73cf987abdf02d85161dd48bda40 Mon Sep 17 00:00:00 2001 From: ziad hany Date: Thu, 4 Dec 2025 01:47:34 +0200 Subject: [PATCH 3/4] Update sigma rule importer to store metadata Signed-off-by: ziad hany --- .../pipelines/v2_improvers/sigma_rules.py | 64 +++++++++++++------ .../v2_improvers/test_sigma_rules.py | 7 ++ 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/vulnerabilities/pipelines/v2_improvers/sigma_rules.py b/vulnerabilities/pipelines/v2_improvers/sigma_rules.py index a22527aa0..66e02e43f 100644 --- a/vulnerabilities/pipelines/v2_improvers/sigma_rules.py +++ b/vulnerabilities/pipelines/v2_improvers/sigma_rules.py @@ -6,12 +6,12 @@ # See https://github.com/aboutcode-org/vulnerablecode for support or download. # See https://aboutcode.org for more information about nexB OSS projects. # - +import datetime from pathlib import Path +import yaml from aboutcode.pipeline import LoopProgress from fetchcode.vcs import fetch_via_vcs -from yaml import YAMLError from vulnerabilities.models import AdvisoryAlias from vulnerabilities.models import DetectionRule @@ -40,28 +40,27 @@ def clone_repo(self): def collect_and_store_rules(self): """ Collect Sigma YAML rules from the destination directory and store/update - them as AdvisoryDetectionRule objects. + them as DetectionRule objects. """ base_directory = Path(self.vcs_response.dest_dir) - yaml_files = list(base_directory.rglob("**/*.yml")) - rules_count = len(yaml_files) + yaml_files = [ + p + for p in base_directory.rglob("**/*.yml") + if not any(part in [".github", "images", "documentation"] for part in p.parts) + ] + rules_count = len(yaml_files) self.log(f"Enhancing the vulnerability with {rules_count:,d} rule records") progress = LoopProgress(total_iterations=rules_count, logger=self.log) for file_path in progress.iter(yaml_files): - if any(part in [".github", "images", "documentation"] for part in file_path.parts): - continue - - with open(file_path, "r") as f: - try: - rule_data = f.read() - except YAMLError as err: - self.log(f"Invalid YAML in {file_path}: {err}. Skipping.") - continue + raw_text = file_path.read_text(encoding="utf-8") + rule_documents = list(yaml.load_all(raw_text, yaml.FullLoader)) + rule_metadata = extract_sigma_metadata(rule_documents) rule_url = f"https://raw.githubusercontent.com/SigmaHQ/sigma/refs/heads/master/{file_path.relative_to(base_directory)}" cve_ids = find_all_cve(str(file_path)) + found_advisories = set() for cve_id in cve_ids: try: @@ -69,29 +68,30 @@ def collect_and_store_rules(self): for adv in alias.advisories.all(): found_advisories.add(adv) except AdvisoryAlias.DoesNotExist: - self.log(f"Advisory {file_path.name} not found.") + self.log(f"AdvisoryAlias {cve_id}: {file_path.name} not found.") continue for adv in found_advisories: DetectionRule.objects.update_or_create( - rule_text=rule_data, + rule_text=raw_text, + rule_type=DetectionRuleTypes.SIGMA, advisory=adv, defaults={ - "rule_type": DetectionRuleTypes.SIGMA, + "rule_metadata": rule_metadata, "source_url": rule_url, }, ) if not found_advisories: DetectionRule.objects.update_or_create( - rule_text=rule_data, + rule_text=raw_text, + rule_type=DetectionRuleTypes.SIGMA, advisory=None, defaults={ - "rule_type": DetectionRuleTypes.SIGMA, + "rule_metadata": rule_metadata, "source_url": rule_url, }, ) - self.log(f"Successfully processed rules.") def clean_downloads(self): if self.vcs_response: @@ -100,3 +100,27 @@ def clean_downloads(self): def on_failure(self): self.clean_downloads() + + +def extract_sigma_metadata(rule_documents): + """ + Extract Sigma metadata from Sigma YAML rules + """ + if not rule_documents: + return None + + first_document = rule_documents[0] + metadata = { + "status": first_document.get("status"), + "author": first_document.get("author"), + "date": first_document.get("date"), + "title": first_document.get("title"), + "id": first_document.get("id"), + } + + rule_date = metadata.get("date") + + if isinstance(rule_date, (datetime.date, datetime.datetime)): + metadata["date"] = rule_date.isoformat() + + return metadata diff --git a/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py b/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py index 42abc00f9..5aec54733 100644 --- a/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py +++ b/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py @@ -70,3 +70,10 @@ def test_sigma_rules_db_improver(mock_fetch_via_vcs): assert len(DetectionRule.objects.all()) == 3 sigma_rule = DetectionRule.objects.first() assert sigma_rule.rule_type == "sigma" + assert sigma_rule.rule_metadata == { + "author": "Swachchhanda Shrawan Poudel (Nextron Systems)", + "date": "2025-06-13", + "id": "04fc4b22-91a6-495a-879d-0144fec5ec03", + "status": "experimental", + "title": "Potential Exploitation of RCE Vulnerability CVE-2025-33053 - Image " "Load", + } From 99f6341084cfcd6932f8497193c5b5d65eac9c1f Mon Sep 17 00:00:00 2001 From: ziad hany Date: Thu, 4 Dec 2025 02:15:11 +0200 Subject: [PATCH 4/4] Update migration file Add a simple test for rule_text, advisory_aliases Signed-off-by: ziad hany --- .../migrations/0104_advisorydetectionrule.py | 59 ------------------- ...detectionrule.py => 0104_detectionrule.py} | 7 +-- .../v2_improvers/test_sigma_rules.py | 5 ++ 3 files changed, 7 insertions(+), 64 deletions(-) delete mode 100644 vulnerabilities/migrations/0104_advisorydetectionrule.py rename vulnerabilities/migrations/{0105_detectionrule_delete_advisorydetectionrule.py => 0104_detectionrule.py} (91%) diff --git a/vulnerabilities/migrations/0104_advisorydetectionrule.py b/vulnerabilities/migrations/0104_advisorydetectionrule.py deleted file mode 100644 index 09f0757ab..000000000 --- a/vulnerabilities/migrations/0104_advisorydetectionrule.py +++ /dev/null @@ -1,59 +0,0 @@ -# Generated by Django 4.2.25 on 2025-11-24 19:30 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("vulnerabilities", "0103_codecommit_impactedpackage_affecting_commits_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="AdvisoryDetectionRule", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ( - "rule_text", - models.TextField( - help_text="Full text of the detection rule, script, or signature." - ), - ), - ( - "rule_type", - models.CharField( - blank=True, - choices=[ - ("yara", "YARA"), - ("sigma", "Sigma Detection Rule"), - ("clamav", "ClamAV Signature"), - ], - max_length=100, - ), - ), - ( - "source_url", - models.URLField( - blank=True, - help_text="URL or reference to the source of the rule (vendor feed, GitHub repo, etc.).", - null=True, - ), - ), - ( - "advisory", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="detection_rules", - to="vulnerabilities.advisoryv2", - ), - ), - ], - ), - ] diff --git a/vulnerabilities/migrations/0105_detectionrule_delete_advisorydetectionrule.py b/vulnerabilities/migrations/0104_detectionrule.py similarity index 91% rename from vulnerabilities/migrations/0105_detectionrule_delete_advisorydetectionrule.py rename to vulnerabilities/migrations/0104_detectionrule.py index cf1e96b32..d5887ce61 100644 --- a/vulnerabilities/migrations/0105_detectionrule_delete_advisorydetectionrule.py +++ b/vulnerabilities/migrations/0104_detectionrule.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.25 on 2025-12-03 02:30 +# Generated by Django 4.2.25 on 2025-12-04 00:14 from django.db import migrations, models import django.db.models.deletion @@ -7,7 +7,7 @@ class Migration(migrations.Migration): dependencies = [ - ("vulnerabilities", "0104_advisorydetectionrule"), + ("vulnerabilities", "0103_codecommit_impactedpackage_affecting_commits_and_more"), ] operations = [ @@ -65,7 +65,4 @@ class Migration(migrations.Migration): ), ], ), - migrations.DeleteModel( - name="AdvisoryDetectionRule", - ), ] diff --git a/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py b/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py index 5aec54733..9e7a6fc03 100644 --- a/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py +++ b/vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py @@ -77,3 +77,8 @@ def test_sigma_rules_db_improver(mock_fetch_via_vcs): "status": "experimental", "title": "Potential Exploitation of RCE Vulnerability CVE-2025-33053 - Image " "Load", } + assert sigma_rule.advisory == adv1 + assert ( + sigma_rule.rule_text + == "title: Potential Exploitation of RCE Vulnerability CVE-2025-33053 - Image Load\nid: 04fc4b22-91a6-495a-879d-0144fec5ec03\nrelated:\n - id: abe06362-a5b9-4371-8724-ebd00cd48a04\n type: similar\n - id: 9a2d8b3e-f5a1-4c68-9e21-7d9e1cf8a123\n type: similar\nstatus: experimental\ndescription: |\n Detects potential exploitation of remote code execution vulnerability CVE-2025-33053\n by monitoring suspicious image loads from WebDAV paths. The exploit involves malicious executables from\n attacker-controlled WebDAV servers loading the Windows system DLLs like gdi32.dll, netapi32.dll, etc.\nreferences:\n - https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2025-33053\n - https://research.checkpoint.com/2025/stealth-falcon-zero-day/\nauthor: Swachchhanda Shrawan Poudel (Nextron Systems)\ndate: 2025-06-13\ntags:\n - attack.command-and-control\n - attack.execution\n - attack.defense-evasion\n - attack.t1218\n - attack.lateral-movement\n - attack.t1105\n - detection.emerging-threats\n - cve.2025-33053\nlogsource:\n category: image_load\n product: windows\ndetection:\n selection_img_path:\n Image|startswith: '\\\\\\\\'\n Image|contains: '\\DavWWWRoot\\'\n selection_img_bin:\n Image|endswith:\n - '\\route.exe'\n - '\\netsh.exe'\n - '\\makecab.exe'\n - '\\dxdiag.exe'\n - '\\ipconfig.exe'\n - '\\explorer.exe'\n condition: all of selection_*\nfalsepositives:\n - Unknown\nlevel: high" + )