The Credentials That Live Outside AWS Are the Ones That Get Stolen

blast-radiussupply-chaincredential-accesslateral-movement

When TeamPCP wanted AWS credentials, they didn't attack AWS. They didn't brute-force IAM users, exploit a misconfigured S3 bucket, or chain an SSRF to the instance metadata service. They compromised a security scanner's CI pipeline and waited for the credentials to come to them.

In 8 days (March 19-27, 2026), the campaign cascaded from a single misconfigured GitHub Actions workflow into Trivy, Checkmarx, LiteLLM, Telnyx, and dozens of npm packages. Wiz data shows LiteLLM alone is present in 36% of cloud environments. The malware harvested AWS credentials, Kubernetes tokens, SSH keys, and database passwords from every host that installed a poisoned package.

The question isn't how they got in. It's where your AWS credentials were sitting when they did.

The credential chain

Most coverage of the TeamPCP campaign focuses on the vulnerability chain: how a misconfigured workflow in Trivy's CI exposed a privileged personal access token, which led to the compromise of downstream projects. That story matters, but it obscures the more important one. This was a campaign built entirely around harvesting credentials from the places where credentials rest.

Here's the chain as a credential flow, not a vulnerability story:

Trivy GitHub Actions workflow (misconfigured, exposed privileged PAT)
  -> GitHub Actions runner secrets exposed
    -> PyPI PYPI_PUBLISH token for LiteLLM stolen
      -> Malicious litellm 1.82.7 / 1.82.8 published to PyPI
        -> pip install on:
           +-- Developer laptops    -> ~/.aws/credentials, env vars, SSH keys
           +-- CI/CD runners        -> AWS_ACCESS_KEY_ID, deploy role credentials
           +-- Docker builds        -> build-time secrets, ECR tokens
           +-- Production servers   -> IAM role via IMDS, k8s service accounts

Every box in that chain is a foothold: a starting identity with real AWS permissions. The attacker doesn't care which one they land on. They care what it can reach.

What the malware actually harvested

The payload in the compromised LiteLLM packages wasn't a targeted exploit of LiteLLM's proxy functionality. It was a comprehensive host credential sweeper that targeted every credential store on the system:

  • AWS credential files: ~/.aws/credentials and ~/.aws/config
  • Environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
  • SSH keys: ~/.ssh/
  • Kubernetes configs: ~/.kube/config and service account tokens
  • CI/CD secrets: Jenkins, GitHub Actions, and other pipeline credentials
  • Shell history: containing account IDs, role ARNs, and resource names
  • Docker configs: including ECR authentication tokens
  • Database credentials: connection strings and password files

All collected data was encrypted with AES-256 (session key wrapped with RSA-4096) and exfiltrated to attacker-controlled infrastructure. Network-level DLP couldn't inspect what was stolen.

Version 1.82.8 was particularly dangerous. It included a malicious litellm_init.pth file that abuses Python's .pth mechanism to execute code on every Python interpreter startup, not just when importing LiteLLM. If 1.82.8 was installed in a virtual environment or system Python, the credential harvester ran every time python, pip, or any Python-based tool was invoked. You didn't need to import LiteLLM. You didn't need to run the proxy. Just having the package installed was enough.

Once credentials were harvested, the malware deployed privileged Kubernetes pods named node-setup-* to spread across cluster nodes, installed a systemd persistence mechanism at ~/.config/sysmon/sysmon.py, and polled a C2 endpoint for additional payloads.

The two footholds nobody assesses

Security assessments typically start from an IAM role or user and ask "what can this identity reach?" That's the right question, but it misses the places where credentials rest before they're used. The TeamPCP campaign targeted exactly those places.

Developer workstations

A developer laptop running pip install litellm during the three-hour window the malicious packages were live on PyPI would have exposed:

  • Multiple AWS profiles. ~/.aws/credentials often contains profiles for development, staging, and production accounts. A single compromised workstation can yield footholds into multiple AWS accounts simultaneously.
  • SSO session tokens. If the developer recently ran aws sso login, active session tokens are cached locally and grant access until they expire.
  • Shell history as recon. Commands like aws sts assume-role --role-arn arn:aws:iam::123456789012:role/deploy in .bash_history reveal account IDs, role ARNs, and the trust relationships between them. This is free reconnaissance that maps the paths an attacker would explore from the stolen credentials.
  • Git configurations. Remote URLs point to repositories that contain Terraform state, CDK stacks, and CloudFormation templates, telling the attacker exactly what infrastructure exists and how it's configured.

The blast radius from a developer credential depends on what that developer can access. In many organizations, developers can assume roles across multiple accounts, read from shared secret stores, and deploy to pre-production environments that mirror production's IAM structure.

CI/CD pipelines

The TeamPCP campaign started in a CI pipeline (Trivy's GitHub Actions) and used CI pipelines to propagate (stealing LiteLLM's PyPI publish token from its CI runner). This isn't a coincidence. CI/CD environments are where the most powerful credentials accumulate.

A typical CI/CD runner holds:

  • Deploy role credentials. Whether via OIDC federation or (worse) long-lived access keys, CI runners need permissions to create, update, and delete infrastructure. These are often the most privileged identities in an AWS account.
  • Secret store access. The pipeline needs to inject database passwords, API keys, and service credentials into builds. That means it has read access to Secrets Manager or SSM Parameter Store, possibly across multiple accounts.
  • Cross-service tokens. Docker Hub credentials, PyPI tokens, npm tokens, Slack webhooks, Datadog API keys. Each one extends the blast radius beyond AWS into other systems.
  • Infrastructure-as-code context. The CI runner has the full source tree, including CDK/Terraform files that describe every resource, permission, and trust relationship in the environment. Combined with deploy permissions, this is everything an attacker needs to understand and modify the infrastructure.

