pipebreach logo pipebreach.com
General Analysis

The AI Gateway Attack Surface: What TeamPCP's First Move Actually Opened

PyPI python supply-chain-compromise Analysis only
May 31, 2026 · 18 min read
AI Gateway as man-in-the-middle: the proxy position between application and LLM providers

Context. This post picks up from where Part II of the TeamPCP series ends. It does not repeat the forensic reconstruction of the LiteLLM compromise, already documented in full. What Part II closes with is a claim: “The supply chain is the security perimeter for AI systems. TeamPCP just demonstrated what the first move looks like.” This post is the analysis that claim deserves.


Author's Note

TeamPCP's payload treated LiteLLM like any other package: harvest credentials, exfiltrate, persist, done. That is the expected behavior of an automated credential stealer operating at scale. What I want to examine here is what that same proxy position enables for an attacker who is not in a hurry. The three surfaces described in this post are not hypothetical extensions of TeamPCP's campaign. They are structural properties of how AI gateways work, which the incident made impossible to ignore. The relevant question for any team deploying AI infrastructure in production is not whether these surfaces exist (they do), but whether they are inside or outside your current threat model.


TL;DR

  • An AI Gateway occupies a fundamentally different position than a standard API proxy: it sees the full content of every AI interaction in plaintext (prompts, system instructions, RAG documents, tool call parameters, and model responses), not just request metadata.
  • TeamPCP exploited Surface 1 (credential concentration) and exited in 46 minutes. Surfaces 2 and 3 (persistent prompt interception and response manipulation) require no additional payload and leave no memory artifact.
  • Current AI security tooling (prompt injection defenses, OWASP LLM Top 10 controls, model-level guardrails) assumes the channel between application and model is clean. The LiteLLM compromise demonstrated that assumption is not guaranteed.
  • Any production system running AI agents against sensitive data or consequential actions exposes that data at the proxy layer. The confidentiality and integrity requirements for that channel are not currently formalized in any AI security framework.
  • Five structural controls that address this at the infrastructure level; none of them are in the standard deployment checklist for any AI gateway today.

What makes an AI Gateway different

A conventional API gateway handles transport. An AI Gateway handles semantics, and that distinction is the entire threat model.

flowchart LR subgraph APP["Application Layer"] A1["Web App"] A2["API Service"] A3["AI Agent Framework"] end subgraph GW["AI Gateway (LiteLLM or equivalent)"] G["every interaction, both directions, plaintext"] end subgraph LLM["LLM Provider Layer"] P1["OpenAI"] P2["Anthropic"] P3["Azure OpenAI"] P4["Bedrock"] end APP -->|system prompts, user messages, RAG context, tool calls| GW GW -->|completions, tool results, reasoning chains| LLM classDef danger stroke:#f85149,color:#f85149,stroke-width:2px; class G danger;

Figure 1. The Proxy Position. An AI Gateway sits at the intersection of every AI interaction in the organization. Unlike a transport-layer proxy, it processes semantically rich content on both sides: application-side system prompts and user inputs, and model-side responses, reasoning chains, and tool call results. Every interaction passes through this single process in plaintext.

What an API gateway sees per request:
  - Method, path, headers, response code, latency

What an AI gateway sees per request:
  - system_prompt: "You are an ops agent. Internal runbook: [proprietary procedures]"
  - user_message: "Scale down the payment service in prod-us-east"
  - retrieved_context: [internal runbook docs, service dependency graph from RAG]
  - tool_call: { name: "kubectl_apply", args: { manifest: "...", namespace: "prod" } }
  - model_response: "Scaling down payment-service to 0 replicas as requested..."
  - tool_result: { status: "applied", affected_pods: [...] }

The three attack surfaces

TeamPCP’s campaign touched Surface 1 and exited. Surfaces 2 and 3 were never activated. That is not evidence that they are less dangerous; it is evidence that TeamPCP was running an automated credential harvester at scale, not a targeted intelligence collection operation.

