Detection rules › Panther

GitHub Supply Chain - Software Installation Tool User Agents

Severity
medium
Group by
actor, user_agent
Log types
GitHub.Audit
Tags
Supply Chain, Installation Tools, Package Managers
Reference
https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/reviewing-the-audit-log-for-your-organization
Source
github.com/panther-labs/panther-analysis

Detects software installation tool user agents in GitHub audit logs that should never directly access GitHub. Package managers like npm, pip, yarn, and system installers operate at the registry level, not GitHub audit level. Their presence indicates: 1. Supply chain attacks using spoofed user agents to blend in 2. Compromised systems running installation tools with stolen GitHub tokens 3. Malicious automation disguised as legitimate package managers Based on analysis of GitHub audit logs showing zero legitimate npm/yarn/pip user agents, any such patterns are inherently suspicious and warrant immediate investigation.

MITRE ATT&CK coverage

Rule body yaml

AnalysisType: rule
Filename: github_supply_chain_suspicious_user_agents.py
RuleID: "GitHub.SupplyChain.SuspiciousUserAgents"
DisplayName: "GitHub Supply Chain - Software Installation Tool User Agents"
Enabled: true
LogTypes:
  - GitHub.Audit
Tags:
  - Supply Chain
  - Installation Tools
  - Package Managers
Reports:
  MITRE ATT&CK:
    - TA0001:T1195.002  # Supply Chain Compromise: Compromise Software Supply Chain
Severity: Medium
Description: >
  Detects software installation tool user agents in GitHub audit logs that should never 
  directly access GitHub. Package managers like npm, pip, yarn, and system installers 
  operate at the registry level, not GitHub audit level. Their presence indicates:
  1. Supply chain attacks using spoofed user agents to blend in
  2. Compromised systems running installation tools with stolen GitHub tokens  
  3. Malicious automation disguised as legitimate package managers
  
  Based on analysis of GitHub audit logs showing zero legitimate npm/yarn/pip user agents,
  any such patterns are inherently suspicious and warrant immediate investigation.
Runbook: |
  1. Verify the actor and IP address associated with the activity
  2. Check if the GitHub token/credentials used have been compromised
  3. Review all actions performed by this user agent for malicious activity
  4. Investigate if this represents a supply chain attack or credential theft
  5. Consider revoking affected tokens and resetting credentials
  6. Review repository access and recent changes for signs of compromise
