NadMesh Botnet — AI & MCP Infrastructure Credential Hunt

Go-based mesh botnet harvesting cloud keys and Kubernetes service-account tokens from exposed AI/dev services · Linux & container-centric hunt pack
Threat
NadMesh (n4d mesh controller)
Severity
ACTIVE ITW
Type
Botnet / credential harvesting
Initial Access
Exposed AI & dev services, 20+ RCE vectors
Version
v0.2 · 2026-07-18
Author
HuntPack
Confidence
High (single-vendor primary, corroborated)
01

Executive Summary

The attacker objective is cloud credentials, not the AI service itself. NadMesh is a Go-based botnet that surfaced in early July 2026 and was documented by QiAnXin XLab. It scans for exposed AI and self-hosted developer infrastructure — ComfyUI, Ollama, n8n, Gradio, Open WebUI, Langflow, Marimo — exploits it through one of 20+ RCE vectors, and then immediately pivots to harvesting AWS access keys, Kubernetes service-account tokens, ~/.aws/config, .env files, and ~/.docker/config.json. Scanning, exploitation, credential theft, and AI/MCP service intelligence collection are integrated into one mesh-style infrastructure built on attacker-controlled VPS nodes.

The defensive thesis: a misconfigured GPU box is now a cloud-estate breach. The AI service is only the doorway — the value is the container runtime behind it, the cluster it is scheduled on, and the IAM role attached to the node. Teams stand these services up fast and firewall them late, and NadMesh is industrialising exactly that gap.

Highest-value defensive angle: the persistence trio is loud and cheap to hunt. Every infected host gets (1) a hidden .a loader dropped into /tmp, /var/tmp, and /dev/shm, (2) a hidden cron watchdog under /etc/cron.d, and (3) an SSH public key appended to authorized_keys. Those three artifacts are the cheapest reliable signal in this pack — Q1, Q2, and Q3 below.

On the operator scoreboard — read it as indicative, not authoritative. The operator's own dashboard claimed 3,811 unique AWS keys captured as of 2026-07-10. That figure is the operator's self-report, and the same panel is internally inconsistent: a counter reading 17,700 total deploys sits above a funnel claiming 95,700 deploys in the past 24 hours, and the success metrics exclude Ollama and AWS harvests entirely. Treat the numbers as evidence that harvesting is happening at scale, not as a reliable victim count. Do not repeat "3,811 AWS keys stolen" as a hard fact in exec reporting without this caveat.

Defender priority: inventory every internet-reachable AI/dev service on ports 8188, 11434, 5678, 7860, 3000 and 8080 today. If any host was reachable, assume the credentials that host could see are burned and rotate them — a NadMesh compromise is a credential-exposure event first and a malware event second.

02

Source Review & Web Hunter Notes

TierSourceKey FindingCarry Forward
TIER 1QiAnXin XLab — NadMesh Botnet Analysis: A Product-Grade Threat for the AI Service Era (July 2026)Primary technical analysis. Sole publisher of the atomic IOCs, the C2 protocol, the 30-port scan set, the exploit distribution percentages, and the persistence trio. Malware named for the n4d mesh controller string found in its artifacts.Yes — all atomic IOCs
TIER 2The Hacker News (2026-07-17)Corroborates the campaign, the AI-service targeting set (ComfyUI, Ollama, n8n, Open WebUI, Langflow, Gradio), the Shodan-harvester scan queue, the 3,811 AWS key operator claim, and the cluster-admin service-account objective. No independent IOCs.Partial — narrative only
TIER 2GBHackers (July 2026)Corroborates 20+ RCE vectors and the MCP/AI infrastructure targeting. Derivative of the XLab reporting.Partial — narrative only

Analyst decisions

  • Single-source IOC set — accepted with a ceiling. XLab published exactly one SHA1, one IP, and one domain. No additional hashes, IPs, or domains have been invented for this pack. If an atomic IOC is not in Section 4, it does not exist in public reporting as of 2026-07-18.
  • Behavioural detection is weighted over atomic IOCs. With five build versions running concurrently (11 bots on build 33.8-GO-TITAN, stragglers on 30.0) plus Garble obfuscation, UPX -9 packing and random padding, the published SHA1 covers one build of five. The hash query is included for completeness but is the weakest control in the pack.
  • Operator metrics flagged, not repeated. See the Executive Summary callout — the dashboard is internally inconsistent and its own success counters exclude Ollama and AWS harvests.
  • HTTP URI hunting moved to process telemetry. The C2 endpoints (/api/beacon, /api/task/, /api/result/) are documented, but Falcon Linux EDR does not surface HTTP request URIs as first-class fields. These are hunted through CommandLine (curl/wget invocations) rather than through HTTP fields that most tenants will not have. Proxy/NGSIEM log sources are the right place for true URI hunting — noted as a gap in Section 15.
03

Hunt Brief & Attack Chain

Attack chain

#StepTelemetryHunt Angle
1Target discovery. Embedded scanner preloaded with address ranges from 90+ cloud providers; a Shodan API harvester keeps the queue stocked with ComfyUI, Ollama, n8n, Open WebUI, Langflow and Gradio endpoints.External — perimeter/ASMExposure-side: what of ours is on 8188 / 11434 / 5678 / 7860? (Q9, Section 7)
2Scanning. 30-port sweep. Subnets with hits rescanned every 5 min; "dangerous" IPs rescanned as /32 every 15 min; a target is auto-blacklisted after 10 failed deploys (suspected honeypot); random /24s generated when the queue depletes.NetworkConnectIP4Outbound fan-out from an owned host on the 30-port set (Q10)
3Exploitation. 20+ RCE vectors. By observed traffic share: Docker containers API 30.31%, Jenkins script text 22.28%, Telnet weak passwords 10.36%, Redis 8.29%, MCP command execution 0.78%. Also Kubernetes (API / kubelet / etcd) incl. pod escapes, Elasticsearch script RCE, Spring Cloud Gateway, code-server, Airflow, Superset, XXL-Job, WebLogic deserialization, SSH.ProcessRollup2Container/orchestrator abuse from a web-tier parent (Q12)
4Beacon & delivery. /api/beacon serves the initial infection shell script — which itself carries the SSH backdoor. Controller on ports 80 / 8443, spoofed nginx headers.ProcessRollup2, NetworkConnectIP4, DnsRequestcurl/wget of the beacon path, and download-piped-to-shell (Q5, Q6)
5Persistence trio ("double protection"). SSH public-key injection into ~/.ssh/authorized_keys; multi-path disk loaders /dev/shm/.a, /var/tmp/.a, /tmp/.a; cron watchdogs /etc/cron.d/.sys_monitor and /etc/cron.d/.s that revive the bot if any single artifact is removed.ElfFileWritten, ProcessRollup2Q1, Q2, Q3 — the highest-fidelity trio in this pack
6Execution. Go agent, Garble-obfuscated, UPX -9 packed, random padding. Five builds concurrent.ProcessRollup2Hidden ELF executing from a world-writable path (Q8)
7Credential harvesting. Cloud access keys from environment variables, Kubernetes service-account tokens (incl. cluster-admin), ~/.aws/config, .env, ~/.docker/config.json. MCP tools capable of arbitrary SQL or shell execution are catalogued as follow-on capability.ProcessRollup2Secret-file access patterns (Q7) — this is the impact step
8Mesh reporting. Bot pulls tasks from /api/task/<nid>, submits findings to /api/result/<nid>, operator drives /panel.NetworkConnectIP4, DnsRequestAtomic C2 IOCs (Q4), controller-port beaconing (Q11)

Hunt hypotheses (ordered by fidelity)

#HypothesisMITREConfidence
H1A NadMesh loader named .a has been written to a world-writable directory on a Linux host.T1105, T1036.005HIGH
H2A hidden cron watchdog has been created under /etc/cron.d to revive a removed implant.T1053.003HIGH
H3An attacker public key has been appended to a user's authorized_keys.T1098.004MEDIUM
H4A host has resolved or connected to the published NadMesh C2 infrastructure.T1071.001HIGH
H5A process retrieved the beacon shell script from a controller and piped it to a shell.T1105, T1059.004HIGH
H6A process has read cloud/container secret material (~/.aws/config, .env, docker config, K8s SA token) in a pattern inconsistent with the host's role.T1552.001, T1552.007, T1528MEDIUM
H7A hidden, packed ELF is executing from /tmp, /var/tmp or /dev/shm.T1027.002, T1036.005MEDIUM
H8An internal host is fanning out across the NadMesh 30-port scan set — i.e. it is now a scanning node.T1046HIGH
H9An AI/dev service is listening on a NadMesh priority port and is reachable beyond its intended boundary.T1190 (exposure)MEDIUM
H10The Docker API or a container runtime was abused to deploy a container or escape to the host.T1610, T1611, T1552.007MEDIUM
04

Consolidated IOC Table

Provenance note: every value below is published verbatim by QiAnXin XLab. XLab released exactly one SHA1, one IP and one domain — no additional hashes, addresses or domains have been generated, inferred, or extrapolated for this pack.

