# Carbonato botnet: AI-agent-driven worm hijacks unauthenticated Docker daemons on port 2375 and installs the Hermes Agent 'GH0ST' implant

> Carbonato is a worm-like botnet that compromises Linux hosts running Docker daemons whose Engine API is exposed without authentication on TCP/2375. It uses the exposed API to launch a privileged container with the host filesystem bind-mounted and host PID/network namespaces, escapes to the host via nsenter, opens a reverse SSH tunnel to a Costa Rica relay (AS262145), installs persistence across cron, systemd timers, rc.local and OpenRC, and installs the open-source Hermes Agent framework reconfigured through an overwritten SOUL.md persona file that renames the agent 'GH0ST' and makes it execute operator tasks delivered over Telegram. It self-propagates by sweeping every attached network and Docker bridge for other exposed daemons on port 2375 every five minutes.

- **Published:** 2026-09-24T00:00:00Z
- **Last reviewed:** 2026-09-24T00:00:00Z
- **Canonical:** https://intel.threadlinqs.com/threat/TL-2026-2639
- **Card image:** https://intel.threadlinqs.com/og/threat/threat-intel-carbonato-docker-worm-tl-2026-2639.webp?v=59f5cca3 (Threadlinqs threat intel: Carbonato Botnet Docker Daemon Worm: Detection & IOCs (TL-2026-2639) — High severity, 22 IOCs, 9 detection rules.)
- **ID:** TL-2026-2639
- **Severity:** HIGH
- **Category:** MALWARE
- **Status:** ACTIVE
- **Actor:** Carbonato operator
- **Detections:** 9 (1 shown free below) · **IOCs:** 22 (3 shown free below)

## Description

OVERVIEW. Carbonato is a Linux botnet that requires no software vulnerability to infect a host: its only prerequisite is a Docker daemon whose Engine API is reachable without authentication, most commonly because it has been bound to TCP port 2375 with no TLS and no auth plugin. ThreatDown (Malwarebytes) documented the campaign from an unauthenticated Docker registry the operators left exposed on port 5000 of a US-based server, recovering 59 repositories, 234 image tags, 605 SHA-256-verified blobs, 4.3 GB of image data and roughly 945,000 indexed files in one day of passive, read-only collection. Artifact timestamps in that archive span October 2024 to August 2026 and cover two linked product lines: a factory distributing trojanized cryptocurrency wallet applications and the Docker botnet itself. The exposed image configuration JSON leaked environment variables, entrypoints and full build history, which is how researchers recovered C2 addresses, Telegram bot tokens and the LLM gateway password.

INITIAL ACCESS AND CONTAINER ESCAPE. The implant searches for Docker daemons that accept unauthenticated connections, then drives the daemon through its own HTTP API to create a privileged container named netns-probe from alpine:latest with Privileged=true, Binds=[/:/host], PidMode=host and NetworkMode=host, and starts it via POST /containers/<id>/start. Command execution on the host is then achieved with the Docker exec API running nsenter -t 1 -m -u -n -i sh -c id, which enters the host's mount, UTS, network and IPC namespaces from inside the container. Because the host root filesystem is already bind-mounted at /host and the container shares the host PID and network namespaces, this is a straight container escape to full host control with no kernel exploit required. Thousands of hosts with reachable unauthenticated daemons make this a viable mass-infection primitive rather than a targeted technique.

HOST HOLDING, PERSISTENCE AND MASQUERADING. The implant kit entry.sh (observed at v5.3) opens a reverse SSH tunnel to a relay in Costa Rica, with the remote port derived deterministically from the MD5 hash of the victim's own IP address. It installs an SSH server on the victim and adds the operators' key, then sends a Telegram deployment report containing the container ID, hostname, IP address and country, written in voseo Spanish. Persistence is established through cron jobs, systemd timers, rc.local and OpenRC hooks, after which the files are marked immutable. Paired watchdog scripts (auto-persist-host.sh) monitor the deployment and re-pull the implant from the operators' registry if files or the container disappear. The deployment is disguised as a legitimate system component: the container is named systemd-resolved and serves a fake systemd-networkd resolver v2.0 banner, process arguments imitate the kernel worker thread form [kworker/u2:0], and a miner is planted at /usr/sbin/systemd-logind to look like a system service.

