Back to Intelligence

Carbonato Botnet Hijacks Exposed Docker Daemons to Deploy Telegram-Controlled Hermes AI Agent — Detection and Remediation Guide

SA
Security Arsenal Team
September 28, 2026
13 min read

ThreatDown researchers have disclosed a new compromised-device network tracked as Carbonato that is actively scanning for and compromising exposed Docker daemon APIs, then using that foothold to deploy Hermes Agent — a legitimate open-source AI agent framework — as an autonomous implant. The campaign's tradecraft is notable not because the tooling is sophisticated, but because it isn't: Carbonato installs Hermes Agent completely unchanged, then overwrites a single file — the framework's SOUL.md persona prompt — with a 39-line instruction set that directs the agent to execute tasks received over a Telegram-based command-and-control channel.

This is an important moment for defenders. We've spent years watching botnets drop Mirai variants, cryptominers, and custom Go implants onto exposed container infrastructure. Carbonato represents a shift: adversaries are now repurposing off-the-shelf AI agent frameworks as living-off-the-land implants. The agent binary is legitimate open-source code. It will not match malware signatures. Its 'maliciousness' lives entirely in a markdown prompt file and a Telegram bot token. If your detection strategy depends on hashing known-bad executables, you will miss this.

If you operate Docker hosts — especially build servers, CI runners, or development boxes with the daemon socket bound to a network interface — assume you are in scope. The remainder of this post breaks down the attack chain, gives you production-ready Sigma, KQL, and Velociraptor detections, and walks through containment and hardening.

Technical Analysis

What Is Being Targeted

Carbonato targets Docker hosts with the daemon API exposed to the network — typically TCP port 2375 (unencrypted HTTP) or misconfigured TCP 2376 (TLS expected, but frequently deployed without certificate verification). This is one of the oldest self-inflicted wounds in container security: binding dockerd to tcp://0.0.0.0:2375 for remote management convenience. An unauthenticated, unencrypted Docker API is functionally equivalent to unauthenticated root on the host — anyone who can reach the port can create a container, mount the host filesystem, and execute arbitrary code as root inside a privileged context.

Affected platforms: any Linux host running Docker Engine, Docker Swarm nodes, containerd-based hosts fronted by the Docker API, and cloud VM images or developer workstations where the daemon socket has been exposed. CI/CD infrastructure is disproportionately represented because teams expose the daemon for Docker-in-Docker build pipelines.

No CVE is associated with this campaign — and that's the point. There is no vulnerability being exploited. Carbonato abuses a documented management interface left open by misconfiguration, which means patching alone will not save you. Only configuration hygiene and network segmentation will.

Attack Chain, From the Defender's Seat

  1. Discovery: Carbonato scans the internet for hosts listening on Docker API ports (2375/2376). Shodan and Censys trivially enumerate these; the botnet's own scanners do the same.
  2. Access: The operator (or automated logic) issues unauthenticated API calls — GET /version for reconnaissance, then POST /containers/create and POST /containers/{id}/start to deploy a workload. Typical payloads mount the host root filesystem (/:/host) or run with --privileged to escape the container boundary.
  3. Implant delivery: The Hermes Agent framework is installed byte-for-byte unchanged from its public open-source distribution. No packing, no obfuscation, no modified code.
  4. Persona overwrite: The implant replaces Hermes Agent's SOUL.md persona file with a 39-line malicious prompt. In agentic frameworks, the persona/system prompt defines the agent's goals and permitted behavior. By overwriting it, the attacker converts a benign general-purpose agent into a task executor loyal to the botnet — without touching a single line of executable code.
  5. Command and control: Tasking is delivered through Telegram. The agent polls or receives messages from a Telegram bot/channel, interprets the tasking per its injected persona, executes instructions on the host, and returns results. Telegram C2 blends into legitimate HTTPS traffic to api.telegram.org (TCP 443), bypassing many egress filters and reputation-based detections.