TypeValueConfActionContextExpiry
IPv4209.99.186[.]235HIGHdetectNadMesh C2 / controller node2027-01-18
Domaincdnorigin[.]netHIGHdetectNadMesh C2 domain2027-01-18
SHA131c69b3e12936abca770d430066f379ec1d997ecHIGHhuntGo agent sample. SHA1 only — no SHA256 published. Covers one of five concurrent builds.2026-10-18
File path/tmp/.aHIGHdetectDisk-backed loader (persistence trio)2027-01-18
File path/var/tmp/.aHIGHdetectDisk-backed loader (persistence trio)2027-01-18
File path/dev/shm/.aHIGHdetectMemory-backed loader (persistence trio)2027-01-18
File path/etc/cron.d/.sys_monitorHIGHdetectHidden cron watchdog2027-01-18
File path/etc/cron.d/.sHIGHdetectHidden cron watchdog2027-01-18
File path~/.ssh/authorized_keysMEDhuntSSH public-key backdoor target. Legitimate file — the modification is the indicator, not the path.2026-10-18
HTTP headerX-Mesh-Auth: <node_id>:<hex_hmac>HIGHdetectBot auth. HMAC-SHA256, ±60 s time window.2027-01-18
HTTP headerX-Operator-KeyHIGHdetectOperator auth — plaintext comparison2027-01-18
URI path/api/beaconHIGHdetectServes the initial infection shell script (carries the SSH backdoor)2027-01-18
URI path/api/task/<nid>HIGHdetectBot pulls scan tasks2027-01-18
URI path/api/result/<nid>HIGHdetectOpen submission of scan findings2027-01-18
URI path/panelMEDhuntWeb management dashboard. Generic path — low standalone value.2026-10-18
Port80, 8443MEDenrichController listeners (spoofed nginx headers)2026-08-18
Stringn4d mesh controllerHIGHhuntControl branding in artifacts — the string the family is named for2027-01-18
Stringn4d_panel cookie = sha256(opKey + "YYYY-MM-DD-HH")MEDhuntPanel session cookie scheme2026-10-18
Build ID33.8-GO-TITAN, 30.0MEDenrichTwo of five concurrent build versions2026-10-18

CVEs in the NadMesh scanning queue

CVEProductAffectedNote
CVE-2026-39987Marimo notebooksbefore 0.23.0Pre-auth RCE
CVE-2026-41176rclone RC1.45.0 – 1.73.5Auth bypass on the remote-control interface
CVE-2022-22947Spring Cloud Gatewaysee vendor advisorySpEL injection RCE — long-known, still swept
CVE-2017-12611Apache Strutssee vendor advisoryFreemarker tag OGNL RCE — nine years old and still in the queue

Full 30-port scan set (verbatim)

80, 443, 3000, 5000, 8000, 8080, 8443, 8888, 9000, 9999, 6443, 10250, 9200, 22, 23, 8088, 2718, 8090, 10000, 2379, 8848, 8265, 8188, 5678, 11434, 7860, 5432, 3306, 2375, 2376, 6379

Priority AI/dev targets within that set: 8188 ComfyUI · 11434 Ollama · 5678 n8n · 7860 Gradio · plus Open WebUI, Langflow and Marimo on common web ports. Infrastructure ports of note: 2375/2376 Docker API · 6443 Kubernetes API · 10250 kubelet · 2379 etcd · 6379 Redis · 9200 Elasticsearch.

05

Affected Surface & Telemetry Matrix

SurfaceRequired TelemetryPriorityGap Risk
Linux servers / GPU & ML hostsFalcon Linux sensor — ProcessRollup2, ElfFileWritten, NetworkConnectIP4, DnsRequestCRITICALHIGH — GPU/ML boxes are frequently built outside the standard image and ship without a sensor
Container workloadsFalcon Container sensor / Kubernetes ProtectionCRITICALHIGH — node-only sensor coverage misses in-container process activity; Docker API abuse is the top vector at 30.31%
Kubernetes control planeK8s audit logs, kubelet logs, etcd access logsCRITICALMEDIUM — audit logging often sampled or off in self-managed clusters
Cloud IAM (AWS)CloudTrail, GuardDuty, IAM Access Analyzer, Access Key last-usedCRITICALMEDIUM — this is where stolen keys are actually used; endpoint telemetry alone will miss the impact
Perimeter / external attack surfaceASM or external scan of the 30-port setHIGHHIGH — shadow AI services are the entire premise of this campaign
Web proxy / egressProxy or NGSIEM HTTP logs (URI-level)MEDIUMHIGH — needed for true /api/beacon URI hunting; Falcon EDR does not carry HTTP URIs
CI/CD & dev tooling (Jenkins, code-server, Airflow, Superset, XXL-Job)Application logs + Falcon on the hostHIGHMEDIUM — Jenkins script console is the #2 vector at 22.28%
Data services (Redis, Elasticsearch, Postgres, MySQL)Falcon on host + service auth logsMEDIUMMEDIUM — Redis at 8.29% of traffic
06

ATT&CK Mapping

TacticTechniqueObserved BehaviorQuery / Control
ReconnaissanceT1595.002 — Active Scanning: Vulnerability Scanning30-port sweep across 90+ cloud provider ranges; Shodan API harvester stocks the queueQ9, Q10 / M1056
DiscoveryT1046 — Network Service DiscoveryCompromised hosts become scanning nodes; 5-minute subnet rescan, 15-minute /32 rescanQ10 / M1030
Initial AccessT1190 — Exploit Public-Facing Application20+ RCE vectors against AI/dev services and middlewareQ12, Section 12 / M1051, M1030
Initial AccessT1133 — External Remote ServicesTelnet weak passwords (10.36%), SSHQ10 / M1032, M1035
ExecutionT1059.004 — Unix ShellBeacon shell script piped straight to a shellQ5, Q6 / M1038
ExecutionT1610 — Deploy ContainerDocker containers API RCE — top vector at 30.31%Q12 / M1035
Privilege EscalationT1611 — Escape to HostKubernetes pod escapes; host-path container mountsQ12 / M1047, M1038
PersistenceT1053.003 — Scheduled Task/Job: Cron/etc/cron.d/.sys_monitor, /etc/cron.d/.s watchdogsQ2 / M1018, M1022
PersistenceT1098.004 — Account Manipulation: SSH Authorized KeysPublic-key injection into ~/.ssh/authorized_keysQ3 / M1027, M1032
Defense EvasionT1027.002 — Obfuscated Files: Software PackingGarble obfuscation + UPX -9 + random padding; five concurrent buildsQ8 / M1049
Defense EvasionT1036.005 — Masquerading: Match Legitimate Name or LocationHidden .a files in world-writable dirs; hidden cron names; spoofed nginx response headersQ1, Q8 / M1022
Credential AccessT1552.001 — Unsecured Credentials: Credentials In Files~/.aws/config, .env, ~/.docker/config.json exfiltrationQ7 / M1041, M1047
Credential AccessT1552.007 — Container APIDocker socket / API abuse to read container config and secretsQ7, Q12 / M1035
Credential AccessT1528 — Steal Application Access TokenKubernetes service-account tokens, incl. cluster-admin; AI model access tokensQ7 / M1026
Command & ControlT1071.001 — Application Layer Protocol: Web ProtocolsHTTP controller on 80/8443, X-Mesh-Auth HMAC beaconing, spoofed nginx headersQ4, Q11 / M1037
Command & ControlT1105 — Ingress Tool Transfer/api/beacon delivers the infection script; multi-path loader dropQ1, Q5, Q6 / M1031
Impact / follow-onT1078.004 — Valid Accounts: Cloud AccountsHarvested AWS keys and K8s service accounts reused against the cloud estateCloudTrail (Section 7) / M1026, M1032
07

Native Audit-Log Hunts (non-CQL)

These run outside Falcon and cover the parts of the chain endpoint telemetry cannot see — especially the cloud-side impact, which is where a NadMesh compromise actually costs you.

AWS — did stolen keys get used?

  • CloudTrail: hunt for API calls from an unfamiliar sourceIPAddress using a key whose normal caller is an EC2/EKS node. Long-lived keys pulled from a .env file will suddenly be used from outside your ranges.
  • Reconnaissance burst: sts:GetCallerIdentity, iam:ListUsers, iam:ListAttachedUserPolicies, s3:ListBuckets, ec2:DescribeInstances clustered within minutes is the classic "what did I just steal" sequence.
  • IAM Access Advisor / key last-used: for every key that lived on a reachable AI host, compare last-used region and service against the host's normal behaviour.
  • GuardDuty: prioritise UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration and Recon:IAMUser/* findings dated on or after 2026-07-01.

Kubernetes — audit log

  • Anonymous or system:anonymous requests accepted by the API server on 6443.
  • Any successful request to the kubelet read/write API on 10250 from a non-control-plane source (/run, /exec, /pods).
  • etcd on 2379 reachable without client-cert auth — that is a full secret dump in one request.
  • Service accounts bound to cluster-admin: enumerate every ClusterRoleBinding to cluster-admin and justify each one. NadMesh specifically prizes these.
  • Pod creations with hostPID, hostNetwork, privileged: true, or a hostPath mount of / — the escape primitives.

Application / service logs

  • Ollama (11434): any request from outside the host. Ollama has no auth by default — an external request is a finding, not an anomaly.
  • ComfyUI (8188) / Gradio (7860) / n8n (5678) / Open WebUI / Langflow: access from non-corporate IP space; unexpected workflow, custom-node, or extension creation.
  • Jenkins: /script (script console) access in the access log — 22.28% of NadMesh exploitation traffic. Any hit here is high severity.
  • Marimo: confirm version 0.23.0 or later (CVE-2026-39987 pre-auth RCE).
  • rclone: confirm the RC interface is not exposed and the version is outside 1.45.0–1.73.5 (CVE-2026-41176).
  • Redis (6379) / Elasticsearch (9200): unauthenticated access; Redis CONFIG SET dir / SLAVEOF commands from unexpected clients.

Perimeter

  • External scan of your own ranges across the full 30-port set in Section 4. Anything answering on 8188, 11434, 5678, 7860, 2375, 2376, 6443, 10250, 2379 or 6379 from the internet is an open finding.
  • Firewall/proxy: outbound to 209.99.186[.]235 or cdnorigin[.]net; and HTTP requests to URI paths /api/beacon, /api/task/, /api/result/.
08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.

Escaping convention used throughout: nearly every NadMesh IOC is a Unix path, and a bare / inside a CQL /.../ regex closes the literal early and silently breaks the query. Two idioms are used here: match the basename via FileName (no slash at all — e.g. FileName=/^\.a$/i), and where a directory genuinely matters, use a quoted wildcard string match (TargetFileName="*/tmp/.a") or an explicitly escaped slash (\/). No query below contains an unescaped slash inside a regex literal.

