From 3d83e479ab354c3a31b6c961878059858c5f66b5 Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud Date: Thu, 27 Nov 2025 10:41:54 +0100 Subject: [PATCH 1/9] [New Rule] Execution via GitHub Runner with Audit Disabled via Environment Variables --- ...nner_with_audit_disabled_via_env_vars.toml | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml diff --git a/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml new file mode 100644 index 00000000000..00a8d6f39bd --- /dev/null +++ b/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml @@ -0,0 +1,134 @@ +[metadata] +creation_date = "2025/11/27" +integration = ["endpoint"] +maturity = "production" +updated_date = "2025/11/27" + +[rule] +author = ["Elastic"] +description = """ +This rule detects the execution of processes via GitHub Actions runners with audit logging disabled through environment variables. +Such activity may indicate an attempt to evade detection while executing potentially malicious code in a CI/CD environment. This +activity was observed in the Shai-Hulud worm, which abused GitHub Actions runners to propagate itself while disabling audit +logging by setting the RUNNER_TRACKING_ID environment variable to 0. +""" +from = "now-9m" +index = ["logs-endpoint.events.process*"] +language = "eql" +license = "Elastic License v2" +name = "Execution via GitHub Runner with Audit Disabled via Environment Variables" +references = [ + "https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise", + "https://socket.dev/blog/shai-hulud-strikes-again-v2", + "https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack", +] +risk_score = 73 +rule_id = "df0553c8-2296-45ef-b4dc-3b88c4c130a7" +setup = """## Setup + +This rule requires data coming in from Elastic Defend. + +### Elastic Defend Integration Setup +Elastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app. + +#### Prerequisite Requirements: +- Fleet is required for Elastic Defend. +- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html). + +#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System: +- Go to the Kibana home page and click "Add integrations". +- In the query bar, search for "Elastic Defend" and select the integration to see more details about it. +- Click "Add Elastic Defend". +- Configure the integration name and optionally add a description. +- Select the type of environment you want to protect, either "Traditional Endpoints" or "Cloud Workloads". +- Select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html). +- We suggest selecting "Complete EDR (Endpoint Detection and Response)" as a configuration setting, that provides "All events; all preventions" +- Enter a name for the agent policy in "New agent policy name". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead. +For more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html). +- Click "Save and Continue". +- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts. +For more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html). + +Elastic Defend integration does not collect environment variable logging by default. +In order to capture this behavior, this rule requires a specific configuration option set within the advanced settings of the Elastic Defend integration. + #### To set up environment variable capture for an Elastic Agent policy: +- Go to “Security → Manage → Policies”. +- Select an “Elastic Agent policy”. +- Click “Show advanced settings”. +- Scroll down or search for “linux.advanced.capture_env_vars”. +- Enter the names of environment variables you want to capture, separated by commas. +- For Linux, this rule requires the linux.advanced.capture_env_vars variable to be set to "RUNNER_TRACKING_ID". +- For macOS, this rule requires the macos.advanced.capture_env_vars variable to be set to "RUNNER_TRACKING_ID". +- Click “Save”. +After saving the integration change, the Elastic Agents running this policy will be updated and the rule will function properly. +For more information on capturing environment variables refer to the [helper guide](https://www.elastic.co/guide/en/security/current/environment-variable-capture.html). +""" +severity = "high" +tags = [ + "Domain: Endpoint", + "OS: Linux", + "OS: MacOS", + "Use Case: Threat Detection", + "Tactic: Execution", + "Tactic: Initial Access", + "Tactic: Defense Evasion", + "Use Case: Vulnerability", + "Data Source: Elastic Defend", + "Resources: Investigation Guide", +] +type = "eql" +query = ''' +process where host.os.type in ("linux", "macos") and event.type == "start" and event.action == "exec" and +process.parent.name in ("Runner.Worker", "Runner.Listener") and process.env_vars like~ "RUNNER_TRACKING_ID*" and +not process.env_vars like~ "RUNNER_TRACKING_ID=github_*" +''' + +[[rule.threat]] +framework = "MITRE ATT&CK" + + [rule.threat.tactic] + name = "Execution" + id = "TA0002" + reference = "https://attack.mitre.org/tactics/TA0002/" + + [[rule.threat.technique]] + id = "T1059" + name = "Command and Scripting Interpreter" + reference = "https://attack.mitre.org/techniques/T1059/" + +[[rule.threat]] +framework = "MITRE ATT&CK" + + [rule.threat.tactic] + name = "Initial Access" + id = "TA0001" + reference = "https://attack.mitre.org/tactics/TA0001/" + + [[rule.threat.technique]] + name = "Supply Chain Compromise" + id = "T1195" + reference = "https://attack.mitre.org/techniques/T1195/" + + [[rule.threat.technique.subtechnique]] + name = "Compromise Software Dependencies and Development Tools" + id = "T1195.001" + reference = "https://attack.mitre.org/techniques/T1195/001/" + + +[[rule.threat]] +framework = "MITRE ATT&CK" + + [rule.threat.tactic] + name = "Defense Evasion" + id = "TA0005" + reference = "https://attack.mitre.org/tactics/TA0005/" + + [[rule.threat.technique]] + name = "Impair Defenses" + id = "T1562" + reference = "https://attack.mitre.org/techniques/T1562/" + + [[rule.threat.technique.subtechnique]] + name = "Disable or Modify Tools" + id = "T1562.001" + reference = "https://attack.mitre.org/techniques/T1562/001/" From 7c15993f8f9c2e5cb9ca282d64909881bf5a1d97 Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud Date: Thu, 27 Nov 2025 10:55:02 +0100 Subject: [PATCH 2/9] [New Rule] Tampering with RUNNER_TRACKING_ID in GitHub Actions Runners --- ...a_github_runner_with_audit_disabled_via_env_vars.toml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml index 00a8d6f39bd..d0a2778266a 100644 --- a/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml +++ b/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml @@ -7,16 +7,15 @@ updated_date = "2025/11/27" [rule] author = ["Elastic"] description = """ -This rule detects the execution of processes via GitHub Actions runners with audit logging disabled through environment variables. -Such activity may indicate an attempt to evade detection while executing potentially malicious code in a CI/CD environment. This -activity was observed in the Shai-Hulud worm, which abused GitHub Actions runners to propagate itself while disabling audit -logging by setting the RUNNER_TRACKING_ID environment variable to 0. +This rule detects processes spawned by GitHub Actions runners where "RUNNER_TRACKING_ID" is overridden from its +default "github_*" value. Such tampering has been associated with attempts to evade runner tracking/cleanup on +self-hosted runners, including behavior observed in the Shai-Hulud 2.0 npm worm campaign. """ from = "now-9m" index = ["logs-endpoint.events.process*"] language = "eql" license = "Elastic License v2" -name = "Execution via GitHub Runner with Audit Disabled via Environment Variables" +name = "Tampering with RUNNER_TRACKING_ID in GitHub Actions Runners" references = [ "https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise", "https://socket.dev/blog/shai-hulud-strikes-again-v2", From 22d74f663c20a64cfb4e06b0fc7bb96e94a31dea Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud Date: Thu, 27 Nov 2025 10:56:11 +0100 Subject: [PATCH 3/9] ++ --- ...ution_via_github_runner_with_audit_disabled_via_env_vars.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml index d0a2778266a..df286599c77 100644 --- a/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml +++ b/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml @@ -20,6 +20,7 @@ references = [ "https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise", "https://socket.dev/blog/shai-hulud-strikes-again-v2", "https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack", + "https://www.praetorian.com/blog/self-hosted-github-runners-are-backdoors/", ] risk_score = 73 rule_id = "df0553c8-2296-45ef-b4dc-3b88c4c130a7" From 5994dfe09581cd42822e16901746f75e70cda8d6 Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud Date: Thu, 27 Nov 2025 10:58:24 +0100 Subject: [PATCH 4/9] ++ --- ...ub_runner_with_runner_tracking_id_tampering_via_env_vars.toml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename rules/cross-platform/{execution_via_github_runner_with_audit_disabled_via_env_vars.toml => execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml} (100%) diff --git a/rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml similarity index 100% rename from rules/cross-platform/execution_via_github_runner_with_audit_disabled_via_env_vars.toml rename to rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml From 49bf68a70366b9c1388079058ea253bc75f57268 Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud <78494512+Aegrah@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:59:49 +0100 Subject: [PATCH 5/9] Update execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml --- ...runner_with_runner_tracking_id_tampering_via_env_vars.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml index df286599c77..3e15ef3ffba 100644 --- a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml +++ b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml @@ -22,7 +22,7 @@ references = [ "https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack", "https://www.praetorian.com/blog/self-hosted-github-runners-are-backdoors/", ] -risk_score = 73 +risk_score = 47 rule_id = "df0553c8-2296-45ef-b4dc-3b88c4c130a7" setup = """## Setup @@ -63,7 +63,7 @@ In order to capture this behavior, this rule requires a specific configuration o After saving the integration change, the Elastic Agents running this policy will be updated and the rule will function properly. For more information on capturing environment variables refer to the [helper guide](https://www.elastic.co/guide/en/security/current/environment-variable-capture.html). """ -severity = "high" +severity = "medium" tags = [ "Domain: Endpoint", "OS: Linux", From 903feec5c52326498e1a20fe4daf3945c92b1025 Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud <78494512+Aegrah@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:00:15 +0100 Subject: [PATCH 6/9] Remove 'Use Case: Vulnerability' entry Removed 'Use Case: Vulnerability' from the list. --- ...ub_runner_with_runner_tracking_id_tampering_via_env_vars.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml index 3e15ef3ffba..836cc5d484d 100644 --- a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml +++ b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml @@ -72,7 +72,6 @@ tags = [ "Tactic: Execution", "Tactic: Initial Access", "Tactic: Defense Evasion", - "Use Case: Vulnerability", "Data Source: Elastic Defend", "Resources: Investigation Guide", ] From f356f1198100fe6aa6d797a4373fa59f0c35f313 Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud <78494512+Aegrah@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:05:28 +0100 Subject: [PATCH 7/9] Add timestamp override to GitHub runner execution rules --- ...ub_runner_with_runner_tracking_id_tampering_via_env_vars.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml index 836cc5d484d..79cb73dcde3 100644 --- a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml +++ b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml @@ -75,6 +75,7 @@ tags = [ "Data Source: Elastic Defend", "Resources: Investigation Guide", ] +timestamp_override = "event.ingested" type = "eql" query = ''' process where host.os.type in ("linux", "macos") and event.type == "start" and event.action == "exec" and From c6df8e1b825603c4abde07918932ca45ddb6904b Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud <78494512+Aegrah@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:13:32 +0100 Subject: [PATCH 8/9] Update rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml --- ...b_runner_with_runner_tracking_id_tampering_via_env_vars.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml index 79cb73dcde3..49504bfd003 100644 --- a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml +++ b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml @@ -67,7 +67,7 @@ severity = "medium" tags = [ "Domain: Endpoint", "OS: Linux", - "OS: MacOS", + "OS: macOS", "Use Case: Threat Detection", "Tactic: Execution", "Tactic: Initial Access", From dac78b73c0ee5a0c3b97c1b4991734a411127e7e Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud <78494512+Aegrah@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:14:41 +0100 Subject: [PATCH 9/9] Enhance guide for RUNNER_TRACKING_ID tampering Added detailed investigation guide for tampering with RUNNER_TRACKING_ID in GitHub Actions runners, including triage steps, false positive analysis, and remediation actions. --- ...er_tracking_id_tampering_via_env_vars.toml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml index 49504bfd003..ebd0cb49ce7 100644 --- a/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml +++ b/rules/cross-platform/execution_via_github_runner_with_runner_tracking_id_tampering_via_env_vars.toml @@ -16,6 +16,35 @@ index = ["logs-endpoint.events.process*"] language = "eql" license = "Elastic License v2" name = "Tampering with RUNNER_TRACKING_ID in GitHub Actions Runners" +note = """## Triage and analysis + +> **Disclaimer**: +> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs. + +### Investigating Tampering with RUNNER_TRACKING_ID in GitHub Actions Runners + +This rule surfaces processes launched by GitHub Actions runners where RUNNER_TRACKING_ID is deliberately set to a non-default value. Attackers do this to break runner job tracking and cleanup on self-hosted runners, enabling long‑lived or hidden workloads. A common pattern is a workflow step that exports a custom RUNNER_TRACKING_ID and then spawns bash or node to fetch and execute a script via curl|bash or npm install scripts, keeping the process alive after the job finishes to run mining or exfil tasks. + +### Possible investigation steps + +- Correlate the event to its GitHub Actions run/job and workflow YAML, identify the repository and actor (commit/PR), and verify whether RUNNER_TRACKING_ID was explicitly set in the workflow or injected by a step script. +- On the runner host, determine if the spawned process persisted beyond job completion by checking for orphaning or reparenting to PID 1, sustained CPU/memory usage, and timestamps relative to the runner process exit. +- Review nearby telemetry for fetch-and-execute patterns (curl|bash, wget, node/npm lifecycle scripts), unexpected file writes under /tmp or actions-runner/_work, and outbound connections to non-GitHub endpoints. +- Enumerate persistence artifacts created during the run, including crontab entries, systemd unit files, pm2 or nohup sessions, and changes to authorized_keys or rc.local, and tie them back to the suspicious process. +- Assess blast radius by listing secrets and tokens available to the job, checking audit logs for their subsequent use from the runner IP or unusual repositories, and decide whether to revoke or rotate credentials. + +### False positive analysis + +- A self-hosted runner bootstrap script or base image intentionally sets a fixed RUNNER_TRACKING_ID for internal log correlation or debugging, causing all runner-spawned processes to inherit a non-github_* value. +- A composite action or reusable workflow accidentally overrides RUNNER_TRACKING_ID through env mapping or variable expansion (for example templating it from the run ID), resulting in benign non-default values during standard jobs. + +### Response and remediation + +- Quarantine the self-hosted runner by stopping Runner.Listener, removing the runner from the repository/organization, and terminating any Runner.Worker children or orphaned processes (PID 1) that carry a non-default RUNNER_TRACKING_ID. +- Purge persistence by removing artifacts created during the run, including systemd unit files under /etc/systemd/system, crontab entries in /var/spool/cron, pm2/nohup sessions, edits to ~/.ssh/authorized_keys or /etc/rc.local, and files under /tmp and actions-runner/_work linked to the tampered process. +- Revoke and rotate credentials exposed to the job (GITHUB_TOKEN, personal access tokens, cloud keys), delete leftover containers and caches in actions-runner/_work, invalidate the runner registration, and redeploy the runner from a clean, patched image. +- Escalate to incident response if you observe outbound connections to non-GitHub endpoints, processes persisting after job completion, modifications to ~/.ssh/authorized_keys or /etc/systemd/system, or repeated RUNNER_TRACKING_ID tampering across runners or repositories. +- Harden by restricting self-hosted runners to trusted repositories and actors, enforcing ephemeral per-job runners with egress allowlisting to github.com, setting strict job timeouts, and adding a workflow guard step that exits if RUNNER_TRACKING_ID does not start with github_.""" references = [ "https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise", "https://socket.dev/blog/shai-hulud-strikes-again-v2",