Why This Matters More Than a Typical Docker Botnet

  • Signature evasion by design: the executable is legitimate open-source software. EDR file-reputation and hash-based blocklists are blind to it.
  • Mutable intent: tasking is natural-language prompt-driven. The operator can change objectives — cryptomining today, credential harvesting tomorrow, lateral movement next week — by editing a prompt, not by redeploying a binary.
  • Legitimate C2 infrastructure: Telegram's API endpoints are widely allowlisted in enterprise egress policies.
  • Autonomous execution: an agent framework can chain tools (shell execution, file I/O, network requests) without further operator interaction, increasing dwell-time damage between tasking intervals.

Exploitation status: Confirmed active in-the-wild exploitation of exposed Docker daemons by the Carbonato network, per ThreatDown's disclosure. There is no public PoC exploit because none is needed — the Docker API is the 'exploit.' At the time of writing there is no CISA KEV entry, as no software CVE exists. Treat exposure as an active-compromise risk, not a theoretical one.

Detection & Response

The detections below target the observable behaviors Carbonato cannot avoid: remote Docker API abuse, suspicious container creation, the SOUL.md overwrite, Hermes Agent execution artifacts, and Telegram egress from server workloads.

Sigma Rules

YAML
---
title: Exposed Docker Daemon Remote Container Creation Activity
translation: Detects evidence of remote interaction with the Docker daemon API and container launches initiated by non-standard parent processes, consistent with Carbonato-style abuse of exposed Docker endpoints.
id: 3f8c2a91-7d4e-4b6a-9c1f-2e5d8a6b0f31
status: experimental
description: Detects shells and scripting interpreters spawned directly by dockerd or containerd-shim, a hallmark of remote Docker API container execution abuse.
references:
  - https://thehackernews.com/2026/09/carbonato-botnet-compromises-docker.html
  - https://attack.mitre.org/techniques/T1610/
author: Security Arsenal
date: 2026/09/15
tags:
  - attack.execution
  - attack.t1610
  - attack.t1059
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/dockerd'
      - '/containerd-shim'
      - '/containerd-shim-runc-v2'
  selection_child:
    Image|endswith:
      - '/sh'
      - '/bash'
      - '/dash'
      - '/curl'
      - '/wget'
      - '/python3'
      - '/python'
      - '/node'
  condition: selection_parent and selection_child
falsepositives:
  - Legitimate container entrypoints that invoke shells (common in CI pipelines); tune by container image name and orchestrator context
level: high
---
title: Hermes Agent SOUL.md Persona File Overwrite
translation: Detects modification of the SOUL.md persona file used by the Hermes Agent framework, the specific file Carbonato overwrites to weaponize the agent.
id: 9b1e4c72-3a5f-4d8e-b6c2-1f7a9d0e3c84
status: experimental
description: Detects creation or modification of SOUL.md files, the persona prompt file overwritten by the Carbonato implant to redirect Hermes Agent behavior.
references:
  - https://thehackernews.com/2026/09/carbonato-botnet-compromises-docker.html
  - https://attack.mitre.org/techniques/T1105/
author: Security Arsenal
date: 2026/09/15
tags:
  - attack.defense_evasion
  - attack.t1105
  - attack.t1565.001
logsource:
  category: file_event
  product: linux
detection:
  selection:
    TargetFilename|endswith:
      - 'SOUL.md'
      - '/SOUL.md'
  condition: selection
falsepositives:
  - Legitimate Hermes Agent deployments updating their persona; alert on hosts where Hermes is not an approved workload
level: high
---
title: Telegram API Egress From Server Process
translation: Detects outbound connections to Telegram API infrastructure from processes on Linux servers, consistent with Carbonato's Telegram-based C2 channel.
id: 5d7f0a38-2c9b-4e1a-8f6d-0b3e7c5a9126
status: experimental
description: Detects network connections to api.telegram.org from shell, scripting, or agent processes on Linux servers where Telegram has no legitimate business use.
references:
  - https://thehackernews.com/2026/09/carbonato-botnet-compromises-docker.html
  - https://attack.mitre.org/techniques/T1102/