Q1 · NadMesh disk-backed loader — hidden ".a" written to a world-writable path
CONF HIGHFP LOWCOST LOW

Looks for: the multi-path loader from the NadMesh persistence trio — a hidden file literally named .a dropped into /tmp, /var/tmp or /dev/shm. This is the single highest-fidelity signal in the pack. FP: low, but not zero — some build toolchains emit static-library artifacts named .a; those are normally lib*.a under a build tree, not a bare dotfile in /tmp.

// HUNT: NadMesh disk-backed loader — hidden ".a" in world-writable paths
// MITRE: T1105, T1036.005
// CONF: high  FP: low  COST: low
// REQUIRES: Falcon Linux sensor file-write telemetry
// FALSE POSITIVES: build systems emitting ar(1) static libraries named ".a"
// TUNING: exclude known build hosts by aid, or ContextBaseFileName in (gcc, ld, ar, make)
#event_simpleName=/ELFFileWritten|NewExecutableWritten/
| FileName=/^\.a$/i
| (TargetFileName="*/tmp/.a" or TargetFileName="*/var/tmp/.a" or TargetFileName="*/dev/shm/.a")
| table([@timestamp, aid, ComputerName, UserName, TargetFileName, FileName, ContextBaseFileName, FileSize], limit=200)
Q2 · Hidden cron watchdog created under /etc/cron.d
CONF HIGHFP LOWCOST LOW

Looks for: the cron half of the persistence trio — process activity referencing the hidden watchdog files .sys_monitor or .s under /etc/cron.d. Hunted through CommandLine because cron-file writes are not reliably surfaced as a dedicated file event on Linux; a companion basename filter on file telemetry is in Q2b below. FP: low — a dot-prefixed file in /etc/cron.d is anomalous by itself, and cron actually ignores names containing a dot, which is precisely why the operator chose them (the entries are invisible to a casual ls and inert to cron's own parser until invoked directly).

// HUNT: NadMesh cron watchdog — hidden .sys_monitor / .s under /etc/cron.d
// MITRE: T1053.003
// CONF: high  FP: low  COST: low
// REQUIRES: Falcon Linux sensor process telemetry
// FALSE POSITIVES: admin scripts that enumerate /etc/cron.d during audits
// TUNING: exclude ParentBaseFileName in (ansible, puppet, chef-client, cloud-init, aide, tripwire)
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/cron\.d\/\.(sys_monitor|s)(\s|$|")/i
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, GrandparentBaseFileName], limit=200)
Q2b · Hidden cron watchdog — file-write basename variant
CONF HIGHFP LOWCOST LOW

Looks for: the same artifacts from the file-write side, matched on basename so there is no path slash to escape. FP: low. Caveat: generic (non-executable) file-write visibility on Linux varies by sensor version and policy — if this returns zero events across a long window while Q2 returns hits, your tenant is not emitting the file event and Q2 is your authoritative control.

// HUNT: NadMesh cron watchdog — file-write basename match
// MITRE: T1053.003
// CONF: high  FP: low  COST: low
// REQUIRES: Linux file-write telemetry  (⚠ validate in tenant — generic file-write
//           coverage on Linux varies by sensor version / policy)
// FALSE POSITIVES: none expected — dot-prefixed cron.d entries are not a normal admin pattern
#event_simpleName=/ELFFileWritten|NewExecutableWritten/
| FileName=/^\.(sys_monitor|s)$/i
| TargetFileName="*/etc/cron.d/*"
| table([@timestamp, aid, ComputerName, UserName, TargetFileName, FileName, ContextBaseFileName], limit=200)
Q3 · SSH authorized_keys tampering (public-key backdoor injection)
CONF HIGHFP HIGHCOST MED

Looks for: a process appending to authorized_keys — the third leg of the persistence trio, delivered by the /api/beacon script itself. FP: high, and honestly so. Configuration management legitimately rewrites this file constantly. This query is only useful with the exclusions applied — in an estate running Ansible or cloud-init at scale, the unfiltered version is unusable. Treat it as a hunt with a tuned allowlist of writers, never as a standalone alert.

// HUNT: SSH authorized_keys modification (NadMesh public-key backdoor)
// MITRE: T1098.004
// CONF: high  FP: high  COST: medium
// REQUIRES: Falcon Linux sensor process telemetry
// FALSE POSITIVES: Ansible/Puppet/Chef/Salt, cloud-init, ssh-copy-id, provisioning and
//   golden-image pipelines, IdP key-sync agents. Volume will be dominated by these.
// TUNING: exclude the parents below, then baseline the surviving writers per host role
//   and alert only on writers that are NOT on the allowlist for that role.
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/authorized_keys/i
| CommandLine=/\b(echo|printf|cat|tee|sed|curl|wget|python3?|perl)\b/i
| ParentBaseFileName!=/^(ansible|python3|puppet|chef-client|salt-minion|cloud-init|ssh-copy-id)/i
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, GrandparentBaseFileName], limit=200)
Q4 · Atomic IOC quick-triage — C2 IP, C2 domain, agent SHA1
CONF HIGHFP LOWCOST LOW

Looks for: any contact with the three published atomic indicators, in one pass. Run this first — it is the cheapest possible answer to "are we already in this?". FP: low. Critical caveat on the hash: XLab published a SHA1, and most Falcon tenants index and surface SHA256HashData as the primary process hash. SHA1HashData is a real data-model field but is not populated in every tenant or every event type — if the hash clause never fires, that is a telemetry gap, not a clean result. Confirm SHA1HashData is populated in your tenant before drawing a negative conclusion, and lean on Q1/Q2 as the authoritative controls.

// HUNT: NadMesh atomic IOCs — C2 IP / C2 domain / agent SHA1
// MITRE: T1071.001, T1105
// CONF: high  FP: low  COST: low
// REQUIRES: NetworkConnectIP4, DnsRequest, ProcessRollup2 (+ SHA1HashData populated)
// FALSE POSITIVES: none expected — these are campaign-specific published indicators
// NOTE: ⚠ validate in tenant — SHA1HashData is a valid Falcon field but is not populated
//   in every tenant/event type. A silent zero on the hash clause may be a gap, not a clear.
// NOTE: the published SHA1 covers ONE of five concurrent builds (Garble + UPX -9 + random
//   padding), so hash coverage is inherently partial. Behavioural queries carry this pack.
#event_simpleName=/NetworkConnectIP4|DnsRequest|ProcessRollup2|SyntheticProcessRollup2|ELFFileWritten/
| RemoteAddressIP4="209.99.186.235"
   or DomainName=/(^|\.)cdnorigin\.net$/i
   or SHA1HashData="31c69b3e12936abca770d430066f379ec1d997ec"
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, RemoteAddressIP4, RemotePort, DomainName, SHA1HashData, SHA256HashData], limit=200)
Q5 · Beacon retrieval — curl/wget against NadMesh controller API paths
CONF HIGHFP LOWCOST LOW

Looks for: the NadMesh controller URI paths (/api/beacon, /api/task/, /api/result/) appearing on a command line — i.e. the infection script being fetched, or the bot invoking its task/result endpoints via a shell helper. FP: low; /api/beacon is a distinctive path. Scope note: this only sees C2 traffic that passes through a command line. Agent-internal HTTP from the Go binary will not appear here — that is Q11's job, and true URI-level coverage needs proxy logs (see Section 15).

// HUNT: NadMesh beacon/task/result API paths on a command line
// MITRE: T1105, T1071.001
// CONF: high  FP: low  COST: low
// REQUIRES: Falcon Linux sensor process telemetry
// FALSE POSITIVES: internal apps that happen to expose an /api/result or /api/task route
// TUNING: if noisy, require the curl/wget clause AND a non-RFC1918 destination
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/\b(curl|wget|fetch|python3?)\b/i
| CommandLine=/api\/(beacon|task|result)\b/i
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, GrandparentBaseFileName], limit=200)
Q6 · Remote script piped directly into a shell
CONF HIGHFP MEDCOST MED

Looks for: the delivery primitive — curl … | sh. NadMesh's /api/beacon endpoint serves a shell script that installs the SSH backdoor, and this pattern catches it regardless of which controller served it. FP: medium. This is also how a great deal of legitimate software installs itself (Rust, Homebrew, Docker convenience scripts, many CI bootstraps), so expect real volume on developer and build hosts. It earns its place because it catches NadMesh variants whose C2 you do not yet know.

// HUNT: remote script piped to a shell (NadMesh beacon delivery primitive)
// MITRE: T1105, T1059.004
// CONF: high  FP: medium  COST: medium
// REQUIRES: Falcon Linux sensor process telemetry
// FALSE POSITIVES: legitimate install one-liners (rustup, homebrew, get.docker.com,
//   nvm, CI bootstrap scripts). Heavy on dev/build hosts.
// TUNING: exclude known-good install domains in CommandLine, and exclude build-agent aids;
//   prioritise hits on production/GPU hosts where no one should be curl-piping anything.
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/\b(curl|wget)\b/i
| CommandLine=/\|\s*(sudo\s+)?(ba|da|z|k)?sh\b/i
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, GrandparentBaseFileName], limit=200)
Q7 · Cloud & container secret harvesting (the impact step)
CONF HIGHFP MEDCOST MED

