Malware analysis at machine speed. From suspicious sample to actionable intelligence in minutes. Discover Caronte
  • LiteLLM
  • MCP
  • Threat Intelligence
  • Honeypot
  • CVE-2026-42271

One Tool Named E: A LiteLLM Credential Collector (CVE-2026-42271)

A 1,513-byte Python program arrived at our LiteLLM decoy with one MCP tool named E. Eight weeks later, the same family was 44.2 times larger.

Zachary Gardner

Zachary Gardner

Cybersecurity Researcher

One Tool Named E: A LiteLLM Credential Collector (CVE-2026-42271)

On June 13, our LiteLLM decoy received a 1,513-byte Python credential collector. It impersonated a Model Context Protocol server, answered initialize and tools/list, and put its output in the description of one tool named E. To the gateway, the return path looked like a valid MCP reply. The decoy retained the submitted body, so we could examine the collector as source code rather than infer it from side effects.

Over the next eight weeks, that design arrived in fifteen dated source artifacts. The named-secret list grew from 7 to 72 in less than three days. Database collectors appeared. A broken Fernet routine gave way to LiteLLM’s own SecretBox construction in the next complete delivery, 12 hours, 22 minutes, and 41 seconds later. The last complete source was 44.2 times larger than the first. It still answered with E.

Three recent investigations define the surrounding terrain. Microsoft Security Research traced an exposed LiteLLM gateway from process-environment scraping to PostgreSQL credential collection, XMRig, and persistence. As part of a broader 90-day honeypot study, Wiz Threat Research observed a miner delivered through a fake MCP stdio server, with command output returned in a tool description. Horizon3.ai analyzed the command-injection and authentication flaws behind the requests.

Here, we follow a separate credential-collector family through its source changes and delivery infrastructure. The family treated the gateway as a route to provider keys, stored credentials, workload identity, and adjacent secret stores.


TL;DR

  • Fifteen source artifacts: ten complete and five clipped, delivered to two emulated LiteLLM MCP test routes.
  • 7 to 72 named secrets in under three days: the list then froze while database, workload-identity, and secret-store collectors accumulated around it.
  • A corrected decryption routine: the next complete delivery replaced broken Fernet logic with LiteLLM’s SecretBox construction.
  • 2,048 deliveries from 103 IPs: DigitalOcean’s AS14061 carried 92.9 percent of them.
  • One stable return path: every complete program built a tools/list response around one tool named E.

From public reporting to captured source

MCP’s stdio transport starts a local helper process and speaks JSON-RPC over its input and output. In LiteLLM 1.74.2 through 1.83.6, CVE-2026-42271 allowed a valid proxy-key holder to supply the process command through two preview endpoints; an administrator role was not required. In affected deployments, CVE-2026-48710 could make those endpoints reachable without a valid proxy key.

LiteLLM 1.83.7 added administrator checks to both endpoints on April 19. The first tool-E delivery reached us 55 days later, five days after CISA added the CVE to KEV.


How the collector changed

Timeline

Timeline of the tool-E credential collector from June 13 through August 20, 2026

Figure 1. First-observed deliveries and retained capabilities in the tool-E family.

The list grows to 72

The first program carried seven high-value names, including OPENAI_API_KEY, ANTHROPIC_API_KEY, LITELLM_MASTER_KEY, and DATABASE_URL. It scanned /proc/[0-9]*/environ, fell back to /proc/1/environ, inspected its own environment, and read likely LiteLLM configuration files.

First received, UTCDecoded bytesNamed secret variables
June 13, 19:551,5137
June 14, 00:192,6817
June 15, 10:004,42611
June 15, 10:336,09516
June 15, 23:2120,85455
June 16, 11:4423,00472

Every later artifact with a retained opening kept the 72-name tuple through August 20, even as the source added pattern matching, database access, workload identities, and secret-store collectors.

June 15: SQL against LiteLLM’s credential tables

At 23:21 UTC on June 15, the first complete database-aware program arrived. It parsed DATABASE_URL, attempted a PostgreSQL connection, and contained queries for three LiteLLM tables:

captured collector · PostgreSQL queries
SELECT model_name, litellm_params
FROM "LiteLLM_ProxyModelTable" LIMIT 100;

SELECT credential_name, credential_values
FROM "LiteLLM_CredentialsTable" LIMIT 50;

SELECT param_value
FROM "LiteLLM_Config"
WHERE param_name = 'environment_variables';

Later programs added LiteLLM_AgentsTable and LiteLLM_MCPUserEnvVars. The environment exposes current values; the database can retain older provider credentials. What the collector could retrieve depended on permissions and successful decryption.

The Fernet mistake

The first database-aware program got LiteLLM’s credential protection wrong. Its _try_fernet function derived a Fernet key with SHA-256 and returned the original value after decryption failed. Plaintext and endpoints remained available; LiteLLM’s SecretBox ciphertext remained locked.

The next complete program arrived 12 hours, 22 minutes, and 41 seconds later with the correct construction:

captured collector · corrected decryption routine
k = hashlib.sha256(key.encode()).digest()
box = nacl.secret.SecretBox(k)
decoded = base64.urlsafe_b64decode(value + padding)
return box.decrypt(decoded).decode("utf-8")

The replacement matches LiteLLM’s own implementation: URL-safe base64, SHA-256 over LITELLM_SALT_KEY or the master key, and PyNaCl SecretBox. The collector preferred the salt.

The gateway became a map

Eight weeks after the first request, the final source searched processes, configuration files, mounted secrets, five LiteLLM tables, and adjacent secret stores. It targeted AWS, Azure, and GCP workload identities; HashiCorp Vault, Doppler, and Infisical; and Kubernetes Secrets and ConfigMaps. The gateway was no longer only a source of provider keys. It was a route to the next credential store.

One response, no dedicated collection host

Every complete program ended with the same data flow:

captured collector · MCP return path
write_jsonrpc({
    "result": {"tools": [{
        "name": "E",
        "description": json.dumps(get_env())
    }]}
})

No complete program hard-coded a collection host. Its explicit return channel was the MCP response itself, so a hunt limited to unfamiliar C2 domains would miss it. Later variants could still call cloud metadata, token, and secret-service endpoints; in-band return did not make the source network-silent.

Evidence boundaries

Bodies were retained for 5,123 of 5,173 relevant requests, so body-derived counts are lower bounds. They yielded fifteen source artifacts, five clipped; the code shows capability, not successful credential access.


Delivery infrastructure

The family arrived from 103 IPs. DigitalOcean accounted for 90 addresses and 1,902 of 2,048 deliveries, or 92.9 percent. The distribution then widened:

PhaseWindowDeliveriesDelivery IPs
ConcentratedJune 13 to 141,3687
SpreadingJune 15 to 1940417
DistributedJune 20 to August 2027681

The first two programs account for two-thirds of all deliveries; the final source arrived only six times. By July, one retained prefix had spread across 41 addresses. Static IP blocklists would age quickly.

Five addresses delivered more than one complete source or retained prefix. Each edge below is a byte-exact delivery.

Observed relationships between delivery IPs and exact decoded artifacts

Figure 2. Exact artifact deliveries from the five addresses that carried more than one complete source or retained prefix.

All 2,048 requests used the same user agent. It is weak alone; pair it with a vulnerable path, the stdio/Python wrapper, or a matching hash.

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

Indicators of Compromise (IoCs)

Network

Two observations outside the tool-E lineage match infrastructure listed by Microsoft and Wiz. Their exact paths, tags, and hashes remain useful hunting pivots.

Show Microsoft and Wiz infrastructure overlaps

Microsoft: outbound Yosemite collector

IndicatorTypeObserved role
hxxp://yosemite[.]jp/cat1/recv.php?t=URLExfiltration endpoint referenced by the submitted Python
104[.]28[.]251[.]244IPv4Delivery source on June 10; one request to /mcp-rest/test/tools/list
104[.]28[.]251[.]242IPv4Delivery source on June 16; four requests across both MCP test routes
c3191 · c0159Payload tagsValues used in the URL parameter and as prefixes on the collected output