THE AI AGENT IMPLANT. The campaign's distinguishing feature is that its command-and-control loop is an LLM agent. Hermes Agent, an MIT-licensed open-source agent framework from Nous Research, is installed on the compromised host unmodified. The implant entrypoint then overwrites its SOUL.md persona file with a 39-line prompt that names the agent GH0ST and instructs it to maintain persistence and obey instructions received over Telegram. Operators send tasks into a Telegram chat (chat 750752697, which also receives the deployment reports); the model interprets the task, writes terminal commands, reads the output and decides what to do next, returning its report to the same chat. Documented loot priorities rank AI API keys above SSH credentials, access tokens and databases, with named providers including OpenAI, Anthropic, Google/Gemini, OpenRouter, Together, Groq, Mistral, Cohere, LocalAI, Ollama, vLLM, LiteLLM and One API; loot rules require every key, password and token to be stored in plaintext under /root/.hermes/loot/. The operators also ran an LLM gateway on a free tier that advertised 12 models while serving 27 through its API, fronted by proxies at carbonato-proxy-{drab,zeta,zeta-2}.vercel.app.

WORM PROPAGATION. Every five minutes the implant enumerates the host's attached networks and Docker bridges, sweeps each /24 on port 2375, verifies that the responding service is Docker, skips hosts it has already infected, and then repeats the full deployment against the new victim through a container named net-setup that pulls the system/resolved image. The AI agent plays no role in this phase: propagation is deterministic scripting, which is what makes the botnet grow without operator involvement.