Looks for: the behaviour that actually costs you money — a process touching ~/.aws/config or credentials, ~/.docker/config.json, .env files, or the Kubernetes service-account token path. This is the NadMesh objective, and it is the query to run even if every other query is clean. FP: medium. Legitimate tooling reads these constantly — the AWS CLI reads its own config, kubelet reads SA tokens, deploy scripts read .env. The signal is in who is reading them: an unexpected parent (a web service, a shell spawned by a container runtime) reading all four categories in quick succession is the pattern.

// HUNT: NadMesh credential harvesting — AWS config, docker config, .env, K8s SA token
// MITRE: T1552.001, T1552.007, T1528
// CONF: high  FP: medium  COST: medium
// REQUIRES: Falcon Linux sensor process telemetry
// FALSE POSITIVES: aws-cli, kubectl, kubelet, docker/compose, terraform, deploy scripts,
//   backup jobs, and secret-scanning tools all legitimately read these paths.
// TUNING: exclude FileName in (aws, kubectl, kubelet, docker, terraform, helm) and exclude
//   ParentBaseFileName in (systemd, containerd, dockerd). Then rank by hosts where a SINGLE
//   process touched MORE THAN ONE of the four secret categories inside a few seconds.
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/(\.aws\/(config|credentials)|\.docker\/config\.json|serviceaccount\/token|\.env(\s|$|"))/i
| CommandLine=/\b(cat|grep|egrep|head|tail|tar|base64|curl|python3?|xxd|strings|cp)\b/i
| FileName!=/^(aws|kubectl|kubelet|docker|terraform|helm)$/i
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, GrandparentBaseFileName], limit=200)
Q8 · Hidden ELF executing from a world-writable path
CONF HIGHFP MEDCOST LOW

Looks for: execution of a dot-prefixed binary out of /tmp, /var/tmp or /dev/shm — the runtime half of Q1, and the shape of a UPX-packed Go agent regardless of build version. Because NadMesh runs five concurrent obfuscated builds, this behavioural query outlives any hash. FP: medium. Installers, package post-install scripts and some CI runners genuinely execute from /tmp; the dot-prefix requirement removes most of that, but not all.

// HUNT: hidden binary executing from a world-writable path (NadMesh Go agent shape)
// MITRE: T1027.002, T1036.005
// CONF: high  FP: medium  COST: low
// REQUIRES: Falcon Linux sensor process telemetry
// FALSE POSITIVES: installers and package post-install scripts staging in /tmp; some CI
//   runners execute hidden helper binaries from /tmp.
// TUNING: exclude build/CI aids; exclude ParentBaseFileName in (dpkg, rpm, apt, yum, dnf).
//   Cross-reference any hit against Q1 — same host in both is a confirmed infection.
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| (ImageFileName="*/tmp/*" or ImageFileName="*/var/tmp/*" or ImageFileName="*/dev/shm/*")
| FileName=/^\./
| ParentBaseFileName!=/^(dpkg|rpm|apt|apt-get|yum|dnf)$/i
| table([@timestamp, aid, ComputerName, UserName, ImageFileName, FileName, CommandLine, ParentBaseFileName, SHA256HashData], limit=200)
Q9 · Exposure hunt — AI/dev services listening on NadMesh priority ports
CONF MEDFP MEDCOST LOW

Looks for: your own attack surface — which hosts are running ComfyUI (8188), Ollama (11434), n8n (5678) or Gradio (7860), plus the infrastructure ports NadMesh prizes (Docker API 2375/2376, K8s API 6443, kubelet 10250, etcd 2379, Redis 6379). This is a preventive hunt: it produces an inventory, not an alert. FP: "medium" only in the sense that most results are legitimate services — the finding is any of them being reachable from outside its intended boundary, which this query cannot tell you on its own. Pair it with the external scan in Section 7.

// HUNT: inventory of AI/dev and infra services on NadMesh priority ports
// MITRE: T1190 (exposure-side)
// CONF: medium  FP: medium  COST: low
// REQUIRES: NetworkListenIP4 telemetry
// FALSE POSITIVES: by design — most hits are legitimate internal services. This query
//   builds an inventory; exposure must be confirmed externally.
// TUNING: none. Review the full output as an asset list, then externally scan the hosts.
#event_simpleName=/NetworkListenIP4/
| LocalPort=8188 or LocalPort=11434 or LocalPort=5678 or LocalPort=7860
   or LocalPort=2375 or LocalPort=2376 or LocalPort=6443 or LocalPort=10250
   or LocalPort=2379 or LocalPort=6379 or LocalPort=9200 or LocalPort=8265
| groupBy([ComputerName, aid, LocalPort, LocalAddressIP4, FileName], function=[count(as=listen_events)])
| sort(field=LocalPort, order=asc, limit=1000)
Q10 · Internal host fanning out across the NadMesh 30-port scan set
CONF HIGHFP LOWCOST HIGH

Looks for: a host that has become a NadMesh scanning node. The embedded scanner rescans hit subnets every 5 minutes and dangerous /32s every 15, so an infected host generates a very distinctive fan-out: many distinct destinations on a small set of service ports. FP: low once vulnerability scanners are excluded — but those exclusions are mandatory or your scanner fleet will be the entire result set. COST: high — this aggregates raw network events; run it over a bounded window (a few hours) rather than 7 days.

// HUNT: outbound fan-out on the NadMesh 30-port scan set (host is now a scan node)
// MITRE: T1046, T1595.002
// CONF: high  FP: low  COST: high
// REQUIRES: NetworkConnectIP4 telemetry. Run over a BOUNDED window (2-6h), not 7d.
// FALSE POSITIVES: authorised vulnerability scanners, service-discovery/monitoring agents,
//   container orchestrators health-checking large pod fleets, load balancers.
// TUNING: exclude your scanner aids FIRST, then monitoring agents by FileName
//   (nessus, nmap, masscan, zabbix, prometheus, consul, telegraf).
// Threshold below is a starting point — baseline your estate before promoting.
#event_simpleName=/NetworkConnectIP4/
| ConnectionDirection=0
| RemotePort=2375 or RemotePort=2376 or RemotePort=6379 or RemotePort=6443
   or RemotePort=10250 or RemotePort=2379 or RemotePort=9200 or RemotePort=8188
   or RemotePort=11434 or RemotePort=5678 or RemotePort=7860 or RemotePort=8265
   or RemotePort=8848 or RemotePort=2718 or RemotePort=23
| FileName!=/^(nmap|masscan|zmap|nessusd|zabbix_agentd|telegraf|prometheus|consul)$/i
| groupBy([aid, ComputerName, FileName], function=[count(field=RemoteAddressIP4, distinct=true, as=distinct_targets), count(field=RemotePort, distinct=true, as=distinct_ports)])
| distinct_targets >= 50
| sort(field=distinct_targets, order=desc, limit=200)
Q11 · NadMesh controller artifact strings (n4d mesh / X-Mesh-Auth)
CONF MEDFP LOWCOST LOW

Looks for: the family's own branding and protocol strings on a command line — n4d mesh controller, the X-Mesh-Auth / X-Operator-Key headers, and the n4d_panel cookie name. Catches manual operator interaction, script-based beaconing, and analyst-facing artifacts. FP: low — these strings are essentially unique. CONF: medium only because the compiled Go agent sets these headers internally, where a command line will never see them; this fires on the shell-script and hands-on-keyboard portions of the chain.

// HUNT: NadMesh controller branding and protocol strings on a command line
// MITRE: T1071.001
// CONF: medium  FP: low  COST: low
// REQUIRES: Falcon Linux sensor process telemetry
// FALSE POSITIVES: none expected — strings are campaign-unique
// NOTE: the Go agent sets X-Mesh-Auth internally; this only catches the shell-script and
//   hands-on-keyboard portions. Proxy header logging is the complete control (see s15).
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/(n4d[\s_-]?mesh|n4d_panel|X-Mesh-Auth|X-Operator-Key)/i
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, GrandparentBaseFileName], limit=200)
Q12 · Docker API abuse & container escape primitives (top exploit vector)
CONF HIGHFP MEDCOST MED

Looks for: the #1 NadMesh exploitation vector — Docker containers API RCE at 30.31% of observed traffic — plus the escape primitives it leads to: remote Docker daemon targeting via -H tcp://…:2375, privileged containers, host-PID/host-network, and a hostPath mount of /. FP: medium. Legitimate platform tooling does run privileged containers and mount host paths (monitoring agents, CSI drivers, node-exporters are the usual offenders). The high-signal combination is a remote Docker host flag on a machine that is not a CI runner.

// HUNT: Docker API abuse and container escape primitives (NadMesh top vector, 30.31%)
// MITRE: T1610, T1611, T1552.007
// CONF: high  FP: medium  COST: medium
// REQUIRES: Falcon Linux sensor process telemetry (host-level; container sensor preferred)
// FALSE POSITIVES: monitoring/CSI/node-exporter DaemonSets legitimately run privileged and
//   mount host paths; CI runners legitimately target remote Docker daemons.
// TUNING: exclude CI-runner aids and known platform DaemonSet images; alert first on the
//   remote-daemon clause (-H tcp) seen on a non-CI host.
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/\b(docker|ctr|crictl|nerdctl|kubectl)\b/i
| CommandLine=/(-H\s+tcp:|--host\s+tcp:|:2375|:2376|--privileged|--pid=host|--net(work)?=host|-v\s+\/:|--volume\s+\/:|\/var\/run\/docker\.sock)/i
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, GrandparentBaseFileName], limit=200)
09