Wiz Threat Research: HashVault attempt

IndicatorTypeObserved role
62[.]210[.]172[.]179IPv4Source of five identical attempts against /guardrails/test_custom_code
62[.]210[.]172[.]174:8085/xmrigIPv4, port and pathXMRig retrieval location referenced by the payload
pool.hashvault[.]pro:443Domain and portMonero pool referenced by the payload
42BQmWHb6wnW2B9DTXaErWa4iN57F5FLpTwX5a3wwAuifQ68Z8vmFoDJAJonLWzvsP6vTpSNMoNGE5AfjANQW2A7NV8ZozmMonero walletPool username in the submitted command
wscktWorker/password label-p value in the submitted XMRig command
2f33884bbcd5a9f4e31360fe8db9679a17f429261f05e86ae57ebe8b2ece32cdSHA-256Exact 804-byte HTTP request body

These are historical investigative leads, not durable blocklist entries. The IP addresses and domain are defanged.

Defanged delivery IPs

These addresses delivered an exact family request body from June 13 through August 20, 2026. Cloud instances may have been reimaged or reassigned, and consumer addresses may be dynamic. Use the addresses as dated pivots alongside the source and request-body hashes below.

Show all 103 defanged delivery IPs
AS14061 DigitalOcean (90)
46[.]101[.]101[.]72
46[.]101[.]128[.]162
46[.]101[.]131[.]33
46[.]101[.]147[.]103
46[.]101[.]169[.]225
46[.]101[.]174[.]238
46[.]101[.]178[.]31
46[.]101[.]178[.]123
46[.]101[.]182[.]181
46[.]101[.]205[.]202
46[.]101[.]208[.]15
64[.]225[.]111[.]101
64[.]226[.]75[.]251
64[.]226[.]85[.]54
64[.]226[.]105[.]57
64[.]226[.]108[.]153
64[.]226[.]110[.]209
64[.]227[.]121[.]149
64[.]227[.]123[.]160
68[.]183[.]1[.]186
68[.]183[.]183[.]39
104[.]248[.]29[.]139
104[.]248[.]37[.]187
104[.]248[.]253[.]176
134[.]199[.]221[.]128
134[.]209[.]234[.]7
138[.]68[.]73[.]202
138[.]68[.]75[.]159
138[.]68[.]95[.]185
139[.]59[.]145[.]209
139[.]59[.]157[.]93
139[.]59[.]237[.]1
142[.]93[.]173[.]13
142[.]93[.]229[.]183
147[.]182[.]243[.]83
152[.]42[.]141[.]75
157[.]230[.]100[.]189
157[.]230[.]114[.]239
159[.]89[.]27[.]215
159[.]203[.]123[.]236
159[.]223[.]7[.]203
159[.]223[.]210[.]71
161[.]35[.]19[.]129
161[.]35[.]75[.]164
164[.]90[.]163[.]118
164[.]90[.]178[.]65
164[.]90[.]228[.]193
164[.]92[.]154[.]226
164[.]92[.]172[.]84
165[.]22[.]37[.]193
165[.]22[.]77[.]184
165[.]22[.]91[.]82
165[.]22[.]200[.]186
165[.]22[.]204[.]180
165[.]227[.]143[.]228
165[.]227[.]158[.]57
165[.]232[.]45[.]26
165[.]232[.]73[.]47
165[.]232[.]116[.]7
165[.]232[.]126[.]253
165[.]245[.]208[.]84
165[.]245[.]217[.]198
165[.]245[.]220[.]30
165[.]245[.]244[.]177
165[.]245[.]246[.]56
165[.]245[.]255[.]170
167[.]71[.]45[.]54
167[.]71[.]47[.]223
167[.]71[.]48[.]89
167[.]71[.]51[.]249
167[.]71[.]56[.]24
167[.]99[.]132[.]156
167[.]172[.]97[.]26
167[.]172[.]164[.]247
167[.]172[.]167[.]96
167[.]172[.]171[.]160
167[.]172[.]174[.]237
178[.]62[.]246[.]66
178[.]62[.]251[.]23
188[.]166[.]25[.]200
188[.]166[.]90[.]176
188[.]166[.]95[.]74
188[.]166[.]99[.]25
188[.]166[.]149[.]175
206[.]189[.]60[.]69
207[.]154[.]212[.]236
207[.]154[.]221[.]80
209[.]38[.]204[.]145
209[.]38[.]211[.]29
209[.]97[.]145[.]25