flowchart TD GW["Compromised AI Gateway"] S1["Surface 1: Credential Concentration. EXPLOITED, 46min window"] S2["Surface 2: Prompt and Response Interception. Silent, no payload in memory"] S3["Surface 3: Response Manipulation. In-transit modification"] GW ==>|exploited by TeamPCP| S1 GW -->|latent| S2 GW -->|latent| S3 classDef crit stroke:#f85149,color:#f85149,stroke-width:3px; classDef high stroke:#e3b341,color:#e3b341; class S1 crit; class S2,S3 high;

Figure 2. Three Surfaces, One Proxy Position. The same supply chain compromise unlocks three distinct attack surfaces depending on attacker intent. Surface 1 was what TeamPCP activated. Surfaces 2 and 3 require no additional payload: only the proxy position itself and the decision not to leave.

Surface 1: Credential concentration

The analysis is in Part II. The framing worth adding here is that an AI Gateway is, by design, the highest density secrets store in the stack. No current deployment checklist formalizes what a PAM equivalent looks like for it. The keys live in environment variables, passed at container startup, co-located with the runtime in the same process boundary, and a single compromised dependency reaches all of them simultaneously.

Concretely, the standard deployment puts every provider credential one os.environ read away from any code running in the gateway process. There is no privilege boundary between the proxy logic and the secrets it proxies:

# What the gateway's own config does at startup, and therefore
# what any imported dependency can do at import time:
import os

OPENAI_API_KEY    = os.environ["OPENAI_API_KEY"]
ANTHROPIC_API_KEY = os.environ["ANTHROPIC_API_KEY"]
AZURE_API_KEY     = os.environ["AZURE_OPENAI_API_KEY"]
AWS_BEDROCK_CREDS = os.environ["AWS_SECRET_ACCESS_KEY"]
# ...plus k8s service account tokens mounted into the same pod

# A malicious transitive dependency runs in this exact process.
# It does not need to exploit anything. It reads the environment.

This is what made the 46 minute window so productive. The payload did not have to escalate or pivot to reach the keys. They were already in the address space it was executing in. Surface 1 is not a vulnerability in the gateway, it is the deployment topology that every gateway ships with by default.

Surface 2: Prompt and response interception

Surface 2 requires no active payload after the initial compromise. A backdoored proxy that silently logs requests and responses to an attacker-controlled endpoint leaves nothing unusual in process memory, generates no anomalous subprocess activity, and produces no detectable filesystem artifacts beyond the initial compromise mechanism (which was already removed from PyPI within 46 minutes).

What it produces, indefinitely and silently, is a complete record of every AI interaction in the organization. In any production deployment running agents against internal systems, that record includes:

System prompts. The instructions that define how AI systems behave. These encode proprietary business logic, internal procedures, and operational rules that organizations treat as IP. They are passed in plaintext to the model on every request, rarely rotated, and never designed to transit a potentially compromised process.

RAG context. Documents retrieved from internal knowledge bases and injected into the model’s context. Whatever lives in the vector store (internal docs, user data, operational records) becomes readable at the proxy layer on every retrieval. The vector database entry point is already a discussed attack surface, and the proxy sits upstream of all of it.

Tool call parameters and results. In agentic systems, the model invokes tools to query a database, read a file, call an internal API, or execute a command. Both the invocation, with its parameters, and the result, with its data, pass through the proxy. Monitoring this layer is equivalent to monitoring every action the agent takes, plus the data those actions return.

Model responses. This includes reasoning chains from models that expose them (o1, DeepSeek-R1, Gemini 2.0 Flash Thinking). These are not just completions. They are the model’s interpretation of the proprietary context it was given.

Surface 3: Response manipulation

Surface 3 is the least discussed and the most consequential for agentic systems.

A proxy positioned between application and model can modify responses in transit, after the model generates them and before the application receives them. This requires no model-level access, no prompt injection, and no interaction with any of the security controls currently deployed for AI systems.

flowchart TD subgraph NORMAL["Normal flow"] NA["Agent"] --> NG["AI Gateway (clean)"] NG --> NP["LLM Provider"] NP --> NG2["response replicas=2, forwarded unchanged"] NG2 --> NX["agent runs kubectl_apply replicas=2, OK"] end subgraph COMP["Compromised flow"] CA["Agent"] --> CG["AI Gateway (compromised)"] CG --> CP["LLM Provider"] CP --> CM["proxy rewrites replicas=2 to replicas=0"] CM --> CX["agent runs kubectl_apply replicas=0, service down"] end classDef danger stroke:#f85149,color:#f85149,stroke-width:2px; class CM,CX danger;

