On March 27, 2026, two malicious versions of the Telnyx Python SDK (4.87.1 and 4.87.2) were uploaded to PyPI using stolen credentials linked to the TeamPCP threat group. The payload used WAV steganography to hide credential-stealing malware inside audio files. PyPI quarantined both versions within six hours, but the package's 742,000+ monthly downloads meant significant exposure.
Supply Chain Attack | Credential Theft | WAV Steganography | PyPI
The threat actor group TeamPCP continues its aggressive supply chain campaign. On March 27, 2026, two malicious versions of the official Telnyx Python SDK were uploaded to PyPI. The platform responded quickly by quarantining the packages, but the incident still exposed environments pulling from a library with over 742,000 monthly downloads and 3.8 million total downloads historically.
This marks the latest hit in a weeks-long operation that previously targeted Trivy (CVE-2026-28353), Checkmarx KICS, and LiteLLM. The pattern is now unmistakable: stolen credentials from one compromise fuel the next, and the attackers are evolving their evasion techniques with each iteration.
TL;DR
- What: Two malicious versions of the Telnyx Python SDK (4.87.1 and 4.87.2) were published to PyPI on March 27, 2026 using stolen maintainer credentials
- How: The payload was injected into telnyx/_client.py and executed automatically on import. It downloaded .WAV audio files from a C2 server and extracted hidden malware from the audio frames using base64 + XOR steganography
- Impact: Credential harvesting (SSH keys, cloud tokens, Kubernetes secrets, environment variables), platform-specific persistence, and encrypted exfiltration — affecting a package with 742,000+ monthly downloads
- Window: Approximately 6 hours (03:51 UTC to 10:13 UTC) before PyPI quarantined both versions
- Attribution: TeamPCP — the same group behind the Trivy, KICS, and LiteLLM compromises. Credentials likely stolen from the earlier LiteLLM incident
- Fix: Uninstall and pin to telnyx==4.87.0, the last known clean version. Rotate all credentials on any affected system
What Happened
The timeline was tight. At 03:51 UTC on March 27, 2026, telnyx==4.87.1 appeared on PyPI. Roughly sixteen minutes later, at approximately 04:07 UTC, telnyx==4.87.2 followed. Neither version had a corresponding release or tag in the official Telnyx GitHub repository. The attacker used stolen PyPI publishing credentials — likely obtained from the earlier LiteLLM compromise — to push the packages directly, bypassing the normal release workflow entirely.
The malicious code was injected into a single file: telnyx/_client.py — the core HTTP client that runs on every import telnyx call. The legitimate SDK ends at approximately line 7,758; the attacker appended 74 lines of malicious code at lines 7,760 through 7,825. No install hooks or postinstall scripts were used — the malware executes at module import time, bypassing security scanners that monitor setup.py lifecycle hooks. The injected code uses only standard library modules (subprocess, tempfile, wave, urllib.request, base64, os, sys), so no suspicious external dependencies appear in the package metadata.
PyPI and multiple independent security researchers detected the issue rapidly. Both versions were quarantined by 10:13 UTC the same day, limiting the exposure window to roughly six hours. For automated pipelines pulling unpinned dependencies, six hours is more than enough.
Telnyx issued an official Supply Chain Security Notice, confirming that their core platform, APIs, and infrastructure remained untouched. The compromise was isolated to the PyPI distribution of the Python SDK. A community member filed GitHub issue #235 at approximately 06:52 UTC, and PyPI published advisory PYSEC-2026-3 the same day.
A critical factor in this breach: the Telnyx PyPI project used a long-lived API token rather than PyPI Trusted Publishers (OIDC-based publishing). Anyone with the token could publish from anywhere — no GitHub Actions workflow, no identity verification, no audit trail tying a release to a specific commit.
Two Versions, One Evolution
The attacker published two versions within minutes, and the difference between them reveals a real-time debugging cycle.
Version 4.87.1 — The Botched Release
The first malicious version contained a bug. A NameError in the Windows execution path — the attacker called Setup() with a capital S instead of the lowercase setup() — prevented the Windows payload from executing. The Linux and macOS paths still worked in some cases, but the mistake crippled the attack on Windows targets.
Version 4.87.2 — The Fix
Sixteen minutes later, 4.87.2 landed with the typo corrected, enabling the full attack chain across all platforms. The rapid iteration shows an attacker who was testing in real-time against a live package registry — publishing, catching the error, fixing, and republishing.
Technical Breakdown: WAV Steganography as Payload Delivery
The most notable evolution in this attack is the payload delivery mechanism. Previous TeamPCP operations used inline base64-encoded payloads or straightforward download-and-execute patterns. The Telnyx compromise introduced WAV audio steganography — a technique that blends remarkably well with Telnyx's voice and telephony focus.