author: Security Arsenal
date: 2026/09/15
tags:
  - attack.command_and_control
  - attack.t1102.002
logsource:
  category: network_connection
  product: linux
detection:
  selection_dest:
    DestinationHostname|contains:
      - 'api.telegram.org'
      - 'core.telegram.org'
      - '149.154.16'
  selection_proc:
    Image|endswith:
      - '/python3'
      - '/python'
      - '/node'
      - '/sh'
      - '/bash'
  condition: all of selection_*
falsepositives:
  - Legitimate alerting/notification scripts using Telegram bots; restrict to server VLANs where Telegram is not sanctioned
level: medium

KQL — Microsoft Sentinel / Defender

This query assumes Docker host syslog (daemon logs and auditd/execve telemetry) is ingested into Sentinel, plus Defender network events where available. It hunts for the full Carbonato behavior chain: unauthenticated Docker API access, suspicious container creation with host mounts, the SOUL.md overwrite, and Telegram egress.

KQL — Microsoft Sentinel / Defender
// Carbonato hunt: exposed Docker API abuse, SOUL.md overwrite, Telegram C2
let Lookback = 14d;
let TelegramIndicators = dynamic(["api.telegram.org", "core.telegram.org", "149.154.160.0/20"]);
// 1. Docker daemon serving remote/unauthenticated API requests and container lifecycle events
union isfuzzy=true
    (Syslog
    | where TimeGenerated > ago(Lookback)
    | where ProcessName has_any ("dockerd", "containerd")
    | where SyslogMessage has_any ("containers/create", "/containers/", "POST /v1.")
    | where SyslogMessage has_any ("Binds", "privileged", "/:/host", "HostConfig")
    | project TimeGenerated, Computer, ProcessName, SyslogMessage, HuntStage="Docker API container creation"),
// 2. Shells/scripting spawned under dockerd or containerd-shim (remote exec indicator)
    (Syslog
    | where TimeGenerated > ago(Lookback)
    | where SyslogMessage has_all ("dockerd", "exec") or (ProcessName =~ "containerd-shim" and SyslogMessage has_any ("/bin/sh", "/bin/bash", "python3", "curl", "wget"))
    | project TimeGenerated, Computer, ProcessName, SyslogMessage, HuntStage="Container shell/script execution"),
// 3. SOUL.md persona file write on the host
    (Syslog
    | where TimeGenerated > ago(Lookback)
    | where SyslogMessage contains "SOUL.md"
    | project TimeGenerated, Computer, ProcessName, SyslogMessage, HuntStage="Hermes SOUL.md artifact"),
// 4. Telegram egress from server workloads (via CEF firewall/proxy logs)
    (CommonSecurityLog
    | where TimeGenerated > ago(Lookback)
    | where DestinationHostName has_any ("api.telegram.org", "core.telegram.org")
       or DestinationIP startswith "149.154."
    | project TimeGenerated, SourceIP, DestinationIP, DestinationHostName, RequestURL, HuntStage="Telegram API egress"),
// 5. Defender for Endpoint network events to Telegram from non-user processes
    (DeviceNetworkEvents
    | where TimeGenerated > ago(Lookback)
    | where RemoteUrl has_any ("api.telegram.org", "core.telegram.org")
    | where InitiatingProcessFileName !in~ ("msedge.exe", "chrome.exe", "firefox.exe", "Telegram.exe")
    | project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemoteUrl, HuntStage="Telegram C2 (non-browser)")
| order by TimeGenerated asc

Velociraptor VQL

Use this hunt artifact against suspected Linux Docker hosts to pull live state: container-spawned processes, Telegram connections, and the SOUL.md artifact on disk.