CrowdStrike Custom IOA Recommendations

Only queries with genuinely low FP risk belong in a blocking IOA. Three qualify; the rest stay hunt/investigate-only.

IOA NameSource QueryPatternActionExclusions
NadMesh — Hidden .a Loader in World-Writable PathQ1File write, image path matches .*/(tmp|var/tmp|dev/shm)/\.a$Block / KillExclude designated build hosts by host group
NadMesh — Hidden Cron WatchdogQ2 / Q2bProcess command line matches cron\.d/\.(sys_monitor|s)\bBlock / KillExclude file-integrity tooling (aide, tripwire) as parent
NadMesh — Controller Artifact StringsQ11Command line matches n4d[\s_-]?mesh|X-Mesh-Auth|X-Operator-Key|n4d_panelDetect — HighExclude this HuntPack file's own path on analyst workstations
NadMesh — Hidden Binary Exec from World-Writable PathQ8Process exec, image path in /tmp, /var/tmp, /dev/shm with dot-prefixed nameDetect — MediumExclude package managers as parent; exclude CI host group. Baseline before enabling.

Do not promote Q3, Q6, Q7, Q10 or Q12 to a blocking IOA. Each has a legitimate high-volume analogue — configuration management rewriting authorized_keys, install one-liners piping curl to sh, the AWS CLI reading its own config, scanners fanning out, platform DaemonSets running privileged. Blocking on these will break production before it catches an implant. They are hunts.

Falcon Custom IOC ingestion

Add 209.99.186.235 (IPv4) and cdnorigin.net (domain) as detect/prevent IOCs. Note that Falcon IOC Management does not accept SHA1 — only SHA256 and MD5 — so the published agent hash cannot be uploaded as a custom IOC. Hunt it with Q4 instead, and understand that hash-based coverage here is partial by design (five concurrent obfuscated builds, one published hash).

10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
ipv4,209.99.186.235,detect,critical,2027-01-18,NadMesh C2 controller node,campaign:NadMesh
domain,cdnorigin.net,detect,critical,2027-01-18,NadMesh C2 domain,campaign:NadMesh
# NOTE: Falcon IOC Management accepts sha256 and md5 only.
# The single published NadMesh sample hash is a SHA1 and CANNOT be imported here:
#   sha1  31c69b3e12936abca770d430066f379ec1d997ec
# Hunt that hash with Q4 (SHA1HashData) instead. No SHA256 has been published.
Atomic IOCs — JSONmachine-readable
{
  "campaign": "NadMesh",
  "published": "2026-07",
  "source": "QiAnXin XLab",
  "pack_version": "v0.1",
  "pack_date": "2026-07-18",
  "network": {
    "c2_ipv4": ["209.99.186.235"],
    "c2_domain": ["cdnorigin.net"],
    "controller_ports": [80, 8443],
    "uri_paths": ["/api/beacon", "/api/task/", "/api/result/", "/panel"],
    "headers": ["X-Mesh-Auth", "X-Operator-Key"]
  },
  "hashes": { "sha1": ["31c69b3e12936abca770d430066f379ec1d997ec"], "sha256": [] },
  "host": {
    "loaders": ["/tmp/.a", "/var/tmp/.a", "/dev/shm/.a"],
    "cron_watchdogs": ["/etc/cron.d/.sys_monitor", "/etc/cron.d/.s"],
    "ssh_backdoor": "~/.ssh/authorized_keys"
  },
  "strings": ["n4d mesh controller", "n4d_panel", "33.8-GO-TITAN", "30.0"],
  "cves": ["CVE-2026-39987", "CVE-2026-41176", "CVE-2022-22947", "CVE-2017-12611"],
  "scan_ports": [80,443,3000,5000,8000,8080,8443,8888,9000,9999,6443,10250,9200,
                 22,23,8088,2718,8090,10000,2379,8848,8265,8188,5678,11434,7860,
                 5432,3306,2375,2376,6379],
  "note": "XLab published exactly one SHA1, one IPv4 and one domain. No further atomic indicators exist in public reporting as of 2026-07-18."
}
Host Triage Sweepread-only · run as root
#!/bin/bash
# NadMesh host triage — READ ONLY, changes nothing. Run as root.
echo "== persistence trio: disk-backed loaders =="
ls -la /tmp/.a /var/tmp/.a /dev/shm/.a 2>/dev/null

echo "== persistence trio: hidden cron watchdogs =="
ls -la /etc/cron.d/ | grep -E '^\S+\s+\S+.*\s\.'
ls -la /etc/cron.d/.sys_monitor /etc/cron.d/.s 2>/dev/null

echo "== persistence trio: authorized_keys (review every key) =="
for f in /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys; do
  [ -f "$f" ] && echo "--- $f" && awk '{print NR": "$1" "$NF}' "$f"
done

echo "== hidden executables in world-writable dirs =="
find /tmp /var/tmp /dev/shm -maxdepth 2 -name '.*' -type f -perm -u+x -ls 2>/dev/null

echo "== UPX-packed binaries in world-writable dirs =="
find /tmp /var/tmp /dev/shm -maxdepth 2 -type f -exec sh -c \
  'head -c 256 "$1" | grep -qa UPX && echo "UPX: $1"' _ {} \; 2>/dev/null

echo "== established connections to published C2 =="
ss -tanp 2>/dev/null | grep -E '209\.99\.186\.235'
grep -R 'cdnorigin' /etc/hosts /etc/resolv.conf 2>/dev/null

echo "== listening AI/dev and infra ports =="
ss -tlnp 2>/dev/null | grep -E ':(8188|11434|5678|7860|2375|2376|6443|10250|2379|6379|9200)\b'
Exposure & Patch Auditpreventive
#!/bin/bash
# NadMesh exposure audit — what would the botnet find?

echo "== [1] AI/dev services bound to a non-loopback address =="
ss -tlnp | awk '$4 !~ /^127\.|^\[::1\]/ {print}' \
  | grep -E ':(8188|11434|5678|7860|3000|8080|8000|5000)\b'
# ComfyUI 8188 | Ollama 11434 | n8n 5678 | Gradio 7860 | Open WebUI / Langflow / Marimo

echo "== [2] Docker API exposed over TCP (top NadMesh vector, 30.31%) =="
ss -tlnp | grep -E ':(2375|2376)\b'
grep -E '"?hosts"?\s*:' /etc/docker/daemon.json 2>/dev/null
systemctl cat docker.service 2>/dev/null | grep -E '\-H\s+tcp'

echo "== [3] Kubernetes surface =="
ss -tlnp | grep -E ':(6443|10250|2379)\b'
kubectl get clusterrolebindings -o json 2>/dev/null \
  | grep -B4 '"name": "cluster-admin"' | grep -E 'ServiceAccount|"name"'
kubectl auth can-i --list --as=system:anonymous 2>/dev/null | head -20

echo "== [4] CVE version checks from the NadMesh scanning queue =="
marimo --version 2>/dev/null   # CVE-2026-39987 : need 0.23.0 or later
rclone version 2>/dev/null | head -1  # CVE-2026-41176 : 1.45.0-1.73.5 affected
# CVE-2022-22947 Spring Cloud Gateway / CVE-2017-12611 Struts : check app inventory

echo "== [5] long-lived cloud keys sitting in the filesystem =="
find / -maxdepth 6 -name '.env' -o -name 'credentials' -path '*.aws*' 2>/dev/null | head -40
grep -rlE 'AWS_(ACCESS_KEY_ID|SECRET_ACCESS_KEY)' /etc /opt /srv /home 2>/dev/null | head -40
env | grep -E 'AWS_|OPENAI_|ANTHROPIC_|HF_TOKEN' | sed 's'=.*'=***REDACTED***''
Behavioral Signaturesportable detection logic
# NadMesh behavioral signatures — platform-agnostic detection logic
# (translate to your SIEM; the CQL implementations are in section 8)

SIG-01  file_write  basename == ".a"  AND dir IN (/tmp, /var/tmp, /dev/shm)
SIG-02  process     cmdline MATCHES  cron\.d/\.(sys_monitor|s)\b
SIG-03  file_write  basename IN (".sys_monitor", ".s")  AND dir == /etc/cron.d
SIG-04  process     cmdline CONTAINS "authorized_keys" AND writer NOT IN cfgmgmt_allowlist
SIG-05  network     dest_ip == 209.99.186.235  OR  dns_query ENDSWITH "cdnorigin.net"
SIG-06  process     cmdline MATCHES  api/(beacon|task|result)\b
SIG-07  process     cmdline MATCHES  (curl|wget).*\|\s*(ba)?sh
SIG-08  process     cmdline MATCHES  (\.aws/(config|credentials)|\.docker/config\.json
                                     |serviceaccount/token|\.env\b)