Here is how the payload chain works:
- On import, the injected code in telnyx/_client.py reaches out to a C2 server at 83.142.209.203:8080
- It downloads seemingly innocent .WAV audio files — ringtone.wav or hangup.wav — filenames that would not look out of place in a telephony SDK
- The real payload is hidden inside the audio file's raw PCM frames. The malware reads the WAV frames, extracts embedded bytes, and decodes them using a base64 + XOR scheme
- The decoded payload is the actual credential stealer and persistence installer, which executes in memory or drops to disk depending on the target platform
The steganographic decoding logic extracted from the malicious _client.py reveals how the hidden payload is recovered from the audio frames:
with wave.open(wf, 'rb') as w:
raw = base64.b64decode(w.readframes(w.getnframes()))
s, data = raw[:8], raw[8:]
payload = bytes([data[i] ^ s[i % len(s)] for i in range(len(data))])The approach is straightforward but effective. The WAV file is a valid audio container — it will play as sound if opened in a media player. But the raw PCM frame data holds a base64-encoded blob. Once decoded, the first eight bytes serve as an XOR key, and the remaining bytes are the encrypted payload. This is not traditional least-significant-bit steganography — the entire frame data carries the payload, which is why WAV (an uncompressed format) was chosen over MP3 or OGG, which would corrupt the embedded data during compression.
The choice of WAV files is deliberate and context-aware. A telephony SDK downloading audio files does not raise flags in the same way that downloading an executable or a suspicious .bin file would. Network monitors categorize WAV traffic as benign media. Content inspection tools pass it through. The attacker tailored the evasion to the target library's domain — a level of operational craft that marks a clear step up from the LiteLLM attack's inline base64 approach.
What the Payload Does
Once the steganographic layer is decoded, the payload executes a multi-stage credential theft and persistence operation.
Credential Harvesting
The stealer component systematically collects sensitive data from the compromised host:
- SSH keys — private keys, authorized_keys, known_hosts, config files
- Cloud provider tokens — AWS credentials and config, GCP application default credentials, Azure credential files
- Kubernetes secrets — kubeconfig, service account tokens, admin configs
- Environment variables — the full output of the process environment, capturing every API key, secret, and token loaded at runtime
- Database credentials — PostgreSQL (.pgpass), MySQL (my.cnf), MongoDB (.mongorc.js), Redis configuration
- Cryptocurrency wallets — Bitcoin, Ethereum, Solana, Cardano, and other keystores
- TLS/SSL certificates and private keys — .pem, .key, .p12, and .pfx files
- Package manager tokens (.npmrc, .vault-token, .netrc), CI/CD secrets (terraform.tfvars, Jenkinsfile, GitLab CI configs), and shell histories
Kubernetes Lateral Movement
If a Kubernetes service account token is present on the compromised host, the harvester escalates beyond simple credential theft. It dumps all secrets across every namespace, then deploys privileged pods to every node in the cluster. Each pod — named with a node-setup- prefix and placed in the kube-system namespace — mounts the host root filesystem at /host with hostPID, hostNetwork, and privileged flags enabled. A single compromised Telnyx SDK import in one pod can escalate to full cluster takeover.
The payload also makes active AWS API calls when cloud metadata is accessible, including IMDSv2 token requests, Secrets Manager enumeration, and SSM Parameter Store access using custom SigV4 request signing.
Platform-Specific Persistence
The payload uses different persistence mechanisms depending on the target operating system:
| Platform | Technique | Details |
|---|---|---|
| Windows | Startup folder binary + cooldown lock | Downloads hangup.wav from C2, extracts PE binary via steganography, drops it as msbuild.exe in the Startup folder with a hidden .lock file (attrib +h) that enforces a 12-hour cooldown between executions. Launched with CREATE_NO_WINDOW flag to suppress any visible console |
| Linux/macOS | Fileless smash-and-grab + systemd persistence | Spawns an orphaned subprocess (start_new_session=True), downloads ringtone.wav, decodes and pipes the payload via stdin to sys.executable — never written to disk. Operates in a TemporaryDirectory that auto-cleans on exit. Separately installs audiomon.py as a systemd user service (audiomon.service) that polls the C2 every 45-55 minutes with jittered intervals |
Encrypted Exfiltration
All harvested credentials are packaged and encrypted before leaving the host. The exfiltration uses a two-layer encryption scheme:
- A random 32-byte AES-256-CBC session key encrypts the collected data
- The session key itself is encrypted with a hardcoded RSA-4096 public key — the same key observed in the LiteLLM compromise
- Both encrypted blobs are packaged as tpcp.tar.gz and sent to the C2 infrastructure
The reuse of the same RSA-4096 public key from the LiteLLM incident is a strong indicator linking both attacks to the same operator. It also means that only someone with the corresponding private key can decrypt the exfiltrated data — even if a defender intercepts the archive in transit, the contents remain unrecoverable without the attacker's key.
The TeamPCP Campaign: A Pattern of Escalation
This is not an isolated incident. The Telnyx compromise is the latest link in a chain of supply chain attacks attributed to TeamPCP that spans several weeks and multiple high-profile targets.