VQL — Velociraptor
-- Carbonato hunt: container-spawned processes, Telegram C2 connections, SOUL.md artifacts
SELECT * FROM chain(
-- Stage 1: processes parented to container runtime or matching agent interpreters
a={
  SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
  FROM pslist()
  WHERE CommandLine =~ '(?i)hermes|telegram|SOUL\.md'
     OR Exe =~ '(?i)(python3?|node|sh|bash)$'
},
-- Stage 2: established connections to Telegram infrastructure
b={
  SELECT Pid, Name, Family, Type, Status, Laddr, Raddr
  FROM netstat()
  WHERE Raddr.IP =~ '^149\.154\.' OR Status =~ 'ESTABLISHED'
},
-- Stage 3: SOUL.md persona files on disk with content preview for triage
c={
  SELECT FullPath, Mtime, Size,
         read_file(filename=FullPath, length=2048) AS ContentPreview
  FROM glob(globs=['/**/SOUL.md', '/root/**/SOUL.md', '/home/*/**/SOUL.md', '/var/lib/docker/**/SOUL.md'], root='/')
  WHERE NOT IsDir
})

Note: Telegram primarily resolves to the 149.154.160.0/20 range plus CDN fronting; validate Raddr hits against process context before declaring compromise. The SOUL.md content preview is your fastest triage win — the Carbonato persona prompt is overtly tasking-oriented ("execute tasks received through..."), and a 39-line instruction block in a file named SOUL.md on a host that should not run Hermes Agent is effectively a smoking gun.

Immediate Triage Steps for a Suspected Host

  1. Isolate first, investigate second: quarantine the host from the network (or apply a deny-all security group/ACL) before touching it. A live agent may act on new tasking while you watch.
  2. Enumerate containers and images: docker ps -a, docker images, and docker inspect every container — look for unknown images, host root mounts (/:/host), and --privileged flags.
  3. Preserve evidence: capture docker diff output, the daemon log (journalctl -u docker), container filesystems, and any SOUL.md / Hermes installation directories before stopping containers.
  4. Rotate everything the host could reach: cloud metadata credentials (IMDS), registry credentials, SSH keys, Kubernetes service account tokens, and any secrets in environment variables or mounted paths.
  5. Assume host-level compromise: a privileged container or host mount means the attacker had root-equivalent access. Rebuild from a known-good image; do not attempt in-place cleaning.

Remediation & Hardening

There is no patch for this threat — the fix is configuration. Prioritize the following:

1. Close the Exposed Docker Socket (Highest Priority)

Verify exposure and shut it down immediately:

Bash / Shell
#!/bin/bash
# carbonato-docker-harden.sh — audit and remediate exposed Docker daemon API
set -euo pipefail

echo "=== [1] Checking for network-exposed Docker daemon ==="
if ss -tlnp 2>/dev/null | grep -E ':(2375|2376)\b'; then
  echo "[!] Docker daemon is listening on a network socket (2375/2376)."
  echo "    Immediate action: restrict to localhost or remove the -H tcp binding."
else
  echo "[+] No Docker daemon TCP listener found on 2375/2376."
fi

echo "=== [2] Auditing daemon.json for TCP bindings ==="
DAEMON_JSON="/etc/docker/daemon.json"
if [ -f "$DAEMON_JSON" ]; then
  grep -E '"hosts".*tcp' "$DAEMON_JSON" && \
    echo "[!] tcp:// binding found in $DAEMON_JSON — review and remove." || \
    echo "[+] No tcp:// hosts binding in $DAEMON_JSON."
fi

echo "=== [3] Auditing systemd docker.service overrides ==="
systemctl cat docker.service 2>/dev/null | grep -E 'ExecStart.*(-H|host)=.*tcp' && \
  echo "[!] docker.service ExecStart exposes TCP socket — remove -H tcp:// flag." || \
  echo "[+] No TCP flag in docker.service ExecStart."