SIG-09  process     image_path IN (/tmp/*, /var/tmp/*, /dev/shm/*) AND basename STARTSWITH "."
SIG-10  network     outbound distinct_dest > 50 on ports (2375,2376,6379,6443,10250,2379,
                    9200,8188,11434,5678,7860)  within 1h  from a single host
SIG-11  process     cmdline MATCHES  (n4d[\s_-]?mesh|n4d_panel|X-Mesh-Auth|X-Operator-Key)
SIG-12  process     cmdline MATCHES  docker.*(-H tcp:|:2375|:2376|--privileged|-v /:)
SIG-13  http        request_header PRESENT "X-Mesh-Auth" OR "X-Operator-Key"   [proxy/WAF]
SIG-14  http        uri IN (/api/beacon, /api/task/*, /api/result/*)           [proxy/WAF]
11

Detection Validation Gates

GateCheckPass Criteria
1 · Telemetry readyConfirm Linux sensors are reporting ProcessRollup2, ElfFileWritten, NetworkConnectIP4, NetworkListenIP4 and DnsRequest from your GPU/ML and container hosts specifically — not just from the general Linux fleet.All five event types present from at least one AI-workload host in the last 24 h
1b · Field availabilityRun a bare SHA1HashData=* search over 24 h.If it returns nothing, Q4's hash clause is inert in your tenant — record this and rely on Q1/Q2/Q8
2 · Benign baselineRun Q3, Q6, Q7, Q10 and Q12 over 7–14 days with no exclusions and count hits per host role.Exclusion lists written from real data before any of these are scheduled
3 · Positive control — dropper pathOn an isolated, non-production test host, create a benign file at /tmp/.a (e.g. a copy of /bin/true) and execute it.Q1 and Q8 both fire within the expected telemetry delay
4 · Positive control — cron pathOn the same test host, run a command referencing /etc/cron.d/.sys_monitor.Q2 fires; Q2b fires or is confirmed inert (documenting the file-write gap)
5 · Positive control — networkResolve cdnorigin.net from a sensored test host (DNS only — do not connect).Q4 fires on the DomainName clause
6 · Query hygieneConfirm every query parses in Falcon and returns a bounded result set; confirm Q10 completes inside its window.No parse errors; Q10 completes over a 2–6 h window
7 · PromotionOnly Q1, Q2/Q2b and Q11 promote to blocking IOAs. Q8 promotes to detect-only after baselining.Gates 2–4 green for the promoted queries; Q3/Q6/Q7/Q10/Q12 remain hunts

Positive-control discipline: gates 3–5 use benign stand-ins (a copy of /bin/true, a command line string, a DNS lookup). Do not obtain or execute the real sample to test detections, and do not connect to the C2 — a resolution is sufficient to prove the DNS path and does not put a host on the operator's target list.

12

Hardening — Tiered

The real fix is exposure, not detection. NadMesh has no novel exploit — it is an industrialised sweep of services that should never have been on the internet. Every control below is downstream of one question: why is this reachable? If you do only one thing from this pack, do Tier 1 control 1.

Immediate — this week
  1. Take AI/dev services off the internet. ComfyUI (8188), Ollama (11434), n8n (5678), Gradio (7860), Open WebUI, Langflow and Marimo must not be directly internet-reachable. Bind them to 127.0.0.1 or a private interface and put an authenticating reverse proxy or VPN/zero-trust broker in front. Ollama in particular ships with no authentication at all — exposure equals full model and host access. MITRE M1030 (Network Segmentation), M1035 (Limit Access to Resource Over Network). ⚠ best-practice, no formal CIS benchmark exists for these applications.
  2. Close the Docker API. The top NadMesh vector at 30.31%. The daemon must not listen on 2375 (plaintext) at all; if remote access is genuinely required, use 2376 with mutual TLS. Restrict /var/run/docker.sock to the docker group and never bind-mount it into a container. CIS Docker Benchmark 2.x/3.x; MITRE M1035.
  3. Rotate credentials that a reachable host could see. If a host was internet-exposed on any scan-set port, treat every AWS key, K8s token, registry credential and model API token available to it as compromised. Rotate first, investigate second — a stolen key is useful to the operator the moment it lands, and the operator's own dashboard tracks AWS keys as the headline metric. MITRE M1027.
  4. Block the published C2. Deny egress to 209.99.186.235 and cdnorigin.net at the firewall/proxy, and load them as Falcon Custom IOCs. Low effort, and cheap to keep. MITRE M1037 (Filter Network Traffic).
  5. Patch the queued CVEs. Marimo to 0.23.0 or later (CVE-2026-39987, pre-auth RCE); rclone off 1.45.0–1.73.5 (CVE-2026-41176); Spring Cloud Gateway (CVE-2022-22947) and Struts (CVE-2017-12611) per vendor advisories. MITRE M1051 (Update Software).
  6. Kill the Jenkins script console path. 22.28% of exploitation traffic. Require authentication, remove anonymous read, and restrict /script to named administrators. MITRE M1018, M1035.
Near term — 1 to 4 weeks
  1. Get long-lived cloud keys off disk and out of the environment. Replace static AWS keys in .env files and environment variables with IMDSv2-backed instance roles or IRSA/Workload Identity. Enforce IMDSv2 (hop limit 1) so a compromised container cannot reach the metadata service. AWS Security Best Practices; MITRE M1041, M1026.
  2. Scope Kubernetes service accounts away from cluster-admin. Audit every ClusterRoleBinding to cluster-admin and justify or remove each. Set automountServiceAccountToken: false by default and opt in per workload. Prefer short-lived projected tokens over legacy secret-backed ones. CIS Kubernetes Benchmark 5.1.x / 5.1.6; MITRE M1026 (Privileged Account Management).
  3. Lock down the kubelet and etcd. Disable anonymous auth on the kubelet (--anonymous-auth=false), set --authorization-mode=Webhook, and never expose 10250 beyond the control plane. Require client-cert auth on etcd (2379) and keep it off any routable network. CIS Kubernetes Benchmark 4.2.1 / 2.x; MITRE M1035.
  4. Mount world-writable directories noexec. Set noexec,nosuid,nodev on /tmp, /var/tmp and /dev/shm. This directly breaks the NadMesh loader design, which depends on executing .a from exactly those three paths. Pilot first — some installers stage executables in /tmp. CIS Distribution Independent Linux Benchmark 1.1.2–1.1.9; MITRE M1022 (Restrict File and Directory Permissions).
  5. Harden SSH against the key-injection backdoor. Set PermitRootLogin no, PasswordAuthentication no, and point AuthorizedKeysFile at a root-owned directory outside user home directories so an unprivileged compromise cannot append a key. Monitor for changes. CIS Linux Benchmark 5.2.x; DISA STIG; MITRE M1027, M1032.
  6. Kill weak-credential services. Telnet accounts for 10.36% of NadMesh traffic — it should not exist on your estate. Require authentication on Redis (6379) and bind it to loopback; secure Elasticsearch (9200) with authentication and TLS. CIS benchmarks per product; MITRE M1032, M1027.
  7. Restrict MCP servers. Any MCP tool capable of arbitrary SQL or shell execution must require authentication and bind to loopback. NadMesh explicitly catalogues these as follow-on capability. ⚠ best-practice, no formal benchmark — MCP hardening guidance is still immature; MITRE M1035, M1026.
Strategic — 1 to 3 months
  1. Bring shadow AI infrastructure into the asset program. The premise of this campaign is that teams stand up GPU boxes and inference services outside the standard build. Continuous external attack-surface monitoring against the 30-port set, plus a sanctioned, pre-hardened path for teams to get an AI service, removes the demand for the shadow one.
  2. Sensor coverage as a gate. No workload reaches production without a Falcon Linux or container sensor. GPU/ML hosts are the highest-risk and least-covered class in most estates — make that an explicit exception report, not an assumption.
  3. Egress filtering by default. Production and AI workloads should reach a known allowlist, not the open internet. This breaks the beacon and the harvest in one control, independent of any IOC.
  4. Short-lived credentials everywhere. Eliminate long-lived IAM users in favour of federated, time-bounded roles. NadMesh's entire business model is that a harvested static key keeps working — make the harvest worth minutes rather than months. MITRE M1026, M1032.
  5. Admission control on the cluster. Enforce Pod Security Standards (restricted) or an equivalent policy engine to reject privileged pods, hostPID/hostNetwork, and hostPath mounts of / — the escape primitives Q12 hunts for. CIS Kubernetes Benchmark 5.2.x; MITRE M1047.
13

Deployable Playbooks

Playbook 1 — Bind AI services to loopback and front them with authentication

Step 1: rebind each service so it is no longer listening on a routable address.

# Ollama — systemd drop-in (default listens on 0.0.0.0:11434 with NO auth)
sudo mkdir -p /etc/systemd/system/ollama.service.d
cat <<'EOF' | sudo tee /etc/systemd/system/ollama.service.d/10-bind-loopback.conf
[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"
EOF
sudo systemctl daemon-reload && sudo systemctl restart ollama

# ComfyUI  — start with an explicit listen address
python main.py --listen 127.0.0.1 --port 8188

# Gradio   — in application code
demo.launch(server_name="127.0.0.1", server_port=7860)   # never "0.0.0.0"

# n8n      — environment
export N8N_HOST=127.0.0.1
export N8N_PORT=5678
export N8N_BASIC_AUTH_ACTIVE=true

# Verify: every line below should show 127.0.0.1, never 0.0.0.0 or *
ss -tlnp | grep -E ':(8188|11434|5678|7860)\b'

Step 2: put an authenticating reverse proxy in front (nginx shown; equivalent applies to Caddy, Traefik or your zero-trust broker).

# /etc/nginx/conf.d/ai-services.conf
server {
    listen 443 ssl;
    server_name ai-internal.example.com;

    ssl_certificate     /etc/nginx/tls/fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/privkey.pem;

    # Allow only corporate/VPN ranges — replace with your own
    allow 10.0.0.0/8;
    allow 192.168.0.0/16;
    deny  all;

    auth_basic           "AI services";
    auth_basic_user_file /etc/nginx/.htpasswd;   # or auth_request to your SSO

    location /ollama/ { proxy_pass http://127.0.0.1:11434/; }
    location /comfy/  { proxy_pass http://127.0.0.1:8188/;  }
    location /n8n/    { proxy_pass http://127.0.0.1:5678/;  }
}

Playbook 2 — Close the Docker API (top NadMesh vector, 30.31%)

# 1. Confirm the daemon is not listening on TCP
ss -tlnp | grep -E ':(2375|2376)\b'        # expect NO output
sudo grep -E '"hosts"' /etc/docker/daemon.json 2>/dev/null
sudo systemctl cat docker.service | grep -E '\-H\s+tcp'

# 2. If a TCP listener exists, remove it. Plaintext 2375 has no safe configuration.
sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak
# edit /etc/docker/daemon.json and delete the "hosts" tcp entry, leaving only:
#   { "hosts": ["unix:///var/run/docker.sock"] }
sudo systemctl daemon-reload && sudo systemctl restart docker

# 3. If remote access is genuinely required, use 2376 with mutual TLS ONLY
#    (daemon.json): "tls": true, "tlsverify": true,
#                   "tlscacert": "/etc/docker/ca.pem",
#                   "tlscert":   "/etc/docker/server-cert.pem",
#                   "tlskey":    "/etc/docker/server-key.pem"

# 4. Socket permissions — and never bind-mount the socket into a container
sudo chown root:docker /var/run/docker.sock
sudo chmod 660 /var/run/docker.sock
getent group docker            # audit every member; membership is root-equivalent

# 5. Host firewall backstop
sudo iptables -A INPUT -p tcp --dport 2375 -j DROP
sudo iptables -A INPUT -p tcp --dport 2376 -j DROP

Playbook 3 — noexec on world-writable paths (breaks the loader design)

# PILOT THIS FIRST — some installers legitimately execute from /tmp.
# Roll to a canary ring, watch for install failures, then fleet-wide.

# /dev/shm
sudo cp /etc/fstab /etc/fstab.bak
echo 'tmpfs /dev/shm tmpfs defaults,noexec,nosuid,nodev 0 0' | sudo tee -a /etc/fstab
sudo mount -o remount,noexec,nosuid,nodev /dev/shm

# /tmp and /var/tmp (systemd-managed)
sudo systemctl enable --now tmp.mount
sudo mkdir -p /etc/systemd/system/tmp.mount.d
cat <<'EOF' | sudo tee /etc/systemd/system/tmp.mount.d/noexec.conf
[Mount]
Options=mode=1777,strictatime,nosuid,nodev,noexec
EOF
sudo systemctl daemon-reload && sudo systemctl restart tmp.mount

# Verify — all three must report noexec
findmnt -no TARGET,OPTIONS /tmp /var/tmp /dev/shm

Playbook 4 — Kubernetes: strip cluster-admin and lock the kubelet

# 1. Find every service account bound to cluster-admin — justify or remove each
kubectl get clusterrolebindings -o json \
  | jq -r '.items[] | select(.roleRef.name=="cluster-admin")
           | .metadata.name as $n | .subjects[]?
           | select(.kind=="ServiceAccount")
           | "\($n)\t\(.namespace)/\(.name)"'

# 2. Stop auto-mounting SA tokens by default; opt in per workload
kubectl patch serviceaccount default -n <namespace> \
  -p '{"automountServiceAccountToken": false}'

# 3. Kubelet — disable anonymous auth (/var/lib/kubelet/config.yaml)
#    authentication: { anonymous: { enabled: false }, webhook: { enabled: true } }
#    authorization:  { mode: Webhook }
sudo systemctl restart kubelet

# 4. Confirm anonymous access is refused on the API server and kubelet
kubectl auth can-i --list --as=system:anonymous | head
curl -sk https://127.0.0.1:10250/pods -o /dev/null -w '%{http_code}\n'   # expect 401

# 5. etcd must require client certs and must not be routable
ss -tlnp | grep ':2379'          # expect loopback / control-plane only

# 6. Admission control — reject the escape primitives Q12 hunts for
kubectl label namespace <namespace> \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=latest

Playbook 5 — SSH hardening against public-key injection

# /etc/ssh/sshd_config.d/90-hardening.conf
PermitRootLogin no
PasswordAuthentication no
PermitEmptyPasswords no
KbdInteractiveAuthentication no

# Move authorized_keys OUT of user-writable home directories so an unprivileged
# compromise cannot append a backdoor key.
AuthorizedKeysFile /etc/ssh/authorized_keys/%u

# Provision the root-owned key store
sudo mkdir -p /etc/ssh/authorized_keys
sudo chown root:root /etc/ssh/authorized_keys
sudo chmod 755 /etc/ssh/authorized_keys
# migrate each user's existing keys, then:
sudo chown root:root /etc/ssh/authorized_keys/*
sudo chmod 644 /etc/ssh/authorized_keys/*

sudo sshd -t && sudo systemctl reload sshd    # ALWAYS test config before reload
14

Containment Runbook

Sequencing matters here. Rotate credentials before you clean the host. The malware is replaceable; the AWS keys and cluster-admin tokens it already shipped to the operator are not. A host cleaned at hour 6 with keys rotated at hour 48 was never contained.

PhaseActionsOwnerEvidence to capture
0 · Triage
0–1 h
Run Q4 estate-wide for the atomic IOCs, then Q1 and Q2 for the persistence trio. Any host hitting two or more of Q1/Q2/Q3 is a confirmed infection. Run the Host Triage Sweep (Section 10) on candidates.SOC L2Query result exports; sweep output per host
1 · Isolate
1–2 h
Falcon network-contain confirmed hosts. Block 209.99.186.235 and cdnorigin.net at the egress perimeter for the whole estate, not just for the contained hosts.SOC L2 / NetworkContainment timestamps; firewall change ticket
2 · Rotate — do this now
1–4 h
Rotate every credential the host could reach: AWS access keys (disable, then delete after confirming no legitimate use), Kubernetes service-account tokens, registry credentials, model/API tokens, and any secret present in a .env file on that host. Revoke the SSH keys found in authorized_keys. Assume exposure — do not wait for proof of exfiltration.Cloud / IAMRotation log with timestamps; pre-rotation key IDs for CloudTrail correlation
3 · Scope
2–8 h
CloudTrail review for use of the rotated keys from unfamiliar source IPs (Section 7). Kubernetes audit-log review for anonymous or kubelet access. Run Q10 to identify hosts that became scanning nodes — those indicate lateral spread and a longer dwell time.IR / CloudCloudTrail findings; K8s audit extracts; Q10 output
4 · Eradicate
4–24 h
Rebuild, do not clean. The persistence trio is explicitly designed so that removing one artifact lets the others restore the implant, and root-level RCE means host integrity cannot be assured. If a rebuild is genuinely impossible, remove all three legs simultaneously — .a loaders in all three paths, both cron watchdogs, and the injected SSH key — then re-run Q1/Q2/Q3 at 1 h, 24 h and 72 h to confirm no revival.Platform / IRRebuild records; post-eradication query results at each interval
5 · Close the door
24–72 h
Apply Tier 1 hardening to the rebuilt host before it returns to service: service bound to loopback behind authentication, Docker API closed, CVEs patched. A rebuilt host restored to its original exposed configuration will be reinfected — the scanner rescans hit subnets every 5 minutes.PlatformPost-hardening exposure audit (Section 10) showing a clean result
6 · Recover & monitor
72 h+
Return to service. Schedule Q1, Q2, Q4 and Q11 as recurring searches. Run the exposure audit estate-wide, not just on the affected hosts — if one shadow AI service was exposed, others are.SOC / PlatformScheduled search configs; estate-wide exposure inventory
15

Detection Coverage Map

Chain step / TechniqueBehaviorCQLIOACoverage
T1595.002 — Target discoveryShodan-harvested AI endpoint queueGAP external to the estate; covered only by ASM (Section 7)
T1046 — Scanning30-port fan-out from an owned hostQ10GOOD
T1190 — Exploitation (AI services)RCE against ComfyUI / Ollama / n8n / Gradio / MarimoGAP no app-layer telemetry; app logs + Q9 inventory only
T1610 / T1611 — Container abuseDocker API RCE (30.31%), pod escapeQ12PARTIAL host-level only without a container sensor
T1190 — Exploitation (middleware)Jenkins script console (22.28%), WebLogic, Spring, Airflow, Superset, XXL-JobGAP app-layer; native hunts in Section 7
T1133 — Weak credentialsTelnet (10.36%), SSH brute forceQ10 (dest 23)PARTIAL outbound only; inbound needs auth logs
T1105 — Beacon delivery/api/beacon script fetch; curl piped to shQ5, Q6GOOD
T1105 / T1036.005 — Loader dropHidden .a in /tmp, /var/tmp, /dev/shmQ1GOOD
T1053.003 — Cron watchdog/etc/cron.d/.sys_monitor, /etc/cron.d/.sQ2, Q2bGOOD
T1098.004 — SSH key injectionPublic key appended to authorized_keysQ3PARTIAL high FP; usable only with a tuned writer allowlist
T1027.002 — Packed agent executionHidden UPX/Garble Go ELF from a world-writable pathQ8✔ (detect-only)GOOD
T1552.001 / .007 / T1528 — Credential harvestAWS config, .env, docker config, K8s SA tokenQ7PARTIAL command-line visible reads only; a Go binary reading files directly leaves no command line
T1071.001 — C2 beaconingController on 80/8443, X-Mesh-Auth HMACQ4, Q11✔ (Q11)PARTIAL atomic IOCs solid; header/URI hunting needs proxy logs
Exposure postureAI/dev and infra services listening on scan-set portsQ9GOOD inventory; external reachability must be confirmed separately
T1078.004 — Cloud impactHarvested keys reused against the cloud estateGAP outside Falcon entirely — CloudTrail/GuardDuty (Section 7)

Named gaps — read these before trusting a clean result

  • Credential reads without a command line. Q7 sees cat ~/.aws/config. It does not see the Go agent opening that file with a syscall, which is how the compiled implant actually does it. Q7 catches the shell-script stage; file-access telemetry or auditd is the complete control. This is the most important gap in the pack, because it sits on the impact step.
  • No HTTP URI or header telemetry. /api/beacon, /api/task/, /api/result/, X-Mesh-Auth and X-Operator-Key are excellent indicators that Falcon Linux EDR simply does not carry. Implement SIG-13 and SIG-14 (Section 10) on your proxy or WAF.
  • Hash coverage is one build of five. One published SHA1, five concurrent builds, Garble + UPX + random padding. Q4's hash clause is a bonus, never a baseline — and SHA1HashData may not even be populated in your tenant (validation gate 1b).
  • Application-layer exploitation is invisible here. The initial RCE against ComfyUI, Jenkins, Superset and friends happens above the endpoint telemetry line. Section 7's native hunts and the Tier 1 exposure controls are the real coverage for chain steps 1–3.
  • Container-internal activity needs a container sensor. With node-level coverage only, Q12 and Q8 see the host side of a container compromise and miss the rest.

Validation gates: the pack is not considered deployed until Section 11 gates 1–4 are green for the promoted queries (Q1, Q2/Q2b, Q11, and Q8 detect-only). Gate 1b in particular should be run before anyone reports a clean Q4 result as evidence of absence.

16

Hunt Summary Ticket

Hunt Ticket — copy into your case system
TITLE:      NadMesh Botnet — AI / MCP infrastructure credential harvesting hunt
SEVERITY:   Critical (active in the wild; objective is cloud credential theft)
SCOPE:      All Linux hosts, container nodes and Kubernetes clusters. Priority:
            GPU/ML hosts, any host running ComfyUI / Ollama / n8n / Gradio /
            Open WebUI / Langflow / Marimo, and anything internet-reachable on
            the 30-port scan set.

HYPOTHESIS: An internet-reachable AI or self-hosted dev service was exploited via
            one of 20+ RCE vectors; the NadMesh Go agent established the
            persistence trio (hidden .a loaders, hidden /etc/cron.d watchdogs,
            SSH public-key injection) and exfiltrated AWS keys, Kubernetes
            service-account tokens, .env contents and docker config to a mesh
            controller. Compromise of the AI service is the entry point, not the
            objective — the objective is the cloud estate behind it.

QUERIES:    Q1   hidden ".a" loader written to /tmp, /var/tmp, /dev/shm   [IOA]
            Q2   hidden cron watchdog under /etc/cron.d (process)         [IOA]
            Q2b  hidden cron watchdog (file-write basename variant)       [IOA]
            Q3   authorized_keys tampering                          [hunt, FP hi]
            Q4   atomic IOCs - C2 IP / domain / agent SHA1
            Q5   beacon retrieval via controller API paths
            Q6   remote script piped to a shell                    [hunt, FP med]
            Q7   cloud/container secret harvesting                 [hunt, FP med]
            Q8   hidden ELF executing from a world-writable path   [IOA, detect]
            Q9   exposure inventory - AI/dev + infra listening ports
            Q10  30-port outbound fan-out (host became a scan node) [hunt, cost hi]
            Q11  controller artifact strings (n4d mesh / X-Mesh-Auth)     [IOA]
            Q12  Docker API abuse and container escape primitives  [hunt, FP med]

DO FIRST:   1. Q4 estate-wide - cheapest answer to "are we already in this?"
            2. Q1 + Q2 - the persistence trio is the highest-fidelity signal here
            3. Q9 + external scan - what of ours would NadMesh find today?
            4. Any host hitting 2+ of Q1/Q2/Q3 = confirmed. Rotate its credentials
               BEFORE cleaning it (see containment phase 2).

FINDINGS:   [ ] Hosts with hidden .a loaders ............ ____
            [ ] Hosts with hidden cron watchdogs ....... ____
            [ ] Hosts with unexplained authorized_keys . ____
            [ ] Hosts contacting published C2 .......... ____
            [ ] AI/dev services internet-reachable ..... ____
            [ ] Credentials rotated .................... ____
            [ ] K8s service accounts bound cluster-admin ____

GAPS:       - Q7 misses syscall-level secret reads by the compiled Go agent
              (command-line visibility only) — the pack's most significant gap
            - No HTTP URI/header telemetry in Falcon EDR: /api/beacon and
              X-Mesh-Auth require proxy or WAF logging (SIG-13, SIG-14)
            - Published SHA1 covers 1 of 5 concurrent obfuscated builds; confirm
              SHA1HashData is even populated in-tenant (validation gate 1b)
            - App-layer exploitation (ComfyUI, Jenkins, Superset) is invisible to
              endpoint telemetry — native hunts in section 7 carry that coverage
            - Cloud impact (T1078.004) is entirely outside Falcon: CloudTrail

ACTIONS:    - Immediate: take AI/dev services off the internet; close the Docker
              API; rotate credentials from any reachable host; block the C2;
              patch CVE-2026-39987 and CVE-2026-41176
            - Near term: kill long-lived cloud keys; strip cluster-admin from
              service accounts; noexec on /tmp, /var/tmp, /dev/shm; SSH hardening
            - Promote Q1, Q2/Q2b, Q11 to blocking IOAs; Q8 to detect-only after
              baselining. Do NOT promote Q3, Q6, Q7, Q10, Q12.

CAVEAT:     Operator dashboard claims 3,811 unique AWS keys as of 2026-07-10, but
            the panel is internally inconsistent (17,700 total deploys shown above
            a funnel claiming 95,700 in 24h) and its success metrics exclude
            Ollama and AWS harvests. Treat as indicative of scale, not as a
            victim count. Do not repeat as hard fact in exec reporting.

OWNER:      ______________________
VERSION:    v0.2 · 2026-07-18
17

Changelog

v0.22026-07-24CQL correctness pass. All table() calls now carry an explicit row limit: the default is 200 and truncation is silent, so a capped result was indistinguishable from a complete one. Atomic-IOC sweeps (filters over 5+ hashes or C2 IPs) use limit=max so a wide infection is never silently under-scoped; behavioural hunts use limit=200, where exceeding the cap indicates the query needs tuning. Where present, event names that do not exist in the Falcon data model were corrected (e.g. ServiceInstalled is a Sysmon concept, not a Falcon event; ElfFileWritten is ELFFileWritten) — such queries could never return a row. No detection logic, fields, or IOCs changed.
v0.12026-07-18Initial pack. Built from the QiAnXin XLab primary analysis with The Hacker News and GBHackers corroboration. 13 CQL queries (Q1–Q12 incl. Q2b) covering the persistence trio, beacon delivery, credential harvesting, scanning fan-out, Docker/container abuse and exposure inventory. Four IOA candidates identified; Q3/Q6/Q7/Q10/Q12 explicitly held back as hunts. Atomic IOC set limited to the three indicators XLab actually published — one SHA1, one IPv4, one domain. Operator dashboard metrics flagged as internally inconsistent rather than repeated. Coverage gaps named for syscall-level credential reads, HTTP URI/header telemetry, hash coverage across five builds, and app-layer exploitation.
18

References

TierSourceUsed ForAccessed
TIER 1QiAnXin XLab — NadMesh Botnet Analysis: A Product-Grade Threat for the AI Service EraPrimary source for every atomic IOC in this pack. C2 IP and domain, agent SHA1, loader and cron paths, C2 protocol and endpoints, auth headers and panel cookie scheme, 30-port scan set, exploit distribution percentages, malware characteristics and build versions, scanning behaviour, credential-harvesting targets, operator dashboard metrics.2026-07-18
TIER 2The Hacker News — New NadMesh Botnet Hunts Exposed AI Services for Cloud Keys and Kubernetes TokensCorroboration of AI-service targeting set, Shodan harvester, the 3,811 AWS key operator claim, cluster-admin service-account objective, persistence trio narrative2026-07-18
TIER 2GBHackers — New NadMesh Botnet Uses 20+ RCE Vectors to Hijack AI and MCP InfrastructureCorroboration of the 20+ RCE vector count and MCP/AI infrastructure targeting2026-07-18
FRAMEWORKMITRE ATT&CKTechnique and mitigation mapping (Section 6, Section 12)2026-07-18
FRAMEWORKCIS Benchmarks — Docker, Kubernetes, Distribution Independent LinuxHardening control citations (Section 12)2026-07-18
VENDORNVDCVE-2026-39987, CVE-2026-41176, CVE-2022-22947, CVE-2017-126112026-07-18

IOC provenance statement. Every atomic indicator in this pack — the C2 IPv4, the C2 domain, the agent SHA1, the loader paths, the cron watchdog paths, the C2 URI endpoints, the authentication headers, the port set, the CVEs and the build strings — is published by QiAnXin XLab in the Tier 1 source above. No hashes, IP addresses, domains or file paths were inferred, extrapolated, or generated for this pack. Where public reporting is silent (notably: no SHA256, and only one sample hash across five known builds), that silence is stated as a gap rather than filled.