Skip to main content

A Poem on GitHub and a CVE Chain: the PoeLLM Botnet Mining on Exposed AI Infrastructure

Updated 12 min readMarkdown
Part of seriesNewsletters-eng
A Poem on GitHub and a CVE Chain: the PoeLLM Botnet Mining on Exposed AI Infrastructure

Summary

PoeLLM is malware that Black Lotus Labs (Lumen) has tracked since April 2026. It has compromised more than 3,400 servers, peaking at over 800 active systems per day. Victims are concentrated in the United States and Western Europe, and most run self-hosted AI or open-source services exposed to the internet: LiteLLM, Ollama, Gotenberg, Gitea, with signs of Ivanti Sentry targeting as well.

Two details make this campaign worth reading closely:

  • The main entry point is CVE-2026-42271 in LiteLLM. Horizon3 demonstrated that it chains with CVE-2026-48710 (Starlette's "BadHost") into unauthenticated RCE at CVSS 10.0.
  • The C2 address is hidden in a poem stored in a GitHub repository. To rotate C2, the attacker edits a few words in the poem, and every infected machine derives the new address on its own.

Monetization is cryptomining via XMRig and Iron, connecting to the Kryptex pool. But each infected machine is also turned into a scanner and exploit server, so the botnet expands without further operator involvement.

Priority actions:

  • Patch LiteLLM to 1.83.7 or later and Starlette to 1.0.1 or later. If you cannot patch yet, block the two test endpoints below immediately.
  • Rotate every credential the LiteLLM proxy holds: model provider API keys, secrets and environment variables. If the instance was ever internet-exposed, treat them as compromised.

The Vulnerability Chain: From "Authenticated" to "No Credentials at All"

CVE-2026-42271 is a command injection flaw in two LiteLLM MCP server endpoints:

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

These endpoints accept a complete MCP server configuration, including command, its arguments and environment variables used by the stdio transport. When invoked, LiteLLM spawns that command as a subprocess on the proxy host.

The flaw was originally rated as requiring authentication, because access to these endpoints was gated by a valid proxy API key.

Then CVE-2026-48710 arrived. This is a Host header validation flaw in Starlette, disclosed on 26/05/2026 as "BadHost". Horizon3 confirmed that for LiteLLM deployments with Starlette ≤ 1.0.0 in the dependency tree, it bypasses the authentication mechanism entirely.

The result is remote code execution with no credentials at all, running as the LiteLLM proxy process. Horizon3 assessed the chain at CVSS 10.0 Critical.

Successful exploitation allows attackers to:

  • Execute arbitrary commands on the LiteLLM host
  • Access model provider credentials
  • Steal API keys and secrets stored by the proxy
  • Move laterally into connected AI infrastructure
  • Compromise downstream systems behind the gateway

Affected versions: LiteLLM 1.74.2 through 1.83.6, and any deployment with Starlette ≤ 1.0.0.

Timeline: The Campaign Ran Alongside the Research

Merging Horizon3's timeline with Black Lotus Labs's produces a notable picture:

Date Event
13/04/2026 First commit of the C2-bearing poem to GitHub. The first C2 is a router in Brazil, used to test the infection process
20/04/2026 CVE-2026-42271 disclosed, described as authenticated command injection
08/05/2026 LiteLLM ships 1.83.7 with additional authorization controls and updated Starlette dependencies
May 2026 The operator begins broad scanning and exploitation against LiteLLM and Gotenberg
26/05/2026 Public disclosure of CVE-2026-48710 ("BadHost")
01/06/2026 Horizon3 validates the chain and unauthenticated RCE
Early June 2026 BLL discovers PoeLLM infrastructure via an Ivanti Sentry victim (CVE-2026-10520)
Mid-June 2026 Campaign peak
07/10/2026 BLL publishes and null-routes all traffic to PoeLLM C2 servers

The point worth noting: the operator had infrastructure running from 13/04, before CVE-2026-42271 was even disclosed on 20/04, and was exploiting at scale through May, before Horizon3 published the chain on 01/06. The campaign did not follow a research blog; it ran in parallel.

For defenders, that means the gap between "disclosed as high severity, requires authentication" and "actively exploited without authentication" can be zero.

PoeLLM: C2 Hidden in a Poem

The malware is an ELF file named libgcrypt. How it finds its C2 is the most interesting part of the whole report.

The operator created a GitHub repository under the account ejejejdfbbebe, forked from the nodejs.org website source, then added a file named dash.css (a filename absent from the original repo). Inside it sits a two-stanza poem titled On the Nature of Connection.

The malware carries custom logic to extract four words or phrases from the poem, case-insensitively:

Word 1: text between "In the silent hum of " and ","
Word 2: text between "each pulse of " and " threading"
Word 3: text between "Beyond the wall of " and ","
Word 4: find "of distant servers", walk back to the previous whitespace,
        require the 4 bytes before that word to be "the ",
        then take the word after "the"

Those four words are matched against a dictionary hard-coded in the malware body to produce four numbers, combined into the C2's IPv4 address. BLL gives a worked example: a sample found on 23/06/2026 resolved to C2 92.119.165[.]74.

To rotate C2, the operator simply edits a few keywords in the poem on GitHub. Every infected machine derives the new address at its next check. The poem has been changed 11 times since the first commit, each pointing to a new C2. The extraction pattern has never changed — only the keywords.

Malware Capabilities and Self-Expansion

Per BLL's analysis, libgcrypt contains:

  • Remote shell
  • XMRig and Iron miners, connecting to the Kryptex pool
  • HTTP/S scanning
  • Exploit deployment against vulnerable targets

The operationally important part: every infected machine becomes a worker in the exploitation chain. Victims scan the internet for targets, focusing on port 3000 (Gotenberg) and port 4000 (LiteLLM). On finding one, they send a crafted POST request to the vulnerable endpoint, instructing the target to download a file from the C2 on port 81.

After infection, the malware beacons back to C2 on one of ports 3778, 5001, 5002 or 9999.

BLL recently observed victim pools targeting SSH ports and other login portals, suggesting the operator is experimenting with a distributed brute-force framework. Since most targets of that experiment were servers in Italy, BLL assesses the capability is still early in development.

C2 Infrastructure Built on Compromised Routers

Rather than leasing servers, the operator reuses routers with internet-exposed administration pages as C2.

The first C2 (191.37.28[.]160) was a router in Brazil running a Boa web server on port 2222, vulnerable to CVE-2018-21027 and CVE-2018-21028, though BLL found no direct evidence of exploitation. The next (120.224.114[.]212) geolocated to China and was likewise a Boa web server with a router admin page. The pattern repeats across later C2s.

A server in Italy (185.119.19[.]171), previously hosting Prometheus and Uptime Kuma dashboards, received connections from several C2s. BLL assesses with moderate confidence that it is the operator's administrative interface for managing C2 infrastructure and botnet operations.

Attribution

BLL makes no firm attribution, assessing only with moderate confidence that the operator is Italian-speaking, based on three points:

  • Italian-language comments in the malware sample BLL analyzed
  • The administrative server located in Italy
  • The domain malwarescan[.]xyz, registered in February 2026 on an Italian server that the first C2 contacted roughly an hour after the first commit

That is the confidence level to preserve when re-reporting. Language comments in code and server geolocation are each weak indicators on their own.

Analysis

Self-hosted AI infrastructure is a double target. BLL says it plainly: LLM servers are attractive both for the data and credentials they hold and for the powerful GPUs they run on, which suit mining. For a purely financial campaign, a GPU cluster is a bigger prize than any web server.

The /mcp-rest/test/* endpoints are a textbook case of test functionality reaching production. An endpoint that accepts a command and environment variables and spawns a subprocess is reasonable in a development environment and should not exist on an internet-exposed instance. When auditing self-hosted AI tooling, the question to ask first is "does anything here take a command and run it", before "which CVEs affect this version".

Prioritizing patches by original CVSS score is a trap. CVE-2026-42271 was published as high severity and "authenticated". Many organizations would put it in the normal queue. A month later, a flaw in a dependency turned it into unauthenticated CVSS 10.0. A CVE's score does not capture real risk once it sits inside a dependency tree.

Poem-based C2 is not technically hard, but it is hard to take down. There is nothing cryptographically clever here: fixed-pattern string extraction plus a dictionary lookup. The value lies elsewhere. Traffic to GitHub is legitimate in any development environment. The poem contains no IP to scan for or block. Rotating C2 costs one commit, with no need to update malware on 3,400 machines. Take the repo down and the operator creates another.

Using compromised routers as C2 shows cost, not stealth, is the priority. Routers with exposed Boa admin pages are old, easy to take and free. But they leave an obvious trail: BLL partly unravelled the infrastructure through the pattern "every C2 is a router with a vulnerable admin page". A better-resourced group would not do this.

The original report's numbers are inconsistent. BLL's key takeaways say over 3,400 victims while the body still carries "nearly 2,200" in two places. BleepingComputer likewise noted a correction from 2,100 to 3,400 after the report was updated. When citing it, use 3,400 and make clear it is a cumulative victim count, not concurrent activity.

Relevance to Vietnam

  • LiteLLM and Ollama are being stood up widely by internal AI teams. The familiar pattern is a rented GPU server or a lab machine running LiteLLM as a gateway in front of several models, with the port opened to the internet so the team can reach it remotely. That is exactly the configuration PoeLLM scans for.
  • The visible damage is cost, but the bigger risk is credentials. Hijacked GPUs raise electricity bills and slow real workloads. But a LiteLLM proxy typically holds API keys for OpenAI, Anthropic, Google and others, alongside internal keys. Losing those is both direct financial loss and a data risk.
  • Gitea is popular with self-hosting teams in Vietnam because it is lightweight and free. A compromised Gitea means source code and CI secrets in the attacker's hands.
  • AI tooling often sits outside asset management. AI teams build infrastructure at their own pace, usually faster than the security team's review cycle. Many instances are not on the asset list, and therefore not in the patch cycle either.
  • Gotenberg's installation docs explicitly warn against exposing it to the internet. Hundreds of infected servers show that a warning in documentation is not a security control.

Recommendations

  • Patch now: LiteLLM to 1.83.7+ and Starlette to 1.0.1+. Check the whole dependency tree, since Starlette may be pulled in transitively rather than installed directly.
  • If you cannot patch yet: block /mcp-rest/test/connection and /mcp-rest/test/tools/list at the reverse proxy or WAF, restrict network access to trusted ranges, and rotate the credentials the proxy holds.
  • Audit the internet-facing surface of all self-hosted AI and dev tooling: LiteLLM (port 4000), Ollama, Gotenberg (port 3000), Gitea. Add them to the asset inventory and patch cycle like any other service. This outlives the campaign.
  • Hunt for exploitation traces in logs: requests to the two test endpoints, unusual Host header values (a sign of CVE-2026-48710 abuse), and unexpected child processes spawned by the LiteLLM process.
  • Detect post-exploitation behavior: outbound connections to port 81 for file download, beacons to ports 3778, 5001, 5002 or 9999, connections to Kryptex infrastructure, and unexplained GPU or CPU load that does not match known workloads. Add an alert for outbound traffic to ports 3000 and 4000 on other hosts — that means your machine has become a scanner for the botnet.
  • Load the indicators below into SIEM and firewalls, noting the three C2s still active at the time of BLL's publication.

IOCs

Per Black Lotus Labs. BLL maintains an updated IOC set in their GitHub repository.

C2s active at publication (07/10/2026)

92.119.164.50      # since 23/07/2026
103.249.201.108    # since 13/08/2026
178.128.14.204     # since 16/06/2026

Previously used C2s

191.37.28.160      # 13/04 - 16/05/2026 (Brazil router, first C2)
89.39.253.46       # 14/04 - 24/06/2026
120.224.114.212    # 09/05 - 08/09/2026
5.78.73.122        # 08/06 - 22/08/2026
15.204.178.28      # 14/06 - 17/08/2026
92.119.165.74      # 01/07 - 23/07/2026
45.133.73.28       # 18/07 - 22/07/2026
185.132.53.158     # 19/07 - 12/09/2026
136.148.69.233     # 12/09 - 25/09/2026

Administrative and related infrastructure

185.119.19.171     # operator's admin interface (moderate confidence), Italy
57.131.5.211:80    # Italian server hosting malwarescan[.]xyz
malwarescan[.]xyz  # domain registered 02/2026

Mining infrastructure (Kryptex)

5.180.174.162:8029
46.21.245.211:7029    # edge-ru-07[.]kryptex[.]network
kryptex[.]ru          # pool

Malware sample

SHA-256: 6fab94577364beec314afae3b082dd680933f08a8349b9f35b92667e8231b501
Filename: libgcrypt (ELF)

Representative delivery URLs

hxxp://5.78.73[.]122:81/private/python3.6
hxxp://5.78.73[.]122:81/private/bins.sh

The /private/ path pattern on port 81 recurs across other C2s, including 120.224.114[.]212.

Ports to monitor

Port Role
81 C2 serving downloadable payloads
3778, 5001, 5002, 9999 Malware beacon ports
3000 Gotenberg, scanned for targets
4000 LiteLLM, scanned for targets
2222 Boa web server on the first C2 router

GitHub repository hosting the poem

Account:  ejejejdfbbebe
Repo:     fork of the nodejs.org website source
File:     dash.css  (absent from the original repo)
Content:  the poem "On the Nature of Connection"

Related CVEs

CVE-2026-42271    # LiteLLM command injection via MCP test endpoints
CVE-2026-48710    # Starlette "BadHost" Host header validation bypass
CVE-2026-10520    # Ivanti Sentry, entry point of the first victim BLL found
CVE-2018-21027    # router Boa web server, first C2
CVE-2018-21028    # router Boa web server, first C2

References

No comments yet. Be the first to comment.

More from this blog