INFRASTRUCTURE AND ATTRIBUTION. Recovered infrastructure spans several hosting providers: a C2 hub at 45.79.183.61 (Linode), an earlier 'fsociety'-era C2 at 91.99.195.164 (Hetzner), a beacon and reverse-shell host at 213.136.79.115 (Contabo, ports 8080 and 4444), a live LLM gateway at 213.136.83.197 (Contabo), and the reverse-tunnel sink at 190.211.124.187 in AS262145 (Costa Rica). The registry fleet ran across seven endpoints in AS40065, six of which were still online on 3 September 2026. ThreatDown could not link Carbonato to any known threat cluster; the attribution assessment to a Costa Rica-based operator rests on three converging indicators - reports written in voseo Spanish, the Telegram handle Carbo506 (+506 being Costa Rica's calling code), and the fact that 14 of 162 image configurations carry the UTC-06:00 America/Costa_Rica timezone - together with the AS262145 tunnel sink. A BeaconBeagle query against the published C2 IPs on 24 September 2026 returned no beacon or C2 configuration records, so the network infrastructure is not currently correlated with known C2 frameworks in that dataset.

DETECTION AND DEFENSIVE VALUE. Because no exploit or malware binary has to reach the host, prevention is configuration: the Docker daemon must never be exposed on a routable interface without TLS and client authentication, and registries must require authentication. On the host, the highest-fidelity indicators are a /root/.hermes/SOUL.md containing the GH0ST agent name, a .env carrying CARBONATO_API_KEY, unexplained Telegram egress, reverse SSH tunnels terminating in AS262145 on ports derived from the MD5 of the host's own IP, the watchdog at /usr/local/bin/.docker-network-monitor, processes with [kworker/...] style arguments, a miner masquerading as /usr/sbin/systemd-logind, and immutable bits set on files that should not be locked.

## MITRE ATT&CK

- T1190 Exploit Public-Facing Application
- T1610 Deploy Container
- T1059.004 Unix Shell
- T1611 Escape to Host
- T1053.003 Cron
- T1053.006 Systemd Timers
- T1037.004 RC Scripts
- T1036.005 Match Legitimate Resource Name or Location
- T1222.002 Linux and Mac Permissions
- T1572 Protocol Tunneling
- T1102.002 Bidirectional Communication
- T1021.004 SSH
- T1046 Network Service Discovery
- T1552.001 Credentials In Files
- T1496 Resource Hijacking
- AML.T0053 AI Agent Tool Invocation

## Free detection (Splunk SPL, Microsoft KQL, Sigma)

**Carbonato — Host Persistence, Immutability Locking and Systemd Masquerading Artifacts** (`TL-2026-2639-SPL-002`)

Detects Carbonato's host-holding layer. After escaping the container the implant installs persistence across four independent mechanisms simultaneously — cron jobs, systemd timers, rc.local and OpenRC init hooks — then marks the files immutable with chattr +i so that removal attempts fail. It drops a hidden watchdog at /usr/local/bin/.docker-network-monitor plus a paired auto-persist-host.sh script that re-pulls the implant from the operators' registry whenever files or the container disappear, overwrites /root/.hermes/SOUL.md with the 39-line GH0ST agent persona, plants a cryptominer at /usr/sbin/systemd-logind to impersonate the legitimate systemd-logind service, and leaves an implant kit at /opt/gh0st/. This single search covers the full persistence-and-masquerading surface because defenders need one alert that fires on any of these artifacts.

- **Languages:** Splunk SPL, Microsoft KQL, Sigma
- **ATT&CK:** T1053.003, T1053.006, T1037.004, T1222.002, T1036.005, T1552.001, T1496
- **Data sources:** linux_audit, syslog, DeviceFileEvents, DeviceProcessEvents
- **Severity:** high
- **Confidence:** high

### Splunk SPL

```spl
index=linux sourcetype="linux_audit" (type=PATH OR type=SYSCALL OR type=EXECVE)
| eval fpath=coalesce(name, path, file, proc_exe, exe, "")
| eval cmdline=coalesce(mvjoin(akey," "), proc_cmdline, cmd, _raw)
| where match(fpath,"(?i)^/etc/(cron\.d|crontab|cron\.(daily|hourly|weekly|monthly)|systemd/system/[^/]+\.(service|timer)|rc\.local|init\.d/|rc[0-9]\.d/)")
     OR match(fpath,"(?i)(\.docker-network-monitor|auto-persist-host\.sh|/root/\.hermes/(SOUL\.md|\.env|loot/)|/usr/sbin/systemd-logind|/opt/gh0st/)")
     OR match(cmdline,"(?i)chattr\s+\+i\b")
| eval artifact=case(
     match(fpath,"(?i)/root/\.hermes/SOUL\.md"), "GH0ST agent persona override (highest fidelity)",
     match(fpath,"(?i)(\.docker-network-monitor|auto-persist-host\.sh)"), "Carbonato watchdog / re-pull script",
     match(fpath,"(?i)/usr/sbin/systemd-logind"), "cryptominer masquerading as systemd-logind",
     match(fpath,"(?i)/root/\.hermes/(\.env|loot/)"), "CARBONATO_API_KEY / plaintext credential loot store",
     match(fpath,"(?i)/opt/gh0st/"), "Carbonato implant kit entry.sh",
     match(cmdline,"(?i)chattr\s+\+i"), "file immutability set (T1222.002)",
     "persistence path write")
| stats count AS events, min(_time) AS first_seen, max(_time) AS last_seen, values(artifact) AS artifacts, values(fpath) AS paths, values(cmdline) AS commands BY host
| eval first_seen=strftime(first_seen,"%Y-%m-%dT%H:%M:%S"), last_seen=strftime(last_seen,"%Y-%m-%dT%H:%M:%S")
| sort - events
```

### Microsoft KQL

```kql
let lookback = 24h;
let persistence_files = DeviceFileEvents
| where Timestamp > ago(lookback)
| where ActionType in ("FileCreated","FileModified","FileRenamed")
| where FolderPath has_any ("/etc/cron.d","/etc/crontab","/etc/cron.daily","/etc/cron.hourly","/etc/systemd/system","/etc/rc.local","/etc/init.d","/etc/rc3.d","/etc/rc5.d")
    or FolderPath has_any ("/root/.hermes","/usr/local/bin","/opt/gh0st") or FileName =~ "auto-persist-host.sh"
| extend Artifact = case(
    FileName =~ "SOUL.md" and FolderPath has ".hermes", "GH0ST agent persona override",
    FileName has_any ("docker-network-monitor","auto-persist-host.sh"), "Carbonato watchdog / re-pull script",
    FileName =~ "systemd-logind" and FolderPath has "/usr/sbin", "cryptominer masquerading as systemd-logind",
    FolderPath has "/opt/gh0st", "Carbonato implant kit",
    "persistence path write")
| project Timestamp, DeviceName, AccountName, FolderPath, FileName, Artifact, InitiatingProcessFileName, InitiatingProcessCommandLine;
let immutable_set = DeviceProcessEvents
| where Timestamp > ago(lookback)
| where FileName =~ "chattr" and ProcessCommandLine has "+i"
| extend Artifact = "file immutability set (T1222.002)"
| project Timestamp, DeviceName, AccountName, FolderPath = "", FileName = FileName, Artifact, InitiatingProcessFileName, InitiatingProcessCommandLine;
let masquerade_proc = DeviceProcessEvents
| where Timestamp > ago(lookback)
| where ProcessCommandLine has_any ("[kworker/u2:0]","[kworker/u2","[kworker")
| extend Artifact = "process masquerading as kernel worker thread (T1036.005)"
| project Timestamp, DeviceName, AccountName, FolderPath = "", FileName = FileName, Artifact, InitiatingProcessFileName, InitiatingProcessCommandLine;
persistence_files
| union immutable_set, masquerade_proc
| sort by Timestamp desc
```

### Sigma

```yaml
title: Carbonato - Host Persistence, Immutability and Systemd Masquerading Artifacts
description: Detects Carbonato host-holding artifacts - writes to cron, systemd timer, rc.local and OpenRC persistence paths, chattr +i immutability locking, the .docker-network-monitor watchdog, the auto-persist-host.sh re-pull script, the GH0ST Hermes Agent persona file, a cryptominer planted at /usr/sbin/systemd-logind, and processes whose arguments imitate a kernel worker thread.
id: 3b7d9e52-16c8-4a73-b0d4-5e91c2f60a37
status: experimental
references:
  - https://www.threatdown.com/blog/carbonato/
  - https://attack.mitre.org/techniques/T1053/003/
  - https://attack.mitre.org/techniques/T1053/006/
  - https://attack.mitre.org/techniques/T1037/004/
  - https://attack.mitre.org/techniques/T1222/002/
author: Threadlinqs Team
date: 2026-09-24
tags:
  - attack.persistence
  - attack.defense_evasion
  - attack.stealth
  - attack.t1053.003
  - attack.t1053.006
  - attack.t1037.004
  - attack.t1222.002
  - attack.t1036.005
  - attack.t1552.001
  - attack.tool.carbonato
logsource:
  product: linux
  category: file_event
detection:
  selection_carbonato_artifacts:
    TargetFilename|endswith:
      - '/.docker-network-monitor'
      - '/auto-persist-host.sh'
      - '/.hermes/SOUL.md'
      - '/.hermes/.env'
      - '/opt/gh0st/entry.sh'
  selection_persistence_paths:
    TargetFilename|startswith:
      - '/etc/cron.d/'
      - '/etc/cron.daily/'
      - '/etc/cron.hourly/'
      - '/etc/systemd/system/'
      - '/etc/init.d/'
      - '/etc/rc.local'
    TargetFilename|contains: '.timer'
  selection_immutability:
    Image|endswith: '/chattr'
    CommandLine|contains: '+i'
  selection_kworker_masquerade:
    CommandLine|re: '^\[kworker/[a-z0-9]+:[0-9]+\]\s*$'
  condition: selection_carbonato_artifacts or selection_persistence_paths or selection_immutability or selection_kworker_masquerade
falsepositives:
  - Configuration-management tooling (Ansible, Puppet, Chef, Salt) legitimately writing systemd units or cron entries - baseline the paths those agents manage
  - Package upgrades that legitimately set the immutable attribute on a small set of system files
  - Any kernel worker thread will appear as [kworker/...] in ps output - this rule matches only when the process command line is literally the bracketed string, which real kernel threads do not expose as a userland process
level: high
```

**Known false positives:**

- Configuration-management agents (Ansible, Puppet, Chef, Salt) that legitimately write cron entries and systemd units
- Package manager upgrades that legitimately set immutable attributes on a small set of system binaries
- A real kernel worker thread is not visible as a userland process with a bracketed command line, so [kworker/...] appearing in process-creation telemetry is not a benign kernel artifact

**Tuning notes:** Baseline the exact set of files each configuration-management agent manages and exclude those paths from the cron and systemd branches; keep the Carbonato-specific artifact list (.docker-network-monitor, auto-persist-host.sh, /opt/gh0st/, /root/.hermes/) unexcluded because none of those paths exist on a healthy host. Enable auditd watch rules with -w on /etc/cron.d, /etc/systemd/system, /etc/rc.local, /etc/init.d, /usr/local/bin and /usr/sbin with -p wa so the PATH records carry the target filename. Alert at high priority on SOUL.md, .docker-network-monitor and auto-persist-host.sh alone; treat a bare chattr +i as medium until it correlates with one of the other artifacts within the same hour. Add an lsattr sweep as a weekly scheduled hunt to catch files locked before auditd coverage began.

### 8 more detection rules (Splunk SPL, Microsoft KQL, Sigma), unlock with TL Creds

- Carbonato — Unauthenticated Docker Engine API Exploitation and Privileged Container Escape (Defender for Cloud / Sentinel) (locked: unlock with TL Creds)
- Carbonato — GH0ST Hermes Agent Persona Override, Watchdog Persistence and Credential Loot Staging (locked: unlock with TL Creds)
- Carbonato — Telegram Agent C2 Channel, Reverse SSH Relay and Outbound Docker API Worm Propagation (locked: unlock with TL Creds)
- Sigma — Carbonato Docker Engine API Abuse: Privileged Container with Host Root Mount and nsenter Escape (locked: unlock with TL Creds)
- Sigma — Carbonato Host Persistence: Immutable Cron/Systemd/RC Hooks, Watchdog and Systemd-Impersonating Miner (locked: unlock with TL Creds)
- Sigma — Carbonato C2: Telegram Agent Channel, Costa Rica Reverse SSH Relay and Docker API Worm Sweep (locked: unlock with TL Creds)
- Carbonato — Privileged Container Escape via Unauthenticated Docker Engine API (nsenter into host PID 1) (locked: unlock with TL Creds)
- Carbonato — Reverse SSH Tunnel to [indicator], Telegram C2 Egress and Port-2375 Worm Sweep (locked: unlock with TL Creds)

Sign up free to unlock the detection rules: https://threadlinqs.com/?modal=signup

## Sample indicators (3 of 22)

Defanged, TLP:CLEAR.

- IP address: `45.79.183[.]61`
- Domain: `carbonato-proxy-drab[.]vercel[.]app`
- IP address: `91.99.195[.]164`

Full set of 22 indicators unlocks with TL Creds: https://threadlinqs.com/tl-creds

## Sources

- [CARBONATO: a botnet built around an AI agent (ThreatDown / Malwarebytes)](https://www.threatdown.com/blog/carbonato/)
- [New Carbonato malware uses AI agents to hijack exposed Docker hosts (BleepingComputer)](https://www.bleepingcomputer.com/news/security/new-carbonato-malware-uses-ai-agents-to-hijack-exposed-docker-hosts/)
- [Docker documentation - Protect the Docker daemon socket (why an unauthenticated TCP API on 2375 is dangerous)](https://docs.docker.com/engine/security/protect-access/)
- [Docker Engine API reference (the API the campaign drives to create privileged containers)](https://docs.docker.com/reference/api/engine/)
- [Nous Research Hermes Agent - open-source MIT-licensed agent framework abused as the implant](https://github.com/NousResearch/hermes-agent)
- [Hermes Agent documentation - messaging gateways, terminal backends and skill/persona model](https://hermes-agent.nousresearch.com/docs/)
- [MITRE ATT&CK T1610 - Deploy Container](https://attack.mitre.org/techniques/T1610/)
- [MITRE ATT&CK T1611 - Escape to Host](https://attack.mitre.org/techniques/T1611/)
- [MITRE ATT&CK T1102.002 - Web Service: Bidirectional Communication (Telegram as C2)](https://attack.mitre.org/techniques/T1102/002/)
- [MITRE ATT&CK T1496 - Resource Hijacking](https://attack.mitre.org/techniques/T1496/)
- [MITRE ATT&CK T1037.004 - Boot or Logon Initialization Scripts: RC Scripts](https://attack.mitre.org/techniques/T1037/004/)
- [MITRE ATT&CK T1552.001 - Unsecured Credentials: Credentials In Files](https://attack.mitre.org/techniques/T1552/001/)
- [MITRE ATLAS - Adversarial Threat Landscape for AI Systems (technique set including AML.T0103 Deploy AI Agent, AML.T0081 Modify AI Agent Configuration, AML.T0083 Credentials from AI Agent Configuration and AML.T0086 Exfiltration via AI Agent Tool Invocation)](https://atlas.mitre.org/)

## Full data

The other 8 detection rules (Splunk SPL / Microsoft KQL / Sigma) unlock with TL Creds; a free account includes daily creds: https://threadlinqs.com/?modal=signup
Indicator values unlock with TL Creds, at a lower price on Red and Purple; AI agents can query rules and IOCs through the Threadlinqs MCP server (Purple): https://intel.threadlinqs.com/mcp

Canonical: https://intel.threadlinqs.com/threat/TL-2026-2639
