T1677

Poisoned Pipeline Execution

Execution Last updated:

This detection identifies adversaries attempting to poison CI/CD pipelines through direct modification of CI configuration files, injection of malicious code into pipeline-referenced build artifacts, or exploitation of fork-based pull request workflows that expose pipeline secrets. Detections span three attack vectors: (1) Direct pipeline execution — changes to CI config files (e.g., .github/workflows, .gitlab-ci.yml, Jenkinsfile) containing suspicious commands such as credential exfiltration via curl/wget, base64-encoded payloads, or environment variable dumping; (2) Indirect pipeline execution — modifications to Makefiles, linters, test suites, or build scripts that are invoked by trusted CI configurations; (3) Public pipeline execution — fork-based pull requests targeting pull_request_target workflows or injecting malicious branch names that are processed as trusted inputs by pipeline steps. Detection coverage includes Azure DevOps audit logs, GitHub audit log events, and process telemetry from CI runner hosts.

What is T1677 Poisoned Pipeline Execution?

Poisoned Pipeline Execution (T1677) maps to the Execution tactic — the adversary is trying to run malicious code in MITRE ATT&CK.

This page provides production-ready detection logic for Poisoned Pipeline Execution, covering the data sources and telemetry it touches: Microsoft Sentinel, Azure DevOps Auditing, GitHub Advanced Security. The queries below are rated high severity at medium confidence, and ship for 7 SIEM platforms — KQL, SPL, Elastic, QRadar, Sumo, YARA-L, LogScale.

MITRE ATT&CK

Tactic
Execution
Technique
T1677 Poisoned Pipeline Execution
Canonical reference
https://attack.mitre.org/techniques/T1677/
Microsoft Sentinel / Defender
kusto
let CIConfigFiles = dynamic([".github/workflows", ".gitlab-ci.yml", "Jenkinsfile", ".circleci/config", "azure-pipelines.yml", "bitbucket-pipelines.yml", ".travis.yml", "cloudbuild.yaml"]);
let SuspiciousTerms = dynamic(["curl ", "wget ", "base64 -d", "base64 --decode", "printenv", "env |", "| base64", "exfil", "ngrok", "burpcollab", "interactsh", "webhook.site", "requestbin", "pipedream", "SECRET", "TOKEN", "API_KEY", "AWS_", "GITHUB_TOKEN", "CI_JOB_TOKEN"]);
union isfuzzy=true
(
    AzureDevOpsAuditing
    | where TimeGenerated > ago(24h)
    | where OperationName in (
        "Git.Push",
        "Git.RefUpdateBatch",
        "Pipeline.PipelineModified",
        "Build.DefinitionModified",
        "Build.DefinitionCreated"
    )
    | extend DataStr = tostring(Data)
    | where DataStr has_any (CIConfigFiles) or DataStr has_any (SuspiciousTerms)
    | extend
        Actor = ActorUPN,
        Platform = "AzureDevOps",
        OperationType = OperationName,
        SourceIP = IpAddress,
        ProjectContext = ProjectName
    | project TimeGenerated, Actor, Platform, OperationType, SourceIP, ProjectContext, DataStr
),
(
    GitHubAuditLog
    | where TimeGenerated > ago(24h)
    | where Action in (
        "workflows.created",
        "workflows.updated",
        "git.push",
        "protected_branch.update_allow_force_pushes",
        "repo.create"
    )
    | extend DataStr = tostring(Data)
    | where DataStr has_any (CIConfigFiles) or DataStr has_any (SuspiciousTerms)
    | extend
        Actor = Actor,
        Platform = "GitHub",
        OperationType = Action,
        SourceIP = coalesce(IPAddress, "Unknown"),
        ProjectContext = tostring(Data.repo)
    | project TimeGenerated, Actor, Platform, OperationType, SourceIP, ProjectContext, DataStr
)
| extend RiskScore = case(
    DataStr has_any ("base64 -d", "base64 --decode", "interactsh", "ngrok", "webhook.site", "burpcollab"), 95,
    DataStr has_any ("printenv", "env |", "AWS_SECRET", "GITHUB_TOKEN", "CI_JOB_TOKEN"), 85,
    DataStr has_any ("curl ", "wget ", "| base64", "SECRET", "API_KEY"), 70,
    50
)
| where RiskScore >= 70
| sort by RiskScore desc, TimeGenerated desc

Detects suspicious modifications to CI/CD configuration files in Azure DevOps and GitHub environments. Queries AzureDevOpsAuditing and GitHubAuditLog tables for push or pipeline definition change events that reference known CI configuration file paths combined with suspicious command patterns indicating credential exfiltration (curl/wget with env vars, base64 decoding, exfiltration infrastructure hostnames). Risk-scores results based on observed command patterns to prioritize highest-confidence pipeline poisoning attempts.

high severity medium confidence

Data Sources

Microsoft Sentinel Azure DevOps Auditing GitHub Advanced Security

Required Tables

AzureDevOpsAuditing GitHubAuditLog

False Positives

  • Legitimate DevOps engineers updating pipeline definitions to add new build steps or integrations — validate against change management tickets
  • Authorized security scanning tools (Snyk, Dependabot, GitHub Advanced Security) modifying workflow files during automated PR creation
  • Infrastructure-as-code pipelines that legitimately use curl/wget to download build dependencies or SDKs from trusted artifact registries
  • Developers experimenting with pipeline debugging steps that temporarily echo environment context — common during onboarding
  • Automated dependency update bots (Renovate, Dependabot) modifying workflow files or build scripts as part of their normal operation

Sigma rule & cross-platform mapping

The detection logic for Poisoned Pipeline Execution (T1677) above is provided in a vendor-neutral form so you can deploy it on any SIEM. The same logic is shipped here as native KQL (Microsoft Sentinel / Defender), SPL (Splunk), Elastic (Elastic Security (EQL)), QRadar (IBM QRadar (AQL)), Sumo (Sumo Logic CSE), YARA-L (Google Chronicle / SecOps), LogScale (CrowdStrike LogScale (CQL)) queries. In Sigma terms, this detection targets the following logsource:

logsource:
  product: azure

Browse the community-maintained Sigma rules for this technique:


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Direct CI Config Poisoning - GitHub Actions Credential Exfiltration Simulation

    Expected signal: GitHubAuditLog: Action=git.push with repository_file matching .github/workflows path and DataStr containing 'curl' and 'TOKEN'. AzureDevOpsAuditing: OperationName=Git.Push with Data containing workflow file path and suspicious curl command.

  2. Test 2Indirect Pipeline Poisoning - Malicious npm postinstall Script Injection

    Expected signal: DeviceProcessEvents: InitiatingProcessFileName=npm spawning sh/bash with ProcessCommandLine containing 'printenv'. Sysmon Event ID 1: ParentImage=npm, CommandLine matching printenv/grep pattern. linux_secure or auditd: execve syscall for printenv spawned under npm process.

  3. Test 3Public Pipeline Execution - Fork PR with Malicious Branch Name Injection

    Expected signal: GitHubAuditLog: Action=pull_request.opened with pull_request.head.repo.full_name != pull_request.base.repo.full_name (fork indicator). Branch name field containing shell metacharacters triggers IsSuspiciousBranch=true in hunting query.

Unlock Pro Content

Get the full detection package for T1677 including response playbook, investigation guide, and atomic red team tests.

Response PlaybookInvestigation GuideHunting QueriesAtomic Red Team TestsTuning Guidance

Related Detections

Tactic Hub