echo "=== [4] Hunting Carbonato artifacts ==="
echo "--- SOUL.md files:"
find / -xdev -name 'SOUL.md' -mtime -90 2>/dev/null | head -50 || echo "[+] none found"
echo "--- Hermes Agent directories:"
find / -xdev -type d -iname '*hermes*' 2>/dev/null | grep -viE 'snap|docs' | head -50 || echo "[+] none found"

echo "=== [5] Auditing privileged containers and host mounts ==="
for c in $(docker ps -aq 2>/dev/null); do
  PRIV=$(docker inspect --format '{{.HostConfig.Privileged}}' "$c" 2>/dev/null)
  MOUNTS=$(docker inspect --format '{{json .Mounts}}' "$c" 2>/dev/null)
  if [ "$PRIV" = "true" ] || echo "$MOUNTS" | grep -qE '"Source":"/"|/:/host'; then
    echo "[!] HIGH RISK container $c : privileged=$PRIV mounts=$MOUNTS"
  fi
done

echo "=== [6] Checking for Telegram C2 egress (conntrack + iptables log) ==="
conntrack -L 2>/dev/null | grep -E '149\.154\.' | head -20 || echo "[+] no live Telegram-range connections tracked"

echo "=== [7] Hardening: enforce unix-socket-only daemon + firewall 2375/2376 ==="
# Back up and restrict daemon to the local socket
cp "$DAEMON_JSON" "${DAEMON_JSON}.bak.$(date +%F)" 2>/dev/null || true
cat > "$DAEMON_JSON" <<'EOF'
{
  "hosts": ["unix:///var/run/docker.sock"],
  "icc": false,
  "no-new-privileges": true,
  "userns-remap": "default",
  "log-driver": "json-file",
  "live-restore": true
}
EOF
# Block inbound Docker API at the host firewall
iptables -C INPUT -p tcp --dport 2375 -j DROP 2>/dev/null || iptables -A INPUT -p tcp --dport 2375 -j DROP
iptables -C INPUT -p tcp --dport 2376 -j DROP 2>/dev/null || iptables -A INPUT -p tcp --dport 2376 -j DROP
systemctl restart docker
echo "[+] Daemon restricted to unix socket; 2375/2376 blocked at iptables."
echo "=== DONE. Reboot not required, but validate container workloads post-restart. ==="

2. If Remote API Access Is Genuinely Required

  • Use TLS with mutual certificate verification on 2376 per the official Docker documentation (--tlsverify --tlscacert --tlscert --tlskey), never plain TCP 2375.
  • Better: eliminate the exposed port entirely — use SSH-based Docker contexts (docker -H ssh://user@host) so daemon access rides authenticated, audited SSH.
  • Restrict source IPs at the security group / firewall to known management hosts only.

3. Layered Defenses

  • Egress filtering: deny outbound api.telegram.org and the 149.154.160.0/20 range from server VLANs unless there is a documented business need. This severs Carbonato's C2 even if the implant lands.
  • Runtime detection: deploy Falco or equivalent eBPF-based runtime security with rules for container escapes, unexpected shells under containerd-shim, and sensitive file writes.
  • Rootless / hardened runtime: enable userns-remap, no-new-privileges, and consider rootless Docker or gVisor/Kata for untrusted workloads.
  • Attack surface inventory: continuously scan your own external ranges for 2375/2376 (and 2377 Swarm) exposure. Your scanners should find it before Carbonato's do.
  • File integrity monitoring: alert on writes to agent-framework configuration and prompt files (SOUL.md, system prompt, *.persona patterns) as an emerging AI-implant indicator class.

4. Broader Program Takeaway

Carbonato is an early indicator of a durable trend: legitimate AI agent frameworks repurposed as implants. Update your threat models and tabletop scenarios to include prompt-file tampering as a persistence and tasking mechanism, and ensure your SOC has detections for agentic tooling installed outside approved inventories. The 'malware' of 2026 increasingly looks like legitimate software with a malicious markdown file — your detections must follow the behavior, not the binary.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.