Reference: https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/reviewing-the-audit-log-for-your-organization
Tests:
  - Name: "NPM User Agent - Malicious Repository Modification"
    ExpectedResult: true
    Log:
      action: "repo.update"
      user_agent: "npm/10.2.4 node/v18.19.0 linux x64 workspaces/false"
      actor: "malicious-actor"
      repo: "organization/sensitive-repo"
      programmatic_access_type: "personal_access_token"

  - Name: "Yarn User Agent - Suspicious Package Installation"
    ExpectedResult: true
    Log:
      action: "repo.create"
      user_agent: "yarn/3.6.4-core npm/? node/v20.10.0 darwin arm64"
      actor: "suspicious-user"
      repo: "organization/new-malicious-repo"
      programmatic_access_type: "github_app"

  - Name: "Python Pip User Agent - Repository Access"
    ExpectedResult: true
    Log:
      action: "repo.access"
      user_agent: "pip/24.0 {\"ci\":null,\"cpu\":\"aarch64\",\"distro\":{\"name\":\"Alpine Linux\"}}"
      actor: "automated-bot"
      repo: "organization/python-project"
      programmatic_access_type: "personal_access_token"

  - Name: "Ruby Gem User Agent - File Modification"
    ExpectedResult: true
    Log:
      action: "git.push"
      user_agent: "Ruby, RubyGems/3.4.22 linux-x86_64 Ruby/3.1.4 (2023-03-30 patchlevel 223)"
      actor: "gem-installer"
      repo: "organization/ruby-app"
      programmatic_access_type: "deploy_key"

  - Name: "Rust Cargo User Agent - Repository Clone"
    ExpectedResult: true
    Log:
      action: "repo.destroy"
      user_agent: "cargo/1.75.0"
      actor: "cargo-user"
      repo: "organization/rust-project"
      programmatic_access_type: "personal_access_token"

  - Name: "Legitimate Git Clone Action - Should Not Alert"
    ExpectedResult: false
    Log:
      action: "git.clone"
      user_agent: "npm/10.2.4 node/v18.19.0 linux x64 workspaces/false"
      actor: "developer"
      repo: "organization/project"
      programmatic_access_type: "personal_access_token"

  - Name: "Legitimate Git Fetch Action - Should Not Alert"
    ExpectedResult: false
    Log:
      action: "git.fetch"
      user_agent: "yarn/3.6.4-core npm/? node/v20.10.0 darwin arm64"
      actor: "developer"
      repo: "organization/project"
      programmatic_access_type: "personal_access_token"

  - Name: "Normal Web Browser User Agent - Should Not Alert"
    ExpectedResult: false
    Log:
      action: "repo.update"
      user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
      actor: "developer"
      repo: "organization/project"
      programmatic_access_type: "oauth_token"

  - Name: "GitHub CLI User Agent - Should Not Alert"
    ExpectedResult: false
    Log:
      action: "repo.create"
      user_agent: "GitHub CLI 2.40.1"
      actor: "developer"
      repo: "organization/new-project"
      programmatic_access_type: "personal_access_token"

  - Name: "Git Command Line User Agent - Should Not Alert"
    ExpectedResult: false
    Log:
      action: "git.push"
      user_agent: "git/2.39.3"
      actor: "developer"
      repo: "organization/project"
      programmatic_access_type: "deploy_key"

  - Name: "Empty User Agent - Should Not Alert"
    ExpectedResult: false
    Log:
      action: "repo.update"
      user_agent: ""
      actor: "developer"
      repo: "organization/project"
      programmatic_access_type: "personal_access_token"

  - Name: "Short User Agent - Should Not Alert"
    ExpectedResult: false
    Log:
      action: "repo.update"
      user_agent: "ab"
      actor: "developer"
      repo: "organization/project"
      programmatic_access_type: "personal_access_token"

  - Name: "Missing User Agent Field - Should Not Alert"
    ExpectedResult: false
    Log:
      action: "repo.update"
      actor: "developer"
      repo: "organization/project"
      programmatic_access_type: "personal_access_token"

  - Name: "NPM Pattern with CI - Malicious Action"
    ExpectedResult: true
    Log:
      action: "org.update_member"
      user_agent: "npm/9.8.1 node/v18.17.1 linux x64 workspaces/true ci/github-actions"
      actor: "malicious-ci"
      repo: "organization/project"
      programmatic_access_type: "github_app"

  - Name: "Yarn Pattern Variation - Different Version Format"
    ExpectedResult: true
    Log:
      action: "repo.transfer"
      user_agent: "yarn/4.0.2 npm/? node/v21.2.0 win32 x64"
      actor: "suspicious-transfer"
      repo: "organization/valuable-repo"
      programmatic_access_type: "personal_access_token"

  - Name: "Pip Pattern with Complex JSON - Repository Deletion"
    ExpectedResult: true
    Log:
      action: "repo.destroy"
      user_agent: "pip/23.3.1 {\"implementation\":{\"name\":\"cpython\",\"version\":\"3.11.6\"},\"system\":{\"name\":\"linux\"}}"
      actor: "malicious-pip"
      repo: "organization/critical-infrastructure"
      programmatic_access_type: "personal_access_token"

  - Name: "Case Insensitive Pattern Matching - Uppercase NPM"
    ExpectedResult: true
    Log:
      action: "repo.update"
      user_agent: "NPM/10.2.4 NODE/V18.19.0 LINUX X64 WORKSPACES/FALSE"
      actor: "case-test-actor"
      repo: "organization/test-repo"
      programmatic_access_type: "personal_access_token"

Detection logic

Condition

action not in ["git.clone", "git.fetch", "git.pull", "git.checkout", "git.archive", "repo.download"]
not (user_agent is_null or user_agent length_compare "3")

This rule also runs imperative logic the parser cannot express as a filter; the conditions above are the structured part it could extract.

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

FieldKindExcluded values
user_agentis_null(no value, null check)
user_agentlength_compare3
actioningit.archive, git.checkout, git.clone, git.fetch, git.pull, repo.download

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
action
actor
actor_locationactor_location.country_code
org
repo
user