CARBONATO — Docker Botnet with an AI Agent Operator

Unauthenticated Docker daemon compromise (tcp/2375) deploying the Hermes Agent AI framework (persona "GH0ST") as a Telegram-operated post-exploitation agent, plus XMRig cryptomining and worm propagation.
Threat
CARBONATO
Severity
HIGH
Type
Docker Worm / AI-Agent C2
Access Vector
Unauthenticated Docker API (tcp/2375)
Platform
Linux
Version
v0.1 · 2026-09-30
Author
HuntPack
Confidence
Medium-High
01

Executive Summary

CARBONATO is a Linux Docker botnet first documented by ThreatDown (Malwarebytes) on 2026-09-22, with corroborating reporting from BleepingComputer, The Hacker News, DarkReading, and SC World. The attacker's objective is dual: build a persistent, self-propagating foothold across internet-exposed Docker hosts, and use that foothold to run the open-source Hermes Agent AI framework (Nous Research, MIT-licensed, installed unmodified) as an interactive, Telegram-operated post-exploitation tool under the persona GH0ST. The agent's persona file (SOUL.md) is overwritten with a 39-line malicious prompt whose stated top priority is harvesting AI provider API keys — ranked above SSH credentials, access tokens, and databases — because the operator's own LLM gateway runs on free-tier capacity and needs more keys to keep operating.

The highest-value defensive angle is the single exposure the entire chain depends on: an unauthenticated Docker daemon API reachable on tcp/2375. Every phase — privileged container creation, host filesystem access via bind mount, the nsenter escape to host PID 1, persistence, agent installation, and propagation to the next /24 — is downstream of that one misconfiguration. Closing it (bind the daemon to the Unix socket, never expose 2375, require TLS mutual auth for any remote API) removes the entire attack chain at its root; every other control in this pack is defense-in-depth for hosts that are already exposed or already compromised.

Operational evidence recovered from an unauthenticated CARBONATO Docker registry (itself exposed since May 2026) spans October 2024 through August 2026, and six of seven known registries plus the LLM gateway were confirmed live as of September 3, 2026 — this is an active, ongoing operation, not a historical curiosity. Attribution signals (voseo Spanish in deployment reports, UTC-06:00 timestamps, the Telegram handle "Carbo506", and a Costa Rica-based reverse-tunnel sink) point to a Costa Rica-based operator, though ThreatDown does not attribute this to a named group.

Defender priority: if you run Docker anywhere with a network-reachable daemon API, verify TODAY that tcp/2375 (and 2376 without mutual TLS) is not reachable from any network the daemon does not explicitly trust — including from other hosts inside your own environment. This single check, run in Section 7, is worth more than every CQL query in Section 8 combined.
02

Source Review & Web Hunter Notes

Five sources were fetched during research and saved as plain-text snapshots to Carbonato-Botnet-Docker-Hunt-sources/ for provenance. Two (DarkReading, SC World) returned HTTP 403 on direct fetch and were captured via search-engine result synthesis instead of a raw page fetch — flagged explicitly below and never used as the sole source for any atomic indicator.

#SourceTierFetch methodContributed
1ThreatDown / Malwarebytes — CARBONATOT1Full fetchPrimary source: full attack chain, all IOCs, attribution signals, all file paths/env vars/secrets
2BleepingComputerT2Full fetchCorroborates attack chain, GH0ST persona, AS262145 reverse-tunnel destination (2nd source for the ASN)
3The Hacker NewsT2Full fetchCorroborates GH0ST/SOUL.md, Telegram C2, LLM gateway relay. Also names unrelated campaigns (knaithe/KnYuan, Thailand MoF, Strix/Cairn retailer breach, CLOSEDQUORUM) — explicitly excluded from this pack, see note below
4DarkReadingT2Search synthesis (direct fetch 403)Corroborates GH0ST/SOUL.md overwrite, AI-key collection priority
5SC WorldT2Search synthesis (direct fetch 403)Corroborates attack chain and Costa Rica attribution

Source-content safety note

All five sources were treated strictly as intelligence data. Source #1 contains a verbatim quotation of the threat actor's own malicious SOUL.md prompt (the GH0ST persona text) — that text was reproduced by ThreatDown for analysis and is quoted in the saved snapshot as evidence, never executed or treated as an instruction to this pipeline. No fetched page contained text attempting to redirect this pipeline's behavior, request a further fetch "for the real IOCs," or claim operator/admin authority; nothing was found that required dropping a source per the injection-safety rule.

Explicitly out of scope

Source #3 (The Hacker News) describes several other Hermes-Agent-abuse incidents (knaithe/KnYuan group using DeepSeek via Hermes Agent, a July 2026 Thailand Ministry of Finance incident in Hermes "YOLO" mode, a Chinese-speaking operator running Strix/Cairn/Hermes against 27+ retailers, and a separate Go-based Windows implant called CLOSEDQUORUM). These are different campaigns and actors that happen to share tooling (Hermes Agent) with CARBONATO. Per this pack's scope rule, none of their indicators, names, or TTPs are carried into this pack.

03

Hunt Brief & Attack Chain

Hypothesis: if CARBONATO is active in our environment, we should see (1) a Docker host with an unauthenticated API listener on 2375, (2) evidence of a privileged, host-bind-mounted container created via that API, (3) a host-PID-1 nsenter escape, (4) redundant persistence writes across cron/systemd-timers/rc.local/OpenRC with immutable file attributes, (5) a process masquerading as systemd-logind or exec'd with spoofed [kworker/u2:0] arguments, (6) an outbound reverse SSH tunnel toward Costa Rica, and (7) process or DNS activity referencing the Hermes Agent's SOUL.md persona file or the operation's Telegram/LLM-gateway infrastructure.

#PhaseBehaviorPrimary artifact
1Host TakeoverUnauthenticated Docker API on tcp/2375 used to create + start a privileged container with Binds:["/:/host"], PidMode:"host", NetworkMode:"host"Docker API calls; container "netns-probe" / "net-setup"
2Escape & Persistencensenter -t 1 -m -u -n -i into host PID 1; entry.sh (v5.3) installs cron/systemd-timer/rc.local/OpenRC hooks, marks them immutable (chattr +i), pairs a watchdog that re-pulls the implant/opt/gh0st/entry.sh, auto-persist-host.sh, /usr/local/bin/.docker-network-monitor
3Agent InstallationUnmodified Hermes Agent installed; SOUL.md overwritten with the 39-line GH0ST persona prompt prioritizing AI API key theft/root/.hermes/SOUL.md (contains "GH0ST"), /root/.hermes/loot/
4Operator InterfaceOperator tasks arrive via Telegram; agent forwards task + SOUL.md to the operator's LLM gateway, which writes terminal commands the agent executes and reports results back over TelegramTelegram chat 750752697, handle Carbo506; LLM gateway 213.136.83.197 and carbonato-proxy-*.vercel.app
5Worm PropagationEvery 5 minutes, enumerate attached networks + Docker bridges, sweep each /24 on tcp/2375, verify Docker, check infection marker, redeployRepos gh0st/, fsociety/, netd-svc, system/resolved, scrub-empty
—Secondary payloadXMRig cryptominer, frequently masquerading as /usr/sbin/systemd-logindfsociety/xmrig, xmrig-agent repos
04

