The operators behind NadMesh are not after your graphics card. They are after the long-lived cloud keys sitting in the .env file next to your self-hosted AI tools. NadMesh is a new Go botnet that scans the internet for exposed inference servers and low-code builders, Ollama, ComfyUI, n8n, Open WebUI, Langflow and Gradio, then reads each host for anything that grants access to a cloud account or a Kubernetes cluster. QiAnXin's XLab, which named and analyzed it, screenshotted an operator dashboard claiming 3,811 unique Amazon Web Services keys. The host is disposable. The account behind it is the prize.
A botnet built like a product
XLab published its analysis on July 10, 2026, and named the malware after an n4d mesh controller string in its code. It is Go-based, shipped in five concurrent build versions, and packed with Garble obfuscation and UPX to slow analysis. The controller listens on ports 80 and 8443, registers each bot with an HMAC-authenticated check-in, and exposes a web management panel with conversion-funnel analytics and automated canary updates. Distinct source IPs sat near zero through late June, then climbed to roughly 139 a day in the first week of July. The panel and the funnel metrics are the tell: this is run for return on investment, not as an experiment, and the operator is measuring conversion the way a growth team would.
Why an exposed Ollama box is a cloud-account problem
What each bot ships home is not the model or the GPU cycles. It reads ~/.aws/config, .env files, and ~/.docker/config.json, and pulls Kubernetes service-account tokens off the host. XLab stated the operator's intent plainly: the goal is not the machine but the cloud credentials and cluster privileges reachable from it.
Here is the part the headlines skip. Self-hosted AI boxes tend to carry broad, long-lived credentials by default, because wiring an inference server or a workflow builder to object storage, a model registry, or a cluster is the fast path, and minting short-lived scoped roles is extra work almost nobody does on a box they think of as internal. So a single read off a five-dollar inference host can equal standing access to the entire cloud account that host can reach. The credential is flat, the blast radius is not. That asymmetry is what turns a hobby GPU server into an incident that starts in your cloud bill.
Exposure is the default, not a mistake
The target list is not obscure software. These are the tools a team stands up in an afternoon:
-
ComfyUI (default port 8188), a node-based image-generation server.
-
Ollama (11434), a local large-language-model runner.
-
n8n (5678), a low-code workflow automation builder.
-
Gradio (7860), the demo-app framework behind many model front ends.
-
Open WebUI and Langflow, a chat front end and a visual agent builder.
A Shodan-fed harvester keeps NadMesh's scan queue stocked with these, and subnets that produce hits get resampled more densely every few minutes. The uncomfortable truth is that several of these tools historically bind to 0.0.0.0 and ship without authentication enabled by default, so exposure can be their default posture rather than a misconfiguration an operator consciously opted into. That is why "remember to firewall it" is not a control you can trust across a team of people moving fast. The reliable fix is placement: keep these services off the public internet, behind a VPN or an authenticating reverse proxy, and treat any instance that genuinely must be reachable as hostile-facing from day one. We have watched this exact pattern play out one product at a time, from a crawler that shipped its server wide open by default to an exposed Langflow instance an AI agent turned into ransomware. NadMesh is what happens when someone industrializes the search for all of them.
MCP is now a ranked attack surface
The newest wrinkle is the one getting the least coverage. XLab records that the controller ranks Model Context Protocol (MCP) command execution at the top of its exploitation priority, above Kubernetes, Docker API and Redis. MCP is the glue that lets AI agents call external tools, and the vector NadMesh uses is a JSON-RPC tools/call to an execute_command tool. Teams treat MCP servers like localhost developer conveniences, then expose them for a demo and forget. That priority ordering is a signal worth internalizing: attackers now rank an agent's own tool interface as one of the softest remote-code paths on the internet. If you run an MCP server reachable beyond loopback, you are running a remote command interface, and you should threat-model it as one.
It also reuses old, unpatched CVEs
NadMesh does not only hunt exposed dashboards. XLab's analysis lists a set of older, known flaws the controller weaponizes against whatever else it finds while scanning.
| CVE | Product | Affected | Flaw class |
|---|---|---|---|
| CVE-2026-39987 | Marimo notebooks | < 0.23.0 | Pre-auth RCE |
| CVE-2026-41176 | rclone remote control | 1.45.0 to 1.73.5 | Auth bypass |
| CVE-2022-22947 | Spring Cloud Gateway | Actuator exposed | Actuator RCE |
| CVE-2017-12611 | Apache Struts 2 | Freemarker tags | Template RCE |
None of these is a zero-day, and every one has a fix. They keep working because the internet is full of unpatched Apache Struts and Spring instances years after the advisory. By XLab's measured exploit traffic, the bulk lands on open Docker container APIs (about 30 percent) and Jenkins script consoles (about 22 percent), with Redis, weak Telnet, and MCP command execution making up the rest. The lesson is boring and correct: an exposed, unpatched service from 2017 is worth the same to this operator as a shiny AI endpoint, because both hand over the same cloud keys.
Evict before you rotate the keys
On a host it lands on, NadMesh installs triple persistence: an SSH public-key backdoor in ~/.ssh/authorized_keys, disk-backed loaders in paths like /dev/shm and /var/tmp, and a cron watchdog that revives the bot if any single piece is removed. That detail changes the order of your response. If you rotate the exposed cloud keys but leave the implant running, the watchdog and the key backdoor let it re-harvest the replacements on the next cycle, and you have burned a rotation for nothing. Evict first, then rotate.
Concretely, for a host you suspect: pull it offline or block its egress, remove the unfamiliar authorized_keys entry and the cron jobs, kill the loaders, and only then reissue credentials. Before you trust the cloud account again, audit its logs for what the old keys touched during the exposure window. Assume lateral movement into the cluster if a service-account token was reachable.
For detection, do not chase the binary. It is Garble-obfuscated, UPX-packed, and shipped in five variants, so hash-based blocking loses. The durable signals are behavioral: a new key appearing in authorized_keys, a cron entry that restarts a process, JSON-RPC tools/call traffic arriving from a non-loopback source, and outbound connections from an inference box that has no reason to talk to the wider internet. A managed detection service watching host behavior, file integrity on authorized_keys, new scheduled tasks, and unexpected egress, catches this class of implant even when the malware itself is unknown to every scanner. That is the whole point of watching what a host does rather than what a file hashes to, and it is the core of any real threat hunting program on a self-hosted fleet.
The framing to take away is simple. The self-hosted AI box you stood up to save money is a credential store sitting on the public internet, and there is now an industrial-grade botnet built to find every one of them and cash out the cloud account behind it. Price the box accordingly.