The GitGuardian analysis of the Trivy compromise makes this point directly: the initial compromise exposed a privileged personal access token from a CI workflow, which the attacker used to publish malicious Trivy binaries and compromise downstream release artifacts.

Blast radius from each foothold

To make this concrete, consider what an attacker can reach from each credential type:

From a developer workstation credential

~/.aws/credentials (developer profile)
  -> sts:AssumeRole into development account
    -> Secrets Manager: database connection strings
      -> RDS: customer data in dev (often a copy of production)
    -> sts:AssumeRole into staging account (if trust exists)
      -> Same services, closer to production data
  -> S3: Terraform state files (contain every resource ARN and config)
  -> IAM: enumerate roles, discover trust chains to production

The blast radius is every account the developer can reach, every secret those accounts expose, and every resource those secrets unlock. In organizations where developers can assume roles in production (even read-only), the chain extends to production data.

From a CI/CD deploy credential

CI deploy role (often has broad CDK/CloudFormation permissions)
  -> CloudFormation: create/update any stack
    -> IAM: create roles, attach policies (privilege escalation)
    -> Lambda: deploy code into VPC (network access to internal services)
    -> S3: modify deployment artifacts (supply chain within the org)
  -> Secrets Manager: read deploy-time secrets
    -> Database passwords, API keys, third-party credentials
  -> ECR: push container images
    -> Inject malicious code into production containers
  -> sts:AssumeRole into other accounts (if cross-account deploy exists)
    -> Repeat the above in every account the pipeline deploys to

The CI/CD credential typically has a larger blast radius than any individual developer or production service. It's the identity that builds and deploys everything. If it can modify CloudFormation stacks, it can create new IAM roles with arbitrary permissions. If it can push to ECR, it can inject code into production containers. The transitive reach is the entire deployment surface.

What the TeamPCP campaign teaches about foothold selection

TeamPCP's approach was efficient. They didn't look for an exploitable web application or a misconfigured S3 bucket. They went to where credentials accumulate by design: CI/CD pipelines and developer environments. The entire campaign, from Trivy through LiteLLM to Telnyx, was a credential harvesting operation that used supply chain compromise as the delivery mechanism.

This maps directly to how real attackers choose footholds. The SANS analysis of the campaign highlights a painful irony: the most diligent organizations, the ones running security scanners in every build, had the greatest exposure. By targeting security and developer tooling specifically, TeamPCP ensured their malware ran in the environments with the richest credential access. A security scanner running in CI has access to CI secrets. A Python package installed on a developer laptop has access to the developer's credential files.

The question for your environment isn't "were we affected by the LiteLLM compromise?" (check the IOCs if you haven't). The question is: what's the blast radius from the credentials that live in your CI/CD pipeline and on your developers' machines?

Reducing the blast radius

For CI/CD credentials

  • Use OIDC federation, not long-lived keys. The TeamPCP campaign stole PyPI tokens from CI runners. If your AWS credentials in CI are long-lived access keys, they're equally stealable. OIDC scopes the credential to a specific repo, branch, and workflow.
  • Scope deploy roles to specific resources. A deploy role that can cloudformation:* on * has unlimited blast radius. Scope it to the specific stacks it needs to manage. Separate infrastructure deployment from application deployment.
  • Audit what your CI runner can reach. Assume the CI runner is compromised. What AWS accounts can it access? What secrets can it read? What infrastructure can it modify? That's your blast radius.

For developer workstations

  • Don't store long-lived credentials in ~/.aws/credentials. Use aws sso login with short-lived sessions, or a credential manager like aws-vault that doesn't write plaintext credentials to disk.
  • Separate account access by risk tier. A developer who needs to deploy to dev should not have credentials that can reach production. Use separate SSO permission sets with different trust boundaries.
  • Pin dependencies and verify checksums. The .pth mechanism in LiteLLM 1.82.8 means pip install is code execution. Use lockfiles (pip freeze, poetry.lock) and verify hashes. The three-hour window was short, but it only takes one pip install --upgrade to pull a compromised version.

For both

  • Map the blast radius from each credential. Start from the identity your CI pipeline or developer uses, and trace what it can reach. Follow the AssumeRole chains, the secret store access, and the infrastructure modification paths. That's what an attacker would do with the stolen credentials, and it's exactly what our assessment agent does.

Key takeaways

  • TeamPCP didn't attack AWS. They attacked the places where AWS credentials rest. CI/CD pipelines and developer workstations were the targets, not because they're vulnerable, but because they're where credentials accumulate by design.

  • A single misconfigured GitHub Actions workflow cascaded into credential theft across 36% of cloud environments. The Wiz data on LiteLLM's prevalence shows how supply chain compromise translates directly to cloud credential exposure at scale.

  • Developer workstations and CI/CD runners are footholds with real AWS permissions, but they're rarely included in security assessments. If you only assess "what can this IAM role do?" you miss the question "who has credentials for this role, and what else can they reach?"

  • The blast radius from a CI/CD deploy credential is often larger than from any production service identity. It's the identity that creates and modifies infrastructure. Compromising it means compromising everything it deploys.

  • Pinning dependencies and using OIDC federation aren't just hygiene. They're blast radius reduction. Every long-lived credential eliminated and every dependency pinned reduces the damage a supply chain compromise can cause.

  • The question isn't "were we affected by LiteLLM?" It's "what could someone do with the credentials that live in our build pipeline?" Check the IOCs, then map your blast radius.


Ready to map the blast radius from your CI/CD credentials? Sign up for hackaws.cloud and let our autonomous agent trace every lateral movement and privilege escalation path from the identity an attacker actually lands on.