Figure 3. Response Manipulation in an Agentic Flow. The model responds correctly. The proxy intercepts the response and modifies it before it reaches the agent. The agent acts on the modified response. No model-level defense activates because the model was never compromised. No application-level integrity check catches it because the modified response is structurally valid. Model side: "Model was never touched", prompt injection defenses inactive, output validation sees structurally valid JSON, and application logs record replicas=0 as if it came from the model.

The scenario above is deliberately specific because the abstract version (“modify AI responses”) sounds theoretical. The concrete version is an ops agent instructed to scale a service to two replicas, where the proxy rewrites that to zero before the agent acts. That is not theoretical; it is the logical application of a capability that the proxy position makes structurally available to any attacker who chooses not to leave.

The properties that make this difficult to detect:

The model’s response was correct. The modification happens downstream of it, in the proxy layer. Any model-side logging shows the legitimate value.

The modified response is structurally valid. The attacker replaces a valid value with another valid value. Output schema validation passes. Application logs record the attacker’s value as if it came from the model.

This is not prompt injection. OWASP LLM01 describes attacks on the model’s input processing. Surface 3 does not interact with the model at all. The attack is on the transport layer, not the intelligence layer.


Why current AI security doesn’t cover this

The gap is not a failure of the existing frameworks. It is a scope boundary.

flowchart TD subgraph ZA["Application Security scope"] L1["Application / Agent Framework: input validation, AuthZ, sessions"] end GAP["AI Gateway / Proxy: no established security scope covers this layer"] subgraph ZB["AI Security scope"] L3["LLM Provider API"] L4["Foundation Model: prompt injection defenses, guardrails, OWASP LLM Top 10"] end L1 --> GAP GAP --> L3 L3 --> L4 classDef danger stroke:#f85149,color:#f85149,stroke-width:2px; class GAP danger;

Figure 4. The Defense Boundary. Current AI security frameworks address threats to the model and its inputs. The proxy layer sits outside that scope: upstream of every model-level control, downstream of the application, and at the point where both directions of traffic are simultaneously accessible in plaintext. Supply chain integrity (artifact signing, SLSA) reduces deployment risk but does not address runtime behavior.

OWASP LLM Top 10 addresses risks at the model and application layer: prompt injection (LLM01), sensitive information disclosure through model outputs (LLM02), supply chain vulnerabilities in model dependencies (LLM03), and excessive agency (LLM08). These are the right problems to address. They also share an implicit assumption: the channel between the application and the model is trusted.

MITRE ATLAS catalogs adversarial tactics against ML systems: training data poisoning, model evasion, model inversion. Again, the threat model targets the model.

The supply chain controls that have emerged from incidents like TeamPCP (artifact signing with cosign, SLSA provenance, SHA pinning of dependencies, wheel-vs-source verification) address the integrity of the deployed package. They are necessary and reduce the probability of deploying a compromised proxy. They do not address what an attacker can do from the proxy position at runtime.

The Surfaces 2 and 3 described here require no ongoing malicious artifact. Once the proxy is established (through any mechanism, including a supply chain compromise that has since been remediated) the position exists until the deployment is re-architected.


Five controls that address this at the infrastructure level

These are not checklist items for the model or the application. They are infrastructure controls for the proxy layer itself.

Scope Note

The controls below do not assume a specific AI gateway product. They are architectural patterns applicable to LiteLLM, custom proxy implementations, cloud-native AI gateway services, and any equivalent deployment. Implementation details vary; the principles apply across the category.

Infrastructure Controls Five controls for the proxy layer

1. Secrets isolation: the gateway process should not hold the keys it proxies.

The standard deployment pattern (API keys as environment variables in the gateway container) co-locates the secrets with the runtime in the same process boundary. The alternative is a sidecar-based secrets injection pattern: the gateway requests short-lived, scoped credentials from a vault at request time, rather than holding long-lived keys at startup. A compromised gateway process that cannot read its own environment for provider keys cannot perform Surface 1’s credential harvest regardless of what payload it carries.