| Date | Target | Vector | Technique |
|---|---|---|---|
| Late Feb 2026 | Trivy (CVE-2026-28353) | VS Code extension + CLI | Stolen GitHub PAT, malicious extension updates |
| Early Mar 2026 | Checkmarx KICS | Supply chain propagation | Compromised through Trivy dependency chain |
| Mar 24, 2026 | LiteLLM (v1.82.7, v1.82.8) | PyPI credential theft | .pth file persistence, inline payload, base64 encoding |
| Mar 27, 2026 | Telnyx SDK (v4.87.1, v4.87.2) | PyPI credential theft (from LiteLLM) | WAV steganography, import-triggered execution |
Each attack builds on the last. Credentials stolen from one target unlock publishing access to the next. The techniques are evolving too — from inline base64 in LiteLLM to WAV steganography in Telnyx. The group is adapting its evasion methods while maintaining the same core objective: harvest credentials and propagate access.
Who Is Affected
You are at risk if any of your environments — local development, CI/CD pipelines, staging, or production — installed or depended on either of these versions:
- telnyx==4.87.1
- telnyx==4.87.2
The last known clean version is telnyx==4.87.0, published on March 26, 2026, with a matching GitHub tag.
Telnyx is not a niche library. It powers voice, SMS, messaging, fax, and SIP trunking integrations for thousands of applications. The package's 742,000+ monthly downloads and 3.8 million total downloads mean the blast radius of even a six-hour window is significant — particularly for CI/CD pipelines that resolve unpinned dependencies on every build.
Immediate Remediation
Check Your Installed Version
pip show telnyxUninstall and Reinstall Clean Version
pip uninstall telnyx -y
pip install telnyx==4.87.0Scan for Indicators of Compromise
# Search for suspicious WAV files dropped by the payload
find / -name "ringtone.wav" -o -name "hangup.wav" 2>/dev/null
# Check for persistence on Windows
dir "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe"
# Check for persistence on Linux
systemctl list-units --type=service | grep audiomon
# Review outbound connections to the C2 IP
netstat -an | grep 83.142.209Full Incident Response If Exposed
- Treat the affected machine or environment as fully compromised
- Rotate ALL credentials immediately — API keys, SSH keys, cloud tokens, database passwords, Kubernetes secrets, and any secrets present in environment variables
- Scan for persistence artifacts: unexpected .wav files, msbuild.exe in Windows Startup folders, or audiomon.service on Linux
- Review and update lockfiles (requirements.txt, poetry.lock, Pipfile.lock) to pin the exact clean version and explicitly block >=4.87.1
- Audit CI/CD build logs for any pipeline runs that resolved telnyx during the March 27 exposure window
- Rebuild affected environments from clean images — do not trust a host that has been cleaned in place
Indicators of Compromise
| Indicator | Type | Description |
|---|---|---|
| telnyx==4.87.1 | PyPI Package | Malicious version — NameError bug in Windows path, Linux/macOS payload functional |
| telnyx==4.87.2 | PyPI Package | Malicious version — full attack chain operational across all platforms |
| 83.142.209.203:8080 | C2 Server | Hosts ringtone.wav and hangup.wav steganographic payloads |
| ringtone.wav / hangup.wav | Steganographic Payload | WAV audio files containing base64 + XOR encoded malware in raw PCM frames |
| msbuild.exe (in Startup folder) | Persistence Artifact | Windows persistence binary disguised as Microsoft Build Engine |
| audiomon.service | Persistence Artifact | Linux systemd service for maintaining C2 access |
| tpcp.tar.gz | Exfiltration Archive | AES-256-CBC + RSA-4096 encrypted credential package (same RSA key as LiteLLM) |
| 7321caa3...bd0b8d9 | SHA-256 (4.87.1 wheel) | 7321caa303fe96ded0492c747d2f353c4f7d17185656fe292ab0a59e2bd0b8d9 |
| cd081158...9395fe3 | SHA-256 (4.87.2 wheel) | cd08115806662469bbedec4b03f8427b97c8a4b3bc1442dc18b72b4e19395fe3 |
| ~/.config/audiomon/ | Linux Persistence Path | audiomon.py + systemd user service polling C2 every 45-55 minutes with jittered intervals |
| /tmp/.initd_state | State Tracking File | Persistence state file used by the Linux audiomon service |
| PYSEC-2026-3 | Advisory | PyPI security advisory published for the malicious Telnyx versions |
| TeamPCP | Threat Actor | Attributed group behind Trivy, KICS, LiteLLM, and Telnyx compromises |
Broader Lessons from the TeamPCP Campaign
Stepping back from the Telnyx specifics, this campaign highlights several trends that security teams and developers need to internalize.
Stolen credentials propagate. The chain is now well documented: a stolen GitHub PAT led to Trivy, Trivy's compromised CI/CD led to LiteLLM, and LiteLLM's stolen PyPI credentials led to Telnyx. Each compromised project becomes the staging ground for the next. Credential rotation after any incident is not optional — it is the firebreak that stops the chain.
Attackers are innovating evasion. The jump from inline base64-encoded payloads to WAV steganography in a single campaign shows active development on the attacker's side. By embedding malware inside audio files that match the target library's domain (telephony), the payload becomes harder to distinguish from legitimate network activity. Expect future attacks to continue matching evasion techniques to the compromised package's functionality.
Popular infrastructure SDKs are prime targets. Telnyx, LiteLLM, and Trivy are not application frameworks — they are infrastructure tools that sit deep in the stack with privileged access to credentials, APIs, and deployment pipelines. Compromising these packages gives attackers access to the highest-value secrets in an organization's environment.
Fast detection does not eliminate risk. PyPI's quarantine within six hours is a significant improvement in platform response. But six hours is an eternity for automated pipelines. Any CI/CD job that ran during that window with an unpinned telnyx dependency pulled the malicious version, executed the payload, and potentially exfiltrated credentials — all before any human reviewed a single alert.
Long-Term Prevention
- Pin exact dependency versions in all requirements files, lockfiles, and Dockerfiles. Never use >= or ~ constraints in production
- Enable Trusted Publishing (OIDC) on PyPI and enforce 2FA with hardware keys for all maintainer accounts
- Verify PyPI packages against GitHub releases before upgrading — if there is no corresponding tag, do not install
- Monitor outbound network connections from build environments, especially to bare IP addresses. Traffic to the 83.142.209.x range is an immediate red flag
- Audit .wav, .mp3, and other media files downloaded by dependencies — steganography will likely appear in future supply chain attacks
- Integrate supply chain security scanning into CI/CD pipelines and enable alerting for newly published versions of critical dependencies
References
- Telnyx Official Security Notice — telnyx.com/resources/telnyx-python-sdk-supply-chain-security-notice-march-2026
- GitHub Issue #235 — github.com/team-telnyx/telnyx-python/issues/235
- PyPI Advisory PYSEC-2026-3
Conclusion
Supply chain attacks like this remind us that open-source ecosystems are only as secure as their weakest publishing credential. TeamPCP has demonstrated that a single stolen token can cascade through multiple projects over weeks, with each compromise enabling the next. The techniques are getting more sophisticated — WAV steganography is a meaningful evolution — but the initial access vector remains the same: compromised maintainer credentials and unpinned dependencies.
If you use the Telnyx SDK for voice, SMS, messaging, or SIP trunking, act now: downgrade to 4.87.0, rotate every credential that was in scope, and audit your build pipelines. PyPI's rapid quarantine limited the damage, but the six-hour window was enough for automated systems to pull, execute, and exfiltrate.
Pin your dependencies. Verify your sources. Rotate your credentials. The next link in the TeamPCP chain is a matter of when, not if.
About the Author
Cybersecurity expert with extensive experience in threat analysis and security architecture.