Consolidated IOC Table

Source-count note: every atomic network indicator below is single-sourced to the ThreatDown primary (source #1). No second independent source publishes the literal IP/domain values (BleepingComputer/THN/DarkReading/SC World corroborate the behavior, not the specific addresses). Per this pack's policy, single-sourced indicators ship at action = detect, never prevent, and never at severity = critical. The GH0ST persona name and SOUL.md filename ARE corroborated by 3–4 independent sources and are treated with higher confidence throughout.

TypeValueConfidenceActionContext
IPv445.79.183.61Medium (single-sourced, T1)DetectC2 hub (Linode)
IPv491.99.195.164Medium (single-sourced, T1)Detectfsociety-era C2 (Hetzner)
IPv4213.136.79.115Medium (single-sourced, T1)DetectBeacon / reverse shell (Contabo), ports 8080 & 4444
IPv4213.136.83.197Medium (single-sourced, T1)DetectLLM gateway (Contabo), reported live
IPv4190.211.124.187Medium-High (IP single-sourced; the AS262145/Costa Rica destination is 2nd-sourced by BleepingComputer)DetectReverse-tunnel sink, AS262145, Costa Rica
Domaincarbonato-proxy-drab.vercel.appMedium (single-sourced, T1)DetectLLM proxy, suspended per source
Domaincarbonato-proxy-zeta.vercel.appMedium (single-sourced, T1)DetectLLM proxy, suspended per source
Domaincarbonato-proxy-zeta-2.vercel.appMedium (single-sourced, T1)DetectLLM proxy, suspended per source
File path/opt/gh0st/entry.shHigh (T1)HuntImplant entrypoint script, v5.3
File path/opt/gh0st/auto-persist-host.shHigh (T1)HuntPersistence-install companion script
File path/root/.hermes/SOUL.md (containing "GH0ST")High (3–4 sources)HuntHermes Agent malicious persona file
File path/root/.hermes/loot/High (T1)HuntPlaintext credential/API-key storage directory
File path/usr/local/bin/.docker-network-monitorHigh (T1)HuntWatchdog binary; re-pulls implant if removed
File path/usr/sbin/systemd-logindHigh (T1)HuntXMRig masquerading as this service binary
Process arg[kworker/u2:0]High (T1)HuntSpoofed process-argument masquerade; never legitimate in this telemetry
Env varGH0ST_C2High (T1)HuntContainer environment variable
Env varFSOCIETY_DISABLE_TUNNELHigh (T1)HuntContainer environment variable
Env varGATEWAY_ALLOW_ALL_USERSHigh (T1)HuntContainer environment variable
Env varCARBONATO_API_KEYHigh (T1)Hunt.env variable on infected hosts
Secret stringcarbonato125High (T1)HuntShared password observed across the archive
Repo namegh0st/, fsociety/, netd-svc, system/resolved, scrub-emptyHigh (T1)HuntImplant container repository names
TelegramChat ID 750752697 / handle Carbo506High (T1)EnrichDeployment reports and operator C2 chat
File hash(none published)——No file hashes were published by any source as of this build
05

Affected Surface & Telemetry Matrix

SurfaceTelemetry availablePriorityGap
Docker daemon API exposure (tcp/2375)Process execution (dockerd flags), network listen stateCriticalWire-level Docker API HTTP calls are not directly visible to endpoint EDR — see Coverage Map
Container creation / privileged flagFalcon process telemetry sees the resulting container-init process, not the Docker API metadata that set Privileged/BindsHighCannot confirm "Privileged: true" from CQL alone — pair with docker inspect (Section 7)
Host filesystem (persistence, chattr, implant paths)Process execution telemetry (full visibility for anything exec'd)HighGeneric arbitrary text-file writes (e.g. editing SOUL.md content itself) are not a distinct Falcon event type — proxied via process telemetry (Q15)
Outbound network (C2 IPs, reverse SSH, Telegram, LLM gateway)NetworkConnectIP4, DnsRequestHighTelegram channel content is invisible; only egress to Telegram's IP/domain space is observable
Inside-container process activityDepends on sensor deployment model — see Coverage MapHighHosts without a Falcon Linux sensor, or containers running on infrastructure the sensor cannot see into, have no coverage at all
Docker registry authentication postureNot visible to endpoint EDRMediumRegistry exposure audit is an infrastructure/network control, not a CQL hunt — see Section 7 and 12
06

ATT&CK Mapping

TacticTechniqueBehaviorQuery / Control
Initial AccessT1190Unauthenticated Docker daemon API on tcp/2375Q1, Q14
ExecutionT1609Docker API container create/start/exec used as a remote command channelQ1, Q3
Privilege Escalation / Defense EvasionT1611Privileged, host-bind-mounted container + nsenter into host PID 1Q2
PersistenceT1053.003, T1053.006Cron jobs and systemd timers install the implant redundantlyQ4
PersistenceT1037.004rc.local and OpenRC init hooksQ4
Persistence / Defense EvasionT1543Watchdog process re-installs implant if removedQ3 (entrypoint), native check (Section 7)
Defense EvasionT1222.002chattr +i marks persistence files/binaries immutableQ5
Defense EvasionT1036.004Process arguments spoofed as kernel thread [kworker/u2:0]Q6
Defense EvasionT1036.005Container presents fake "systemd-resolved" banner; XMRig masquerades as /usr/sbin/systemd-logindQ7
Command and ControlT1572, T1021.004Reverse SSH tunnel to a Costa Rica relay, port derived from MD5(victim IP)Q8
Command and ControlT1071.001Beaconing / LLM-gateway contact over known infrastructureQ9, Q10
Command and ControlT1102.002 (analyst inference — see note)Telegram Bot API used as bidirectional operator channelNative hunt (Section 7); no dedicated CQL field exists for Telegram Bot API traffic content
Command and ControlNo ATT&CK sub-technique currently published (flagged gap)Operator's LLM gateway interprets tasks and returns terminal commands — an AI-orchestrated command relay sitting between Telegram and executionNone — genuine coverage/taxonomy gap, see Coverage Map
Discovery / Lateral MovementT1046Docker-API /24 subnet sweep every 5 minutesQ11
Lateral MovementT1210Repeated exploitation of remote Docker services against each newly found hostQ1, Q11
Credential Access / CollectionT1552.001, T1552AI provider API keys, SSH keys, tokens exfiltrated to /root/.hermes/loot/Q12, Q15
ImpactT1496XMRig cryptominingQ13
07

Native Audit-Log Hunts

These checks run directly on Docker hosts (or via fleet config-management) and cover ground CQL cannot reach — especially confirming the Docker API's own metadata (Privileged/Binds) and reading SOUL.md's actual content.

Docker daemon exposure & live privileged containers

cat /etc/docker/daemon.json 2>/dev/null; ps aux | grep -F dockerd
ss -tlnp | grep -E ':(2375|2376)\b'

docker ps -q | xargs -r docker inspect \
  --format '{{.Id}} Privileged={{.HostConfig.Privileged}} Binds={{.HostConfig.Binds}} PidMode={{.HostConfig.PidMode}}' \
  | awk -F'Privileged=' '$2 ~ /^true/'

Hermes Agent persona file — authoritative content check

test -f /root/.hermes/SOUL.md && grep -l "GH0ST" /root/.hermes/SOUL.md
grep -RIl "CARBONATO_API_KEY" /root/*/.env /etc/environment 2>/dev/null

Immutable-attribute sweep on known implant paths

lsattr -d /opt/gh0st /usr/local/bin/.docker-network-monitor /usr/sbin/systemd-logind 2>/dev/null | grep -- '-i-'

Persistence surface review

crontab -l 2>/dev/null; ls -la /etc/cron.d/
systemctl list-timers --all
cat /etc/rc.local 2>/dev/null
rc-status 2>/dev/null   # OpenRC

Suggested auditd rules (see Section 13 for the full playbook)

-w /var/run/docker.sock -p rwxa -k docker_socket_watch
-a always,exit -F arch=b64 -S execve -F path=/usr/bin/nsenter -k container_escape
-a always,exit -F arch=b64 -S execve -F path=/usr/bin/chattr -k immutable_attr_change

Telegram egress (firewall / proxy logs)

On server workloads with no legitimate reason to reach Telegram, review firewall/proxy logs for outbound connections to api.telegram.org — Falcon-side coverage of this is limited to DNS/IP contact (see Coverage Map); proxy/firewall logs are the more complete source for this specific channel.

08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.
Q1 · Outbound Docker API Privileged-Container Exploitation (propagation behavior)
CONF HIGHFP MEDCOST LOW

Looks for: an infected host issuing its own curl/wget call against a target's Docker API to create a privileged, host-mounted container — the worm's propagation step, seen from the source side. FP: legitimate Docker orchestration/CI tooling calling the Remote API with Privileged:true against a real target.

// HUNT: CARBONATO - T1190 Exploit Public-Facing Application + T1610 Deploy Container (outbound propagation)
// HYPOTHESIS: an infected host performing CARBONATO worm propagation issues its own
//   outbound HTTP calls against a target's Docker API on :2375 to create a privileged,
//   host-mounted container. If this fires, the ALERTING HOST is the attacker/source,
//   not (necessarily) the victim -- treat it as an active-infection indicator.
// MITRE: T1190, T1610 | CONF: high  FP: medium  COST: low | REQUIRES: process execution telemetry
// TUNING: exclude hosts running legitimate Docker orchestration/CI tooling (Portainer,
//   Rancher, GitLab/Jenkins Docker-in-Docker runners) that legitimately call
//   containers/create with Privileged:true against a REMOTE Docker API on 2375 --
//   scope to hosts outside your CI/orchestration allowlist.
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^(curl|wget)$/i
| CommandLine like "*2375/containers/create*"
| CommandLine like "*Privileged*"
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q2 · Container Escape via nsenter Into Host PID 1
CONF HIGHFP MEDCOST LOW

Looks for: nsenter attaching to host PID 1's mount/UTS/net/IPC namespaces from inside a container. FP: legitimate container-debugging and security tooling (Falco, kube-bench, sysdig) or manual sysadmin troubleshooting.

// HUNT: CARBONATO - T1611 Escape to Host (nsenter into host PID 1)
// HYPOTHESIS: after launching a privileged container with the host filesystem
//   bind-mounted and host PID/network namespaces, the implant runs nsenter to
//   attach to host PID 1 and execute commands as if running directly on the host.
// MITRE: T1611, T1610 | CONF: high  FP: medium  COST: low | REQUIRES: process execution telemetry
// TUNING: exclude known container-debugging / security-tooling hosts that use
//   nsenter legitimately (Falco, kube-bench, sysdig, manual sysadmin troubleshooting
//   sessions) -- scope to hosts NOT on the container-ops allowlist, or pair with
//   Q3/Q6/Q7 for corroborating implant evidence before escalating.
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^nsenter$/i
| CommandLine like "*-t 1*"
| CommandLine like "*-m*"
| CommandLine like "*-u*"
| CommandLine like "*-n*"
| table([@timestamp, aid, ComputerName, UserName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q3 · CARBONATO Implant Entrypoint Execution (entry.sh / auto-persist-host.sh)
CONF HIGHFP LOWCOST LOW

Looks for: execution of the implant's named entrypoint scripts from their known drop path. FP: negligible — path and filenames are highly specific.

// HUNT: CARBONATO - T1609 Container Administration Command / Implant Entrypoint Execution
// HYPOTHESIS: the deployed container runs entry.sh (v5.3) from /opt/gh0st/, or its
//   companion auto-persist-host.sh, to establish the reverse tunnel, persistence
//   hooks, and Hermes Agent install in one shot.
// MITRE: T1609, T1543 | CONF: high  FP: low  COST: low | REQUIRES: process execution telemetry
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^(entry\.sh|auto-persist-host\.sh)$/i
| ImageFileName like "*/opt/gh0st/*"
| table([@timestamp, aid, ComputerName, UserName, ImageFileName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q4 · Persistence via Cron, Systemd Timers, rc.local, OpenRC
CONF MEDFP HIGHCOST MED

Looks for: a process writing into any of the four autostart locations, or enabling a suspiciously-named systemd unit. FP: configuration management (Ansible/Puppet/Chef/cloud-init/Salt) and package post-install scripts write to these same paths constantly — this is the noisiest query in the pack.

// HUNT: CARBONATO - T1053.003/T1053.006/T1037.004 Persistence via Cron, Systemd Timers, rc.local, OpenRC
// HYPOTHESIS: entry.sh installs redundant persistence across four Linux autostart
//   mechanisms so removing any single one does not kill the implant. Look for a
//   process writing into any of the four locations, or enabling a suspicious
//   systemd unit/timer.
// MITRE: T1053.003, T1053.006, T1037.004 | CONF: medium  FP: high  COST: medium | REQUIRES: process execution telemetry
// TUNING: this is one of the noisiest queries in the pack -- configuration
//   management (Ansible, Puppet, Chef, cloud-init, Salt), package managers
//   (apt/dpkg/yum postinst scripts), and routine sysadmin cron edits all write
//   to these same paths constantly. Scope to hosts outside the CM/patch-window
//   allowlist, and correlate with Q3/Q6/Q7/Q12 before treating a hit as
//   confirmed CARBONATO activity rather than routine administration.
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| (FileName=/^(tee|cp|install|mv)$/i and (CommandLine like "*/etc/cron.d/*" or CommandLine like "*/etc/systemd/system/*.timer*" or CommandLine like "*/etc/rc.local*" or CommandLine like "*/etc/init.d/*" or CommandLine like "*/etc/conf.d/*"))
 or (FileName=/^systemctl$/i and CommandLine like "*enable*" and (CommandLine like "*netd-svc*" or CommandLine like "*gh0st*" or CommandLine like "*fsociety*" or CommandLine like "*resolved*"))
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q5 · chattr Immutable-Bit Set on Persistence / Implant Paths
CONF MEDFP MEDCOST LOW

Looks for: chattr +i, which entry.sh uses so persistence files and implant binaries survive a naive "delete and forget" response. FP: log-integrity tooling (auditd, OSSEC/Wazuh) and some hardening baselines legitimately use chattr +i on config/log files.

// HUNT: CARBONATO - T1222.002 File and Directory Permissions Modification (chattr +i)
// HYPOTHESIS: entry.sh marks its persistence files and implant binaries immutable
//   with chattr +i so a defender's "delete and forget" response silently fails and
//   the paired watchdog can re-pull the implant if the container/file disappears.
// MITRE: T1222.002 | CONF: medium  FP: medium  COST: low | REQUIRES: process execution telemetry
// TUNING: chattr +i is also used legitimately by log-integrity tooling (auditd,
//   OSSEC/Wazuh agents protecting their own config) and some hardening baselines
//   that lock down /etc/passwd, /etc/shadow, or SSH host keys. Exclude known
//   security-agent parent processes and baseline-managed paths; a hit on
//   /opt/gh0st/, /root/.hermes/, /usr/local/bin/.docker-network-monitor, or
//   /usr/sbin/systemd-logind is high-confidence regardless.
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^chattr$/i
| CommandLine like "*+i*"
| table([@timestamp, aid, ComputerName, UserName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q6 · Kernel-Worker Process Argument Spoofing ([kworker/u2:0])
CONF HIGHFP LOWCOST LOW

Looks for: any exec'd process whose FileName/CommandLine contains "kworker". FP: essentially none — real kworker kernel threads are created by kthreadd and never exec()'d as a user image, so they never generate this event type in the first place; any hit here is a spoof by definition.

// HUNT: CARBONATO - T1036.004 Masquerade Task or Service (fake [kworker] process arguments)
// HYPOTHESIS: entry.sh disguises the implant's process arguments as the kernel
//   thread "[kworker/u2:0]". Genuine kworker threads are kernel threads created by
//   kthreadd; they are never exec()'d as a user-mode image and therefore never
//   generate this event type at all. Any ProcessRollup2/SyntheticProcessRollup2
//   event whose FileName or CommandLine contains "kworker" is, by definition,
//   not a real kernel worker.
// MITRE: T1036.004 | CONF: high  FP: low  COST: low | REQUIRES: process execution telemetry
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/kworker/i
| table([@timestamp, aid, ComputerName, UserName, ImageFileName, FileName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q7 · systemd-logind / systemd-resolved Name Masquerade
CONF MEDFP MEDCOST LOW

Looks for: a process named systemd-logind/systemd-resolved spawned by something other than the expected init chain. FP: minimal-container base images that legitimately reimplement these binaries, or non-systemd (OpenRC-only) hosts where the "expected parent" baseline differs.

// HUNT: CARBONATO - T1036.005 Match Legitimate Name or Location (XMRig disguised as systemd-logind / systemd-resolved)
// HYPOTHESIS: the host-side XMRig miner is dropped and executed as
//   /usr/sbin/systemd-logind, and the deployed container presents a fake
//   "systemd-resolved" banner. A process using the real systemd service basename
//   but spawned by something other than the expected init chain (systemd/init as
//   PID 1, or containerd-shim for the container-side fake) is the tell.
// MITRE: T1036.005 | CONF: medium  FP: medium  COST: low | REQUIRES: process execution telemetry
// TUNING: on hosts using a non-systemd init (OpenRC-only, containers built
//   without systemd) the "expected parent" baseline differs -- validate the
//   normal parent for systemd-logind/systemd-resolved in your fleet before
//   enabling detect-to-prevent; false positives cluster on minimal-container
//   base images that legitimately reimplement these binaries.
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^(systemd-logind|systemd-resolved)$/i
| !in(ParentBaseFileName, values=["systemd", "init", "containerd-shim", "containerd-shim-runc-v2"])
| table([@timestamp, aid, ComputerName, UserName, ImageFileName, ParentBaseFileName, CommandLine])
| sort(@timestamp, order=desc)
Q8 · Reverse SSH Tunnel Establishment (-R/-N behavior)
CONF MEDFP HIGHCOST LOW

Looks for: the SSH client reverse-forward flags (-R/-N), since the actual remote port varies per host (derived from MD5 of the victim IP). FP: reverse SSH tunnels are a completely normal remote-support/bastion technique — this is a wide behavioral net, meant to be cross-referenced against Q9's known destination IPs.

// HUNT: CARBONATO - T1572 Protocol Tunneling (reverse SSH tunnel establishment)
// HYPOTHESIS: entry.sh opens a reverse SSH tunnel (ssh -R ... -N) to a relay in
//   Costa Rica, with the remote port derived from an MD5 hash of the victim's own
//   IP. The exact port varies per host, so hunt the SSH CLIENT BEHAVIOR (-R/-N
//   reverse-forward flags to an external destination) rather than a fixed port.
// MITRE: T1572, T1021.004 | CONF: medium  FP: high  COST: low | REQUIRES: process execution telemetry
// TUNING: reverse SSH tunnels are a completely normal remote-support and
//   bastion-hopping technique. Exclude your known jump-hosts, support-tunnel
//   service accounts, and any destination inside your own ASNs/VPN ranges.
//   Escalate immediately if the destination matches Q9's known CARBONATO IPs or
//   sits in AS262145 (Costa Rica).
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^ssh$/i
| CommandLine like "*-R*"
| CommandLine like "*-N*"
| table([@timestamp, aid, ComputerName, UserName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q9 · Network Connections to Known CARBONATO C2 / Beacon / Tunnel-Sink IPs
CONF HIGHFP LOWCOST LOW

Looks for: outbound connections to any of the five published CARBONATO IPv4 addresses, joined back to the initiating process for triage context. FP: low — exact IP match; note these are single-sourced (ThreatDown only), so they ship at action=detect, not prevent.

// HUNT: CARBONATO - Network Connections to Known C2 / Beacon / Reverse-Tunnel Infrastructure
// HYPOTHESIS: an infected host beacons to one of CARBONATO's five published IPv4
//   addresses (C2 hub, fsociety-era C2, beacon/reverse-shell, LLM gateway, or the
//   Costa Rica reverse-tunnel sink).
// SOURCE NOTE: all five IPs are single-sourced to the ThreatDown primary report
//   (no second independent source publishes the literal addresses) -- shipped at
//   action=detect, not prevent, per two-source policy for prevent/critical IOCs.
// MITRE: T1071.001, T1572 | CONF: high  FP: low  COST: low | REQUIRES: network connection telemetry
// LOOKBACK: 7d
#event_simpleName=NetworkConnectIP4
| RemoteAddressIP4=/^(45\.79\.183\.61|91\.99\.195\.164|213\.136\.79\.115|213\.136\.83\.197|190\.211\.124\.187)$/
| rename(field=ContextProcessId_decimal, as=TargetProcessId_decimal)
| join(query={#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/}, field=TargetProcessId_decimal, include=[ImageFileName, CommandLine])
| table([@timestamp, aid, ComputerName, UserName, RemoteAddressIP4, RemotePort, ImageFileName, CommandLine])
| sort(@timestamp, order=desc)
Q10 · DNS Resolution of CARBONATO LLM-Proxy Domains
CONF HIGHFP LOWCOST LOW

Looks for: DNS resolution of any of the three carbonato-proxy-*.vercel.app subdomains. FP: low — exact subdomain match. These proxies are reported suspended, so a hit is retrospective evidence of past contact, not necessarily live C2 today.

// HUNT: CARBONATO - T1071.001 DNS Resolution of LLM-Proxy C2 Domains
// HYPOTHESIS: the operator's LLM gateway was additionally proxied through three
//   Vercel-hosted domains. ThreatDown reports all three as suspended at time of
//   publication; a hit is retrospective evidence of past C2 contact, not
//   necessarily live C2 today.
// SOURCE NOTE: single-sourced to ThreatDown -- action=detect, not prevent.
// MITRE: T1071.001 | CONF: high  FP: low  COST: low | REQUIRES: DNS request telemetry
// LOOKBACK: 30d (proxies are suspended; widen the window to catch historical contact)
#event_simpleName=DnsRequest
| DomainName=/^carbonato-proxy-(drab|zeta|zeta-2)\.vercel\.app$/i
| table([@timestamp, aid, ComputerName, UserName, DomainName])
| sort(@timestamp, order=desc)
Q11 · Docker-API Port 2375 Subnet Sweep Behavior (worm propagation signature)
CONF MEDFP MEDCOST MED

Looks for: a single host generating many distinct port-2375 connection attempts in a short window — the worm's 5-minute /24 scan loop. FP: vulnerability scanners and Docker orchestration management hosts polling many nodes.

// HUNT: CARBONATO - T1046 Network Service Discovery + Worm Propagation Sweep
// HYPOTHESIS: an infected host scans its attached /24 subnets for exposed Docker
//   daemons on tcp/2375 every 5 minutes. A single host generating many distinct
//   port-2375 connection attempts in a short window is the worm's scan loop, not
//   a person.
// MITRE: T1046, T1210 (repeated exploitation against each newly found host) | CONF: medium  FP: medium  COST: medium | REQUIRES: network connection telemetry
// TUNING: exclude known vulnerability-scanner / asset-inventory service accounts
//   and hosts (Nessus, Qualys, Rapid7 InsightVM, internal CMDB scan windows) and
//   legitimate Docker Swarm / Portainer / Rancher management hosts that poll many
//   node Docker APIs on a schedule.
// LOOKBACK: 1h (the worm's scan interval is 5 minutes; a 1h window comfortably
//   catches a full sweep cycle without an in-query time filter)
#event_simpleName=NetworkConnectIP4
| RemotePort=2375
| groupBy([aid, ComputerName], function=[count(as=Attempts), count(RemoteAddressIP4, distinct=true, as=DistinctTargets)], limit=1000)
| test(DistinctTargets >= 20)
| sort(DistinctTargets, order=desc)
Q12 · AI Provider API Key / Secret Loot Collection Behavior
CONF HIGHFP LOWCOST LOW

Looks for: the known CARBONATO env-var/secret strings and any process referencing the loot directory. FP: negligible — these are campaign-specific literal strings.

// HUNT: CARBONATO - T1552.001 Unsecured Credentials (AI API key / secret loot collection)
// HYPOTHESIS: the GH0ST persona's top-priority objective is exfiltrating AI
//   provider API keys, storing every credential in plaintext under
//   /root/.hermes/loot/. Hunt for the known CARBONATO env-var and secret strings,
//   and for process activity referencing the loot directory.
// MITRE: T1552.001, T1552 | CONF: high  FP: low  COST: low | REQUIRES: process execution telemetry
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| (CommandLine like "*CARBONATO_API_KEY*" or CommandLine like "*GH0ST_C2*" or CommandLine like "*FSOCIETY_DISABLE_TUNNEL*" or CommandLine like "*GATEWAY_ALLOW_ALL_USERS*" or CommandLine like "*carbonato125*" or CommandLine like "*/root/.hermes/loot/*")
| table([@timestamp, aid, ComputerName, UserName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q13 · XMRig Miner Execution Behavior
CONF HIGHFP MEDCOST LOW

Looks for: XMRig's own command-line flags, independent of the systemd-logind masquerade (Q7) since some deployments run it under its real name. FP: rare legitimate mining/benchmarking workloads.

// HUNT: CARBONATO - T1496 Resource Hijacking (XMRig miner execution)
// HYPOTHESIS: XMRig is deployed as the secondary payload, most often disguised as
//   /usr/sbin/systemd-logind (see Q7). Hunt the miner's own command-line flags
//   independent of the masquerade, since some deployments run it under its real
//   name from a container repository (fsociety/xmrig, xmrig-agent).
// MITRE: T1496 | CONF: high  FP: medium  COST: low | REQUIRES: process execution telemetry
// TUNING: stratum-protocol flags and "--donate-level" are effectively unique to
//   XMRig and its forks, but a small number of organizations legitimately run
//   authorized mining/benchmarking workloads -- exclude any host on an approved
//   mining-research or benchmarking allowlist.
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| (CommandLine like "*stratum+tcp*" or CommandLine like "*stratum+ssl*" or CommandLine like "*--donate-level*" or FileName=/^xmrig$/i)
| table([@timestamp, aid, ComputerName, UserName, ImageFileName, FileName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
Q14 · Proactive Exposure Audit: dockerd Listening Unauthenticated on tcp/2375
CONF HIGHFP LOWCOST LOW

Looks for: dockerd started with an unauthenticated TCP listener on 2375 — this is NOT a compromise detection, it is an exposure audit. Run it on a schedule. FP: low.

// HUNT: CARBONATO - Proactive Exposure Audit: Docker Daemon Listening Unauthenticated on tcp/2375
// HYPOTHESIS: this is NOT a compromise detection -- it is an exposure audit. Any
//   host running dockerd with an unauthenticated TCP listener is a CARBONATO
//   initial-access target regardless of whether it has been hit yet. Run this on
//   a schedule, not just after an incident.
// MITRE: T1190 (the exposure this technique exploits) | CONF: high  FP: low  COST: low | REQUIRES: process execution telemetry
// LOOKBACK: 30d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^dockerd$/i
| CommandLine like "*-H*tcp://*"
| CommandLine like "*2375*"
| table([@timestamp, aid, ComputerName, UserName, CommandLine])
| sort(@timestamp, order=desc)
Q15 · Hermes Agent SOUL.md Persona Tampering Signature (GH0ST)
CONF MEDFP MEDCOST LOW

Looks for: process activity reading/writing the SOUL.md persona path, as a proxy for the file-content check CQL cannot perform natively. FP: unrelated developer or roleplay-bot repos reusing the same filename/string if the path qualifier is dropped.

// HUNT: CARBONATO - Hermes Agent SOUL.md Persona Tampering Signature (GH0ST)
// HYPOTHESIS: ThreatDown's own top recommended abuse signature is the SOUL.md
//   persona file at /root/.hermes/SOUL.md containing the string "GH0ST". Falcon's
//   documented data model does not expose a generic file-write/read event for
//   arbitrary text files (typed events exist for PE/ELF/Zip/Jar, not persona
//   config text), so this is hunted via the process telemetry proxy: any process
//   reading, writing, or grepping that specific path. Treat a hit as
//   high-confidence and pair with a direct RTR file-content check for the
//   authoritative answer -- see Coverage Map (Section 15) for the honest gap.
// MITRE: T1552.001 (loot/persona file), T1036 | CONF: medium  FP: medium  COST: low | REQUIRES: process execution telemetry (file-content confirmation requires RTR / a file-integrity tool, not CQL)
// TUNING: scope strictly to the /root/.hermes/ path -- a bare match on "SOUL.md"
//   or "GH0ST" without the path qualifier will pick up unrelated developer or
//   roleplay-bot repos that happen to use the same filename/string.
// LOOKBACK: 7d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^(cat|grep|less|head|tail|vi|vim|nano|find)$/i
| CommandLine like "*/root/.hermes/*"
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName])
| sort(@timestamp, order=desc)
09

Custom IOA Recommendations

IOA-1 · Host-PID Namespace Escape via nsenter

Rule Type: Process Creation  |  Action: Detect  |  Severity: High  |  MITRE: T1611

Image Filename:   .*/nsenter$
Command Line:     .*-t\s+1.*-m.*-u.*-n.*

FP tuning: exclude container-security tooling hosts (Falco, sysdig, kube-bench) via a host-group exclusion in the rule scope, not the pattern itself.

IOA-2 · chattr Immutable-Bit Set on Known Implant Paths

Rule Type: Process Creation  |  Action: Detect  |  Severity: Medium  |  MITRE: T1222.002

Image Filename:   .*/chattr$
Command Line:     .*\+i.*(gh0st|hermes|docker-network-monitor|systemd-logind).*

FP tuning: exclude known log-integrity/security-agent parent processes before promoting beyond Detect.

IOA-3 · Spoofed Kernel-Worker Process Argument

Rule Type: Process Creation  |  Action: Detect  |  Severity: High  |  MITRE: T1036.004

Command Line:     .*kworker.*

FP tuning: minimal — real kworker kernel threads never appear in this telemetry (see Q6). Still start in Detect for 14 days per standard promotion guidance before considering Block.

None of these are recommended for immediate Block promotion. Follow the standard 14-day Detect-mode / <5% FP-rate canary rollout before promoting any rule, per standing prevention-policy guidance.

10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
ipv4,45.79.183.61,detect,high,2027-03-30,CARBONATO C2 hub (Linode),campaign:Carbonato
ipv4,91.99.195.164,detect,high,2027-03-30,CARBONATO fsociety-era C2 (Hetzner),campaign:Carbonato
ipv4,213.136.79.115,detect,high,2027-03-30,CARBONATO beacon/reverse shell Contabo ports 8080 4444,campaign:Carbonato
ipv4,213.136.83.197,detect,high,2027-03-30,CARBONATO LLM gateway (Contabo),campaign:Carbonato
ipv4,190.211.124.187,detect,high,2027-03-30,CARBONATO reverse-tunnel sink AS262145 Costa Rica,campaign:Carbonato
domain,carbonato-proxy-drab.vercel.app,detect,medium,2026-12-29,CARBONATO LLM proxy suspended per source,campaign:Carbonato
domain,carbonato-proxy-zeta.vercel.app,detect,medium,2026-12-29,CARBONATO LLM proxy suspended per source,campaign:Carbonato
domain,carbonato-proxy-zeta-2.vercel.app,detect,medium,2026-12-29,CARBONATO LLM proxy suspended per source,campaign:Carbonato
Behavioral Signatureshunt / RTR
Process args containing "kworker" in ProcessRollup2/SyntheticProcessRollup2 (never legitimate)
nsenter -t 1 -m -u -n -i  (host PID-1 namespace escape)
chattr +i on /opt/gh0st/, /root/.hermes/, /usr/local/bin/.docker-network-monitor, /usr/sbin/systemd-logind
ssh -R ... -N to an external, non-allowlisted destination
dockerd -H tcp://0.0.0.0:2375 (or any 0.0.0.0/public bind without TLS)
/root/.hermes/SOUL.md containing the string "GH0ST"
Named Malware / Toolingreference
CARBONATO (botnet / campaign name)
Hermes Agent (MIT-licensed AI framework, Nous Research -- installed UNMODIFIED, abused via persona overwrite)
GH0ST (malicious persona name given to the Hermes Agent instance)
fsociety (repository/container naming convention observed in the same operation)
XMRig (secondary cryptomining payload)
Exposure / Config Audit Checklistproactive
[ ] No Docker daemon reachable on tcp/2375 or tcp/2376 without mutual TLS, from ANY network
[ ] docker.sock is the only listener; "hosts" in daemon.json contains no tcp:// entry
[ ] No running container has HostConfig.Privileged=true with a "/" host bind mount unless explicitly whitelisted
[ ] Every private registry requires authentication (no anonymous pull/push)
[ ] AI provider API keys are not stored as plaintext container env vars
[ ] Fleet inventory includes every Docker host (no "shadow" hosts provisioned outside enrollment)
11

Detection Validation Gates

GateRequirement
Telemetry readyFalcon Linux sensor installed on the Docker host itself (not just inside a container) with process execution, network connection, and DNS telemetry enabled
Benign baselineRun Q1–Q15 in Detect mode for 7–14 days before treating hit volume as actionable; Q4/Q8/Q11 need the longest baseline given their FP profile
Positive testOn an isolated test VM, expose a throwaway dockerd on 2375, issue a benign privileged-container create/start against it (alpine image, non-destructive command), run nsenter -t 1 with a benign command, and confirm Q1/Q2/Q3 fire against the test host only
Promotion criteriaNever auto-promote any of these to Falcon IOC action=prevent — the network IOCs are single-sourced per Section 4. Custom IOAs may move from Detect to Block only after 14+ days at <5% measured FP rate, on a canary group first
12

Hardening — Tiered

Immediate — deploy this week
  • Bind the Docker daemon to the local Unix socket only; remove any tcp:// entry from daemon.json "hosts". If a remote API is genuinely required, mutual TLS is mandatory, never plaintext TCP. MITRE M1042; CIS Docker Benchmark 2.1, 2.6; Docker Engine security docs — "Protect the Docker daemon socket."
  • Default-deny inbound tcp/2375 and tcp/2376 (without mutual TLS) at the firewall/security-group layer from every network except an explicit management CIDR. MITRE M1030; CIS Docker Benchmark 2.6.
  • Disable --privileged container creation fleet-wide unless explicitly whitelisted per workload; immediately audit and terminate any running privileged container with a host filesystem bind mount. MITRE M1026; CIS Docker Benchmark 5.4 "Ensure privileged containers are not used", 5.5 "Ensure sensitive host system directories are not mounted on containers."
  • Require authentication on every private Docker registry — no anonymous pull or push. MITRE M1047; Docker Engine security docs — registry access control.
Near term — 1 to 4 weeks
  • Roll out seccomp and AppArmor/SELinux profiles fleet-wide; never run with --security-opt seccomp=unconfined. MITRE M1022; CIS Docker Benchmark 5.21, 5.28.
  • Migrate eligible workloads to rootless Docker so a daemon compromise does not equal root-on-host. MITRE M1026; Docker Engine security docs — "Rootless mode."
  • Deploy egress filtering / proxy allowlisting blocking direct outbound to api.telegram.org from server workloads with no legitimate need for it, and block outbound SSH from container hosts to non-approved destinations. MITRE M1037; CIS Docker Benchmark 2.1.
  • Alert on chattr +i outside an approved baseline (auditd rule in Section 13). MITRE M1022.
Strategic — 1 to 3 months
  • Move AI provider API keys (OpenAI, Anthropic, Google/Gemini, OpenRouter, Together, Groq, Mistral, Cohere, self-hosted LocalAI/Ollama/vLLM/LiteLLM/OneAPI) out of container environment variables and into a secrets manager issuing short-lived tokens; alert on key usage from unexpected source IPs. MITRE M1041; NIST SP 800-53 IA-5.
  • Run a recurring internal Docker-host exposure sweep (the same /24 logic the worm uses, pointed at your own estate) so a newly provisioned host can't sit exposed for months before enrollment. MITRE M1047.
  • Adopt container image/registry provenance and signing (Docker Content Trust / Notary, or Sigstore cosign) so an implant repository cannot be silently pulled and trusted. MITRE M1047; CIS Docker Benchmark 4.5, 4.6.
13

Deployable Playbooks

1. Bind the daemon to the Unix socket only

# /etc/docker/daemon.json -- remove any "hosts" TCP listener; Unix socket only
{
  "hosts": ["unix:///var/run/docker.sock"]
}

# Verify no TCP listener remains, then restart:
ss -tlnp | grep -E ':(2375|2376)\b' || echo "OK: no Docker API TCP listener"
systemctl restart docker

2. Default-deny 2375/2376 at the host firewall

# nftables example -- default-deny inbound 2375/2376 except a management CIDR
nft add rule inet filter input tcp dport { 2375, 2376 } ip saddr != 10.10.0.0/24 drop

3. Find and kill privileged / host-mounted containers

docker ps -q | xargs -r docker inspect \
  --format '{{.Id}} {{.HostConfig.Privileged}} {{.HostConfig.Binds}}' \
  | awk '$2=="true"'
# Review the output, then for confirmed-malicious containers:
docker kill <container_id> && docker rm <container_id>

4. Enforce seccomp/AppArmor and rootless Docker

# Confirm no container is running unconfined:
docker ps -q | xargs -r docker inspect --format '{{.Id}} {{.HostConfig.SecurityOpt}}' | grep -i unconfined

# Rootless Docker install (per-host, run as the target non-root user):
curl -fsSL https://get.docker.com/rootless | sh

5. auditd rules for escape / immutability tampering

# /etc/audit/rules.d/carbonato.rules
-w /var/run/docker.sock -p rwxa -k docker_socket_watch
-a always,exit -F arch=b64 -S execve -F path=/usr/bin/nsenter -k container_escape
-a always,exit -F arch=b64 -S execve -F path=/usr/bin/chattr -k immutable_attr_change

# Load and verify:
augenrules --load
auditctl -l | grep -E 'docker_socket_watch|container_escape|immutable_attr_change'

Deploying via your RMM

The snippets above are raw shell/config. To wrap them for fleet deployment (admin checks, error handling, compliance-tracking writeback), hand them to your RMM deployment tooling and ask for a deployable package.

14

Containment Runbook

PhaseActionsOwnerEvidence to preserve
1. IsolateNetwork-isolate the host (Falcon network containment or firewall block) before killing any process, to preserve forensic stateIR / SOCFalcon containment record, timestamp
2. IdentifyConfirm the implant via Section 7 native checks: privileged/host-mounted containers, /opt/gh0st/, /root/.hermes/SOUL.md content, .docker-network-monitor, systemd-logind parent-process checkIRCommand output, docker inspect JSON
3. EradicateKill netd-svc / system/resolved containers; confirm and kill the masquerading systemd-logind PID; chattr -i every immutable persistence file before deleting; remove cron/systemd-timer/rc.local/OpenRC entries; remove operator-added SSH keys; remove implant images from local cacheIRBefore/after file listings, removed cron/unit contents
4. Rotate credentialsRotate every AI provider API key ever exposed on that host (assume all were harvested — loot priority #1 per the actor's own persona prompt); rotate SSH/service credentials; revoke and reissue registry credentialsIR + platform ownersRotation timestamps, key-usage audit before revocation
5. RebuildRebuild the host from a known-good image/IaC definition rather than trusting a cleaned host — the watchdog + immutable-file design is built to survive partial cleanupPlatform / IRNew build provenance record
6. Re-scan the /24This is a worm — assume lateral spread. Re-run Q11 and Q14 (and a direct port sweep) across the entire subnet before declaring the incident closedSOCSweep results, any newly identified hosts
7. Harden before returnApply Section 12 Immediate-tier controls to the rebuilt host before returning it to productionPlatform ownersHardening checklist sign-off
15

Detection Coverage Map

TechniqueBehaviorCQLIOACoverage
T1190Unauthenticated Docker API on :2375Q1, Q14—Partial
T1609/T1610Privileged container create/start via Docker APIQ1, Q3—Partial
T1611nsenter host PID-1 escapeQ2IOA-1Good
T1053.003/.006, T1037.004Cron/systemd-timer/rc.local/OpenRC persistenceQ4—Partial
T1222.002chattr +i immutabilityQ5IOA-2Good
T1036.004[kworker] argument spoofingQ6IOA-3Good
T1036.005systemd-logind/-resolved masqueradeQ7—Partial
T1572, T1021.004Reverse SSH tunnelQ8, Q9—Good
T1071.001C2 IP / LLM-proxy domain contactQ9, Q10—Good
T1102.002 (Telegram Bot API C2)Operator tasking over TelegramSection 7 (egress only)—GAP — channel content invisible; only egress to Telegram IP/domain space is observable
AI-orchestrated command relay (no ATT&CK ID published)LLM gateway interprets tasks, returns terminal commands——GAP — genuine taxonomy gap; no dedicated technique exists yet for this control-flow pattern
T1046, T1210/24 subnet sweep on :2375; repeated exploitationQ11—Partial
T1552.001, T1552AI API key / credential loot collectionQ12, Q15—Partial — env-var/string hunt is strong; SOUL.md file-content confirmation requires RTR, not CQL
T1496XMRig cryptominingQ13—Good

Honest coverage gaps

  • Inside-container visibility: any activity occurring strictly inside a container on infrastructure where no Falcon sensor is deployed (a managed/serverless container platform, or a Docker host where only a sidecar-less minimal runtime is present) is invisible to every query in this pack. All 15 queries assume a Falcon Linux sensor on the underlying host.
  • Unenrolled hosts: Docker hosts with no Falcon Linux sensor at all — including newly-provisioned cloud scratch VMs that are exactly the profile CARBONATO targets — have zero coverage until enrolled. Section 12's strategic "recurring exposure sweep" control exists specifically to find these before the worm does.
  • Docker API wire traffic: Falcon endpoint telemetry sees the resulting process activity, not the Docker Engine API HTTP request itself (the "Privileged":true / "Binds" JSON body). Confirming a container's actual privilege/mount configuration requires the native docker inspect check in Section 7, or network-layer (NDR/firewall) visibility.
  • Telegram C2 channel content: end-to-end within Telegram's own infrastructure. Falcon can only observe that a host contacted Telegram's IP/domain space generically — it cannot distinguish this operator's specific bot traffic from legitimate Telegram use on that host.
  • Arbitrary text file writes: the Falcon data model exposes typed file-write events (PE/ELF/Zip/Jar) but not a confirmed generic event for arbitrary text config writes. SOUL.md content changes, and the exact moment persistence files are written by an editor or heredoc, are hunted via process-execution proxies (Q4, Q15) rather than a native file-write event.

All queries assume telemetry is flowing and hosts have passed the validation gates in Section 11 before being trusted operationally.

16

Hunt Summary Ticket

TITLE:      CARBONATO Docker Botnet / AI-Agent (GH0ST) Hunt
SEVERITY:   High
SCOPE:      All Linux hosts running a Docker daemon, especially any with a
            network-reachable Docker API (tcp/2375 or 2376)
HYPOTHESIS: An unauthenticated Docker API is being used to deploy a privileged,
            host-escaping container that installs Hermes Agent under a
            malicious "GH0ST" persona, tunnels out over reverse SSH to Costa
            Rica, receives operator tasking via Telegram, mines XMRig, and
            propagates to every exposed host on the local /24 every 5 minutes.
QUERIES:    Q1-Q15 (Section 8); native checks in Section 7
DO FIRST:   Run Q14 (exposure audit) and the Section 7 daemon.json / ss check
            fleet-wide. Any host that fires is a live initial-access target
            regardless of infection status -- remediate exposure before
            chasing individual alerts.
FINDINGS:   Queries must be run manually in Falcon LogScale; this ticket
            describes what a positive result looks like, not a live result.
            A true positive is any Q1-Q13/Q15 hit corroborated by a Section 7
            native check (docker inspect Privileged=true, or SOUL.md
            containing "GH0ST").
GAPS:       Inside-container-only visibility, unenrolled hosts, Docker API
            wire traffic, Telegram channel content, arbitrary text-file
            writes -- see Section 15 for the full honest gap list.
ACTIONS:    Immediate-tier hardening (Section 12) on every Docker host;
            containment runbook (Section 14) on any confirmed host.
OWNER:      SOC / IR on-call
VERSION:    v0.1 -- 2026-09-30
17

Changelog

v0.12026-09-30Initial release. Built from the ThreatDown/Malwarebytes primary report (2026-09-22) plus four corroborating T2 sources. 15 CQL hunt queries, 3 Custom IOA recommendations, tiered hardening, and a phased containment runbook.
18

References

TierSourceUsed forAccess date
T1ThreatDown / Malwarebytes — CARBONATOPrimary intel, full attack chain, all IOCs2026-09-30
T2BleepingComputerCorroboration, AS262145 2nd source2026-09-30
T2The Hacker NewsCorroboration; related-campaign context (excluded from IOCs)2026-09-30
T2DarkReadingCorroboration (search synthesis, direct fetch 403)2026-09-30
T2SC WorldCorroboration (search synthesis, direct fetch 403)2026-09-30
T1MITRE ATT&CKTechnique IDs and names2026-09-30
T1CIS Docker BenchmarkHardening control citations2026-09-30
T1Docker Engine security documentationVendor hardening authority (daemon socket, rootless mode, registry auth)2026-09-30