NadMesh Botnet — AI & MCP Infrastructure Credential Hunt
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.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Forward |
|---|---|---|---|
| TIER 1 | QiAnXin 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 2 | The 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 2 | GBHackers (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 on30.0) plus Garble obfuscation, UPX-9packing 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 throughCommandLine(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.
Hunt Brief & Attack Chain
Attack chain
| # | Step | Telemetry | Hunt Angle |
|---|---|---|---|
| 1 | Target 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/ASM | Exposure-side: what of ours is on 8188 / 11434 / 5678 / 7860? (Q9, Section 7) |
| 2 | Scanning. 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. | NetworkConnectIP4 | Outbound fan-out from an owned host on the 30-port set (Q10) |
| 3 | Exploitation. 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. | ProcessRollup2 | Container/orchestrator abuse from a web-tier parent (Q12) |
| 4 | Beacon & 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, DnsRequest | curl/wget of the beacon path, and download-piped-to-shell (Q5, Q6) |
| 5 | Persistence 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, ProcessRollup2 | Q1, Q2, Q3 — the highest-fidelity trio in this pack |
| 6 | Execution. Go agent, Garble-obfuscated, UPX -9 packed, random padding. Five builds concurrent. | ProcessRollup2 | Hidden ELF executing from a world-writable path (Q8) |
| 7 | Credential 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. | ProcessRollup2 | Secret-file access patterns (Q7) — this is the impact step |
| 8 | Mesh reporting. Bot pulls tasks from /api/task/<nid>, submits findings to /api/result/<nid>, operator drives /panel. | NetworkConnectIP4, DnsRequest | Atomic C2 IOCs (Q4), controller-port beaconing (Q11) |
Hunt hypotheses (ordered by fidelity)
| # | Hypothesis | MITRE | Confidence |
|---|---|---|---|
| H1 | A NadMesh loader named .a has been written to a world-writable directory on a Linux host. | T1105, T1036.005 | HIGH |
| H2 | A hidden cron watchdog has been created under /etc/cron.d to revive a removed implant. | T1053.003 | HIGH |
| H3 | An attacker public key has been appended to a user's authorized_keys. | T1098.004 | MEDIUM |
| H4 | A host has resolved or connected to the published NadMesh C2 infrastructure. | T1071.001 | HIGH |
| H5 | A process retrieved the beacon shell script from a controller and piped it to a shell. | T1105, T1059.004 | HIGH |
| H6 | A 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, T1528 | MEDIUM |
| H7 | A hidden, packed ELF is executing from /tmp, /var/tmp or /dev/shm. | T1027.002, T1036.005 | MEDIUM |
| H8 | An internal host is fanning out across the NadMesh 30-port scan set — i.e. it is now a scanning node. | T1046 | HIGH |
| H9 | An AI/dev service is listening on a NadMesh priority port and is reachable beyond its intended boundary. | T1190 (exposure) | MEDIUM |
| H10 | The Docker API or a container runtime was abused to deploy a container or escape to the host. | T1610, T1611, T1552.007 | MEDIUM |
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.
| Type | Value | Conf | Action | Context | Expiry |
|---|---|---|---|---|---|
| IPv4 | 209.99.186[.]235 | HIGH | detect | NadMesh C2 / controller node | 2027-01-18 |
| Domain | cdnorigin[.]net | HIGH | detect | NadMesh C2 domain | 2027-01-18 |
| SHA1 | 31c69b3e12936abca770d430066f379ec1d997ec | HIGH | hunt | Go agent sample. SHA1 only — no SHA256 published. Covers one of five concurrent builds. | 2026-10-18 |
| File path | /tmp/.a | HIGH | detect | Disk-backed loader (persistence trio) | 2027-01-18 |
| File path | /var/tmp/.a | HIGH | detect | Disk-backed loader (persistence trio) | 2027-01-18 |
| File path | /dev/shm/.a | HIGH | detect | Memory-backed loader (persistence trio) | 2027-01-18 |
| File path | /etc/cron.d/.sys_monitor | HIGH | detect | Hidden cron watchdog | 2027-01-18 |
| File path | /etc/cron.d/.s | HIGH | detect | Hidden cron watchdog | 2027-01-18 |
| File path | ~/.ssh/authorized_keys | MED | hunt | SSH public-key backdoor target. Legitimate file — the modification is the indicator, not the path. | 2026-10-18 |
| HTTP header | X-Mesh-Auth: <node_id>:<hex_hmac> | HIGH | detect | Bot auth. HMAC-SHA256, ±60 s time window. | 2027-01-18 |
| HTTP header | X-Operator-Key | HIGH | detect | Operator auth — plaintext comparison | 2027-01-18 |
| URI path | /api/beacon | HIGH | detect | Serves the initial infection shell script (carries the SSH backdoor) | 2027-01-18 |
| URI path | /api/task/<nid> | HIGH | detect | Bot pulls scan tasks | 2027-01-18 |
| URI path | /api/result/<nid> | HIGH | detect | Open submission of scan findings | 2027-01-18 |
| URI path | /panel | MED | hunt | Web management dashboard. Generic path — low standalone value. | 2026-10-18 |
| Port | 80, 8443 | MED | enrich | Controller listeners (spoofed nginx headers) | 2026-08-18 |
| String | n4d mesh controller | HIGH | hunt | Control branding in artifacts — the string the family is named for | 2027-01-18 |
| String | n4d_panel cookie = sha256(opKey + "YYYY-MM-DD-HH") | MED | hunt | Panel session cookie scheme | 2026-10-18 |
| Build ID | 33.8-GO-TITAN, 30.0 | MED | enrich | Two of five concurrent build versions | 2026-10-18 |
CVEs in the NadMesh scanning queue
| CVE | Product | Affected | Note |
|---|---|---|---|
CVE-2026-39987 | Marimo notebooks | before 0.23.0 | Pre-auth RCE |
CVE-2026-41176 | rclone RC | 1.45.0 – 1.73.5 | Auth bypass on the remote-control interface |
CVE-2022-22947 | Spring Cloud Gateway | see vendor advisory | SpEL injection RCE — long-known, still swept |
CVE-2017-12611 | Apache Struts | see vendor advisory | Freemarker 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.
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Linux servers / GPU & ML hosts | Falcon Linux sensor — ProcessRollup2, ElfFileWritten, NetworkConnectIP4, DnsRequest | CRITICAL | HIGH — GPU/ML boxes are frequently built outside the standard image and ship without a sensor |
| Container workloads | Falcon Container sensor / Kubernetes Protection | CRITICAL | HIGH — node-only sensor coverage misses in-container process activity; Docker API abuse is the top vector at 30.31% |
| Kubernetes control plane | K8s audit logs, kubelet logs, etcd access logs | CRITICAL | MEDIUM — audit logging often sampled or off in self-managed clusters |
| Cloud IAM (AWS) | CloudTrail, GuardDuty, IAM Access Analyzer, Access Key last-used | CRITICAL | MEDIUM — this is where stolen keys are actually used; endpoint telemetry alone will miss the impact |
| Perimeter / external attack surface | ASM or external scan of the 30-port set | HIGH | HIGH — shadow AI services are the entire premise of this campaign |
| Web proxy / egress | Proxy or NGSIEM HTTP logs (URI-level) | MEDIUM | HIGH — 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 host | HIGH | MEDIUM — Jenkins script console is the #2 vector at 22.28% |
| Data services (Redis, Elasticsearch, Postgres, MySQL) | Falcon on host + service auth logs | MEDIUM | MEDIUM — Redis at 8.29% of traffic |
ATT&CK Mapping
| Tactic | Technique | Observed Behavior | Query / Control |
|---|---|---|---|
| Reconnaissance | T1595.002 — Active Scanning: Vulnerability Scanning | 30-port sweep across 90+ cloud provider ranges; Shodan API harvester stocks the queue | Q9, Q10 / M1056 |
| Discovery | T1046 — Network Service Discovery | Compromised hosts become scanning nodes; 5-minute subnet rescan, 15-minute /32 rescan | Q10 / M1030 |
| Initial Access | T1190 — Exploit Public-Facing Application | 20+ RCE vectors against AI/dev services and middleware | Q12, Section 12 / M1051, M1030 |
| Initial Access | T1133 — External Remote Services | Telnet weak passwords (10.36%), SSH | Q10 / M1032, M1035 |
| Execution | T1059.004 — Unix Shell | Beacon shell script piped straight to a shell | Q5, Q6 / M1038 |
| Execution | T1610 — Deploy Container | Docker containers API RCE — top vector at 30.31% | Q12 / M1035 |
| Privilege Escalation | T1611 — Escape to Host | Kubernetes pod escapes; host-path container mounts | Q12 / M1047, M1038 |
| Persistence | T1053.003 — Scheduled Task/Job: Cron | /etc/cron.d/.sys_monitor, /etc/cron.d/.s watchdogs | Q2 / M1018, M1022 |
| Persistence | T1098.004 — Account Manipulation: SSH Authorized Keys | Public-key injection into ~/.ssh/authorized_keys | Q3 / M1027, M1032 |
| Defense Evasion | T1027.002 — Obfuscated Files: Software Packing | Garble obfuscation + UPX -9 + random padding; five concurrent builds | Q8 / M1049 |
| Defense Evasion | T1036.005 — Masquerading: Match Legitimate Name or Location | Hidden .a files in world-writable dirs; hidden cron names; spoofed nginx response headers | Q1, Q8 / M1022 |
| Credential Access | T1552.001 — Unsecured Credentials: Credentials In Files | ~/.aws/config, .env, ~/.docker/config.json exfiltration | Q7 / M1041, M1047 |
| Credential Access | T1552.007 — Container API | Docker socket / API abuse to read container config and secrets | Q7, Q12 / M1035 |
| Credential Access | T1528 — Steal Application Access Token | Kubernetes service-account tokens, incl. cluster-admin; AI model access tokens | Q7 / M1026 |
| Command & Control | T1071.001 — Application Layer Protocol: Web Protocols | HTTP controller on 80/8443, X-Mesh-Auth HMAC beaconing, spoofed nginx headers | Q4, Q11 / M1037 |
| Command & Control | T1105 — Ingress Tool Transfer | /api/beacon delivers the infection script; multi-path loader drop | Q1, Q5, Q6 / M1031 |
| Impact / follow-on | T1078.004 — Valid Accounts: Cloud Accounts | Harvested AWS keys and K8s service accounts reused against the cloud estate | CloudTrail (Section 7) / M1026, M1032 |
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
sourceIPAddressusing a key whose normal caller is an EC2/EKS node. Long-lived keys pulled from a.envfile will suddenly be used from outside your ranges. - Reconnaissance burst:
sts:GetCallerIdentity,iam:ListUsers,iam:ListAttachedUserPolicies,s3:ListBuckets,ec2:DescribeInstancesclustered 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/InstanceCredentialExfiltrationandRecon:IAMUser/*findings dated on or after 2026-07-01.
Kubernetes — audit log
- Anonymous or
system:anonymousrequests 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). etcdon 2379 reachable without client-cert auth — that is a full secret dump in one request.- Service accounts bound to
cluster-admin: enumerate everyClusterRoleBindingtocluster-adminand 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/SLAVEOFcommands 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[.]235orcdnorigin[.]net; and HTTP requests to URI paths/api/beacon,/api/task/,/api/result/.
CrowdStrike LogScale CQL Hunt Queries
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.
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 Name | Source Query | Pattern | Action | Exclusions |
|---|---|---|---|---|
NadMesh — Hidden .a Loader in World-Writable Path | Q1 | File write, image path matches .*/(tmp|var/tmp|dev/shm)/\.a$ | Block / Kill | Exclude designated build hosts by host group |
NadMesh — Hidden Cron Watchdog | Q2 / Q2b | Process command line matches cron\.d/\.(sys_monitor|s)\b | Block / Kill | Exclude file-integrity tooling (aide, tripwire) as parent |
NadMesh — Controller Artifact Strings | Q11 | Command line matches n4d[\s_-]?mesh|X-Mesh-Auth|X-Operator-Key|n4d_panel | Detect — High | Exclude this HuntPack file's own path on analyst workstations |
NadMesh — Hidden Binary Exec from World-Writable Path | Q8 | Process exec, image path in /tmp, /var/tmp, /dev/shm with dot-prefixed name | Detect — Medium | Exclude 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).
Machine-Readable IOC Appendix
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.
{
"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."
}
#!/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'
#!/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***''
# 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]
Detection Validation Gates
| Gate | Check | Pass Criteria |
|---|---|---|
| 1 · Telemetry ready | Confirm 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 availability | Run 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 baseline | Run 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 path | On 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 path | On 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 — network | Resolve cdnorigin.net from a sensored test host (DNS only — do not connect). | Q4 fires on the DomainName clause |
| 6 · Query hygiene | Confirm 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 · Promotion | Only 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.
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.
- 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.1or 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. - 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.sockto thedockergroup and never bind-mount it into a container. CIS Docker Benchmark 2.x/3.x; MITRE M1035. - 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.
- Block the published C2. Deny egress to
209.99.186.235andcdnorigin.netat the firewall/proxy, and load them as Falcon Custom IOCs. Low effort, and cheap to keep. MITRE M1037 (Filter Network Traffic). - 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).
- Kill the Jenkins script console path. 22.28% of exploitation traffic. Require authentication, remove anonymous read, and restrict
/scriptto named administrators. MITRE M1018, M1035.
- Get long-lived cloud keys off disk and out of the environment. Replace static AWS keys in
.envfiles 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. - Scope Kubernetes service accounts away from cluster-admin. Audit every
ClusterRoleBindingtocluster-adminand justify or remove each. SetautomountServiceAccountToken: falseby 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). - 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. - Mount world-writable directories noexec. Set
noexec,nosuid,nodevon/tmp,/var/tmpand/dev/shm. This directly breaks the NadMesh loader design, which depends on executing.afrom 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). - Harden SSH against the key-injection backdoor. Set
PermitRootLogin no,PasswordAuthentication no, and pointAuthorizedKeysFileat 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. - 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
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
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.
| Phase | Actions | Owner | Evidence 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 L2 | Query 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 / Network | Containment 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 / IAM | Rotation 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 / Cloud | CloudTrail 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 / IR | Rebuild 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. | Platform | Post-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 / Platform | Scheduled search configs; estate-wide exposure inventory |
Detection Coverage Map
| Chain step / Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1595.002 — Target discovery | Shodan-harvested AI endpoint queue | — | — | GAP external to the estate; covered only by ASM (Section 7) |
| T1046 — Scanning | 30-port fan-out from an owned host | Q10 | — | GOOD |
| T1190 — Exploitation (AI services) | RCE against ComfyUI / Ollama / n8n / Gradio / Marimo | — | — | GAP no app-layer telemetry; app logs + Q9 inventory only |
| T1610 / T1611 — Container abuse | Docker API RCE (30.31%), pod escape | Q12 | — | PARTIAL host-level only without a container sensor |
| T1190 — Exploitation (middleware) | Jenkins script console (22.28%), WebLogic, Spring, Airflow, Superset, XXL-Job | — | — | GAP app-layer; native hunts in Section 7 |
| T1133 — Weak credentials | Telnet (10.36%), SSH brute force | Q10 (dest 23) | — | PARTIAL outbound only; inbound needs auth logs |
| T1105 — Beacon delivery | /api/beacon script fetch; curl piped to sh | Q5, Q6 | — | GOOD |
| T1105 / T1036.005 — Loader drop | Hidden .a in /tmp, /var/tmp, /dev/shm | Q1 | ✔ | GOOD |
| T1053.003 — Cron watchdog | /etc/cron.d/.sys_monitor, /etc/cron.d/.s | Q2, Q2b | ✔ | GOOD |
| T1098.004 — SSH key injection | Public key appended to authorized_keys | Q3 | — | PARTIAL high FP; usable only with a tuned writer allowlist |
| T1027.002 — Packed agent execution | Hidden UPX/Garble Go ELF from a world-writable path | Q8 | ✔ (detect-only) | GOOD |
| T1552.001 / .007 / T1528 — Credential harvest | AWS config, .env, docker config, K8s SA token | Q7 | — | PARTIAL command-line visible reads only; a Go binary reading files directly leaves no command line |
| T1071.001 — C2 beaconing | Controller on 80/8443, X-Mesh-Auth HMAC | Q4, Q11 | ✔ (Q11) | PARTIAL atomic IOCs solid; header/URI hunting needs proxy logs |
| Exposure posture | AI/dev and infra services listening on scan-set ports | Q9 | — | GOOD inventory; external reachability must be confirmed separately |
| T1078.004 — Cloud impact | Harvested keys reused against the cloud estate | — | — | GAP 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-AuthandX-Operator-Keyare 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
SHA1HashDatamay 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.
Hunt Summary Ticket
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
Changelog
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.References
| Tier | Source | Used For | Accessed |
|---|---|---|---|
| TIER 1 | QiAnXin XLab — NadMesh Botnet Analysis: A Product-Grade Threat for the AI Service Era | Primary 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 2 | The Hacker News — New NadMesh Botnet Hunts Exposed AI Services for Cloud Keys and Kubernetes Tokens | Corroboration of AI-service targeting set, Shodan harvester, the 3,811 AWS key operator claim, cluster-admin service-account objective, persistence trio narrative | 2026-07-18 |
| TIER 2 | GBHackers — New NadMesh Botnet Uses 20+ RCE Vectors to Hijack AI and MCP Infrastructure | Corroboration of the 20+ RCE vector count and MCP/AI infrastructure targeting | 2026-07-18 |
| FRAMEWORK | MITRE ATT&CK | Technique and mitigation mapping (Section 6, Section 12) | 2026-07-18 |
| FRAMEWORK | CIS Benchmarks — Docker, Kubernetes, Distribution Independent Linux | Hardening control citations (Section 12) | 2026-07-18 |
| VENDOR | NVD | CVE-2026-39987, CVE-2026-41176, CVE-2022-22947, CVE-2017-12611 | 2026-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.