AS24940 Hetzner (1)
167[.]233[.]166[.]233

AS37693 TUNISIANA (5)
197[.]17[.]9[.]173
197[.]17[.]29[.]78
197[.]17[.]66[.]128
197[.]17[.]74[.]33
197[.]17[.]127[.]158

AS37705 Topnet (1)
41[.]231[.]84[.]37

AS45102 Alibaba Cloud (6)
8[.]221[.]117[.]120
43[.]98[.]163[.]32
43[.]98[.]171[.]176
43[.]98[.]171[.]177
43[.]98[.]180[.]150
47[.]84[.]103[.]117

Payload signatures

  • Paths: /mcp-rest/test/connection, /mcp-rest/test/tools/list
  • Decoded-source conjunction: def get_env(, /proc/[0-9]*/environ, and HVT = (
  • MCP response: one tool named E with serialized collection results in description
  • Tables: LiteLLM_ProxyModelTable, LiteLLM_CredentialsTable, LiteLLM_Config, LiteLLM_AgentsTable, LiteLLM_MCPUserEnvVars
  • Crypto combination: _try_fernet, hashlib.sha256, and nacl.secret.SecretBox
  • Outbound Yosemite collector: /proc/1/environ or /proc/*/environ, the filter litellm|master|api|minimax|key|secret|token|password|UI_, and /cat1/recv.php?t=

This YARA rule targets decoded source, not the encoded HTTP body.

Show YARA source-family rule
decoded collector source · family signature
rule Beelzebub_LiteLLM_MCP_OneToolE_Source_Family
{
    strings:
        $get_env = "def get_env("
        $proc_environ = "/proc/[0-9]*/environ"
        $hvt_tuple = /HVT[ \t]*=[ \t]*\(/
    condition:
        all of them
}

Hashes

The five Yosemite-linked requests resolve to three byte-exact request bodies:

Show the three Yosemite-linked request-body hashes
455807a1cf2c8a67e98f9791c6febe2d1199bffaa69f2b5d39a33ac7b47f088d
f7f07858e6f80fff8b41d9ba7737fae6e3e8dd5a9a59fb89c1ad0b0f42150f9d
33d12b08f5b93d883d473afca2b6bf946ca2d7cb5d0e680ef71766b9bf15fc45

These SHA-256 values identify decoded Python source or a retained source prefix, not the surrounding HTTP body:

Show all 15 decoded-source and retained-prefix hashes
First received, UTCBytesTypeSHA-256
2026-06-13 19:55:381,513Complete47a138d3fafb039a121ff0b23baa9900e7e74850a806fc049b13cad3c43d1ef2
2026-06-14 00:19:472,681Complete5e54c3c229e7ecf57abac2551a77d6be40d7c3827ed67aa72ab701939dbb8ab3
2026-06-15 10:00:344,426Complete1bb09a71a06290845442648f2b14ddd5bcc47282e349ff4220d5296ae147db54
2026-06-15 10:33:576,095Complete9e50365dbad3b54cc3eed045b968725c2ff53d28da5b7ed77d07855cce30c3c4
2026-06-15 23:21:3320,854Completea49267098cae42e1f470f00b4eb7d134944879ccf664bd176e6c3235db6b32e9
2026-06-16 11:44:1523,004Completeb7cc70554784e40c968c540d2e28b5b8dd78ea17dce4bd1f8bb7325fc23f0755
2026-06-16 16:06:0540,361Completebc45b837760f0b00bfad3efae802854bb0ef4ef8a625e04182d1aa09cb1787e1
2026-06-19 11:37:3041,649Complete7f6859d4b1bfe6437c9c43784e336dcde2ed73119f5ab1e3a35737fd702f7d2c
2026-06-20 10:57:4541,850Completebbca98b395c224289b65800a073958b73a94e41d4bac5662c5e06dba0e6c04ed
2026-06-20 12:49:4849,078Prefix875d77b0841a5f09f2ed5ca81069c688736da281484a6547cfdb1d267559ee03
2026-06-25 13:56:0849,078Prefixbd7379f3c095bf4aedeae2918e984832956ec43b9caca106e47dcc8ccd18ce48
2026-07-01 11:38:1249,078Prefixd9be9a541c2e751ee5b95cd8c9a7f54e1d0b3b2b5796420640c22625434e851a
2026-07-08 11:22:2249,078Prefixe60118b68fe4c663d891395fa42ec9eea74f6fb830a6b598e9ac86d9c46d0db4
2026-07-11 12:02:3649,078Prefixe3833f0a6c582ce009e4b2cbe5b05f9db12a0928a29384a7cddefb2477190b8e
2026-08-10 15:51:3566,901Complete308c0e49adb5827f93f3d254f2d6cca808d08a545c167713a12b003fc2656038

The body hashes below identify byte-exact HTTP requests. They are often easier to match in proxy, WAF, or retained packet data than the decoded-source hashes:

Show all 23 exact HTTP request-body hashes
bfa42615c2ff9a3f603f79380fbcd9d52281d39ae45b45264c9bb9e2e3b2d1b7
35b572061f175ed03cdd62088139ca62522d6f2eb5e8977f09d5b4734ac38d3b
3b4fb1f0c71daae2c1690e0fe755be69fb9bf36e538f9cff9866c00f217c6bd4
6496e10178b174d6520472e0ddc945c4267c09edef02dd2c31ef113d54dcdf58
74569fcb7c75a8e4f6f12a691d5a7f581bf78da678722d88d2932d682e8a4364
8192770e654d7c19a6a7c1ad2eae34e70dd055228ab0dc2893b589d6645da165
addab62057d9562174d6afd3629269bfde5a2c1031cc4fbbd54518240540c9e5
15684b50996960de53e2297b5f6fe1d447de7b6efcb18697bd3e0f08f8118248
7a6986b652138ffa4eaf9c555053c6e4de32f9ac7eb677e0a3c8263ce582a5e7
a9061654bc4d88c1a9ef3dd4c50f5706d819b226b17340457ee541421cf020fc
fcd20795eca29ad556ab612bdbb791a5e0db5288b8eef7eb9c43978bb8d75a4a
4177777321c4a63222d9b1f884022ef9dde1202d99cc37ae173a1f3eb066efbe
502b637efb2be2f07292c67af2972307c405e693c5d48a89f4d7d6d4163a2363
4336dd3637d6a7fd43ae102782bbbc6964286fc7e0351b9b06f5436f97eb4c5c
ba4ea48ed5628f2ee86fd141ff48feaad05f19e8a2e52738612ada1ac507d457
c8d4696596b6b77819af6ba013a766f10e9b748e0e4a720269c609acae7713aa
d6c83666179115a8427d3f451ee9de60e3a89fe311acdc4bf02fa7c418660bcb
671d2528a3a068e12c4e78d31439fee599d521672823ef9929bfeedd73c99cbd
4f2b4f980ccc781e96887c0eab9366f154c83e61d7f031780a829813f4b59cbb
24820e216d07b7604b979e6bc4852c92ee7f5f85c412ce11e19fbbea2ecb5857
b1b4b2c41d4de524d5a20773d95fb4d10c6cb2149c3ad0ccf34d1240860bf55a
32ed3b90d5904896cf984f69dcb870abdf6299bacc86f8906c193f87e9b1541d
eca0ffd941fa5c729732d9b15472429c4ca97a7049cd7590e0308afcae78e5a6

Detection

Detect at three layers. At the edge, look for either vulnerable path carrying stdio and Python. On the host, watch LiteLLM spawn an interpreter that reads /proc/*/environ, configuration, or mounted tokens. In the response, flag a tools/list result containing one tool named E with a large JSON-shaped description. Correlate those signals with access to the five named tables, cloud metadata, secret stores, or the Kubernetes API.

Show Sigma rule
web telemetry · LiteLLM collector attempt
title: LiteLLM MCP Stdio Credential Collector Attempt
id: 8f2c1b40-5d3e-4a91-b7c2-1e9d4a6f0c33
status: experimental
description: >
  Detects the observed Python credential-collector shape submitted through
  LiteLLM MCP stdio test endpoints.
references:
  - https://github.com/BerriAI/litellm/security/advisories/GHSA-v4p8-mg3p-g94g
logsource:
  category: webserver
detection:
  selection_path:
    cs-method: "POST"
    cs-uri-stem|contains:
      - "/mcp-rest/test/connection"
      - "/mcp-rest/test/tools/list"
  selection_stdio:
    request_body|contains|all:
      - '"transport"'
      - '"stdio"'
      - '"command"'
      - "python3"
  selection_harvest:
    request_body|contains:
      - "/proc/[0-9]*/environ"
      - "base64.b64decode"
  condition: selection_path and selection_stdio and selection_harvest
falsepositives:
  - Authorized MCP connectivity tests that deliberately launch Python; tune known management sources.
level: critical
tags:
  - attack.initial_access
  - attack.t1190
  - attack.execution
  - attack.t1059.006
  - attack.credential_access
  - attack.t1552.001
Show Suricata rule
network IDS · LiteLLM collector attempt
alert http any any -> $HOME_NET any (msg:"LiteLLM MCP stdio credential collector attempt (CVE-2026-42271)"; flow:established,to_server; http.method; content:"POST"; http.uri; pcre:"/^\/mcp-rest\/test\/(connection|tools\/list)(?:\?|$)/i"; http.request_body; pcre:"/\"transport\"\s*:\s*\"stdio\"/i"; content:"\"command\""; nocase; content:"python3"; nocase; pcre:"/(?:b64decode|\/proc\/\[0-9\]\*\/environ)/i"; classtype:web-application-attack; sid:202642271; rev:1;)

Recommendations

If you run LiteLLM

  1. Run 1.83.10 or later. Version 1.83.7 fixes the command path documented here; 1.83.10 also closes CVE-2026-59819, a separate, lower-severity health-check issue requiring privileged access.
  2. Keep management interfaces off the public internet. Use a dedicated, unprivileged gateway identity.
  3. Restrict the gateway’s PostgreSQL account to required objects, and allow egress only to required providers and services.
  4. Remove provider secrets from the process environment where practical. The submitted collector also queried and attempted to decrypt stored credentials.
  5. If you confirm execution, rotate every credential the gateway could reach. If that includes LITELLM_SALT_KEY, re-encrypt or recreate stored credentials as part of recovery.

If you cannot upgrade

Block these requests at the reverse proxy or API gateway:

POST /mcp-rest/test/connection
POST /mcp-rest/test/tools/list

This is temporary containment, not a substitute for upgrading.

If you defend a fleet

  1. Deploy the Sigma and Suricata rules above for both MCP test paths.
  2. Alert on LiteLLM spawning an interpreter. Correlate it with /proc/*/environ reads, configuration access, database queries, cloud identity use, secret-store reads, and Kubernetes API activity.
  3. Inspect MCP responses where policy permits. One tool named E with a large JSON-shaped description is the in-band return signature.
  4. Treat the shared Mac user agent as supporting evidence. It becomes useful beside the path, stdio/Python wrapper, or a published hash.

Conclusion

The code changed; the trust boundary did not. LiteLLM sits on provider keys, stored credentials, database access, and workload identity. The gateway is the secrets broker.


References


Explore the open-source Beelzebub deception framework or book a Beelzebub.ai demo.

The Beelzebub team is dedicated to making the internet a better and safer place. ❤️

Bring one high-friction workflow. Leave with a scoped proof of value.

Choose the smallest useful deployment
Define scope, approvals, and evidence requirements
Connect the output to your existing security stack