# Current pattern (vulnerable to Surface 1):
# docker run -e OPENAI_API_KEY=sk-... litellm

# Sidecar injection pattern (structural mitigation):
# Gateway requests credential from vault sidecar per-request.
# Vault issues short-lived token scoped to specific provider + model.
# Token TTL: 60s. Useless after exfiltration without active session.

2. Egress restriction as a deployment primitive.

TeamPCP’s credential exfiltration required outbound connections to attacker-controlled domains (models.litellm.cloud, checkmarx.zone). Strict egress policy (allow only the specific provider API endpoints required, deny everything else) transforms a silent exfiltration into a blocked, logged, and alerted network event. This is not novel; it is standard network security applied to a deployment where it is typically omitted.

# Kubernetes NetworkPolicy: allow only known provider endpoints
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ai-gateway-egress
spec:
  podSelector:
    matchLabels:
      app: ai-gateway
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.0.0.0/8
        - 172.16.0.0/12
        - 192.168.0.0/16
    ports:
    - protocol: TCP
      port: 443
  # Add explicit allow rules for provider IPs only
  # Deny all other egress by default

3. Structured AI traffic logging as a security primitive, not a debugging tool.

Detecting Surface 2 (silent interception) requires a baseline of what normal AI traffic looks like. That baseline cannot exist if the gateway does not emit structured, auditable logs of request and response metadata. “Metadata” here means timestamp, provider, model, token counts, latency, request hash, and response hash. Note that this is the hash, not the content itself, so the log is safe to retain and ship off-box without re-exposing the very prompts you are trying to protect.

// One log line per interaction. No prompt or completion text, only hashes.
{
  "ts": "2026-04-14T11:02:33Z",
  "provider": "anthropic",
  "model": "claude-opus-4-8",
  "prompt_tokens": 1840,
  "completion_tokens": 220,
  "latency_ms": 1320,
  "request_sha256": "9f2c...",
  "response_sha256": "a17b...",
  "egress_dst": "api.anthropic.com:443"
}

A compromised proxy that is silently logging full content to a secondary destination will, on the same input, either emit a divergent response_sha256 or open an egress_dst that is not a known provider endpoint. Both are detectable without ever reading prompt text. For Surface 3 specifically, the check is a comparison: the hash of what the provider actually returned, recoverable from provider-side logging for the major vendors, against the response_sha256 the gateway recorded. A mismatch means the bytes changed inside the proxy, which is exactly the signature of in-transit response manipulation.

4. Response integrity checks for consequential agentic actions.

For agentic systems that execute irreversible actions based on model responses (infrastructure changes, data mutations, external API calls), the application layer should not trust model responses implicitly. This is the same principle as CSRF protection or idempotency keys: the response is not the authorization; the response triggers a verification step before execution.

# Pattern: agent action requires out-of-band confirmation before irreversible operations
async def execute_infra_action(model_response: dict) -> None:
    action_params = extract_action_params(model_response)

    # Do not execute directly from model response.
    # Log the intended action and verify against source of truth.
    if action_params["irreversible"]:
        audit_log(action_params, source="model_response")
        confirmed_params = await fetch_confirmation_from_source_of_truth(
            action_params["reference_id"]
        )
        # Execute from confirmed_params, not from model_response.
        # A proxy-layer modification to model_response does not propagate.
        await execute(confirmed_params)

5. Artifact integrity verification as a required CI gate before every AI gateway deployment.

This is covered in detail in Part II, but the framing here is different: this is not a post-incident remediation. It is a deployment gate that should exist before any AI gateway version reaches production.

# Required CI gate: verify wheel matches declared source before deployment.
# If this check had existed in LiteLLM's consumers' pipelines,
# the 46-minute exposure window would have had zero successful deployments.

#!/bin/bash
PACKAGE=$1; VERSION=$2; GITHUB_REPO=$3
WORKDIR=$(mktemp -d); trap "rm -rf $WORKDIR" EXIT

pip download "${PACKAGE}==${VERSION}" --no-deps -d "$WORKDIR/wheel/" -q
unzip -q "$WORKDIR/wheel/"*.whl -d "$WORKDIR/extracted/"
git clone -q --depth 1 --branch "v${VERSION}" "$GITHUB_REPO" "$WORKDIR/source/" 2>/dev/null

# Fail deployment if any unexpected .pth file exists
find "$WORKDIR/extracted/" -name "*.pth" -not -path "*/dist-info/*" \
  | grep -q . && { echo "FAIL: unexpected .pth file, do not deploy"; exit 1; }

# Fail deployment if wheel diverges from source
diff -rq "$WORKDIR/extracted/${PACKAGE}/" "$WORKDIR/source/${PACKAGE}/" \
  --exclude="*.pyc" --exclude="__pycache__" 2>/dev/null \
  | grep -q . && { echo "FAIL: wheel diverges from source, do not deploy"; exit 1; }

echo "PASS: wheel matches source at v${VERSION}"

What the first move opened

TeamPCP’s LiteLLM campaign is Phase 09 of a documented, ongoing operation. What it demonstrated, as a side effect of a credential harvesting run, is that the AI Gateway is a supply chain target with properties no other supply chain target has.

It concentrates credentials for the organization’s entire AI stack in a single process (Surface 1). It observes every AI interaction in the organization in plaintext, indefinitely, with no payload required beyond the initial compromise (Surface 2). It can modify the instructions that AI agents act on, in transit, without touching the model or triggering any model-level defense (Surface 3).

TeamPCP used a crowbar when the position they compromised was worth a key.

The question for every team shipping AI systems in production is whether their security posture is built for the crowbar they saw, or for what someone who understood the position would do with it.


On AI security frameworks and this gap. OWASP LLM Top 10, MITRE ATLAS, and NIST AI RMF are the right foundations for AI security. The gap described here is not a critique of those frameworks; it is a scope observation. Supply chain integrity (SLSA, cosign, SHA pinning) addresses the deployment path. Model-level defenses address the intelligence layer. The proxy runtime is currently between both scopes. That is the gap this post argues needs to be named explicitly and addressed at the infrastructure level.


MITRE ATLAS & ATT&CK mapping (click to expand)
IDTechniqueSurface
AML.T0010ML Supply Chain CompromiseInitial vector (all surfaces)
AML.T0019Publish Poisoned ArtifactsWheel injection delivery
AML.T0043.001Craft Adversarial Data, Prompt Injection of LLMSurface 2 (passive collection of prompts via compromised intermediary)
T1557Adversary-in-the-MiddleSurface 2 and 3 at proxy layer
T1528Steal Application Access TokenSurface 1, credential harvest
T1565.002Transmitted Data ManipulationSurface 3, response manipulation
T1195.002Compromise Software Supply ChainInitial deployment compromise

References

  1. PipeBreach: TeamPCP Part I: Twenty Days of Silent Access From a Two-Minute PR
  2. PipeBreach: TeamPCP Part II: Backdooring the AI Credentials Vault
  3. OWASP: Top 10 for Large Language Model Applications 2025
  4. MITRE ATLAS: Adversarial Threat Landscape for AI Systems
  5. ARMO: The Library That Holds All Your AI Keys Was Just Backdoored
  6. Snyk: How a Poisoned Security Scanner Became the Key to Backdooring LiteLLM
  7. Trend Micro: Your AI Gateway Was a Backdoor: Inside the LiteLLM Supply Chain Compromise
  8. Okta Threat Intelligence: LiteLLM supply chain attack: an explainer for identity pros
  9. lunar.dev: The LiteLLM Supply Chain Attack: What It Reveals About AI Gateway Security
  10. LiteLLM: Security Update: Suspected Supply Chain Incident (Mar 24, 2026)
  11. NIST: AI Risk Management Framework (AI RMF 1.0)
  12. Endor Labs: TeamPCP Isn’t Done (Mar 24, 2026)
DM
Daniel Malvaceda

DevSecOps Engineer

Occasionally breaks AI pipelines and supply chains to see what falls out..