---
title: "N4D Mesh Controller: New infrastructure, a UPX-packed agent labeled \"go-titan,\" and how to hunt for it"
description: "Datadog Security Research executed a newer N4D Mesh Controller sample in isolated microVMs, uncovering rotated infrastructure, a UPX-packed go-titan agent, MCP tool abuse in action, and direct runtime evidence of multi-service scanning and persistence."
author: "Zander Mackie"
date: 2026-08-20
category: "emerging threats and vulnerabilities"
tags: ["threat research", "ai security", "threat detection"]
url: "https://securitylabs.datadoghq.com/articles/n4d-mesh-controller-go-titan-new-infrastructure-hunting/"
---

N4D Mesh Controller is an active Linux malware campaign that turns exposed Model Context Protocol (MCP) servers, and a wide range of other internet-facing services, into entry points for credential theft, lateral movement, persistence, and command and control (C2). [Public reporting by researcher German Fernandez](https://blog.offensive-intel.com/n4d-mesh-controller/) first documented the campaign's exploitation framework in June 2026. New isolated-execution analysis by Datadog Security Research paints a clearer picture of the malware campaign's current tactics, including a newer loader-to-agent chain, rotated infrastructure, exact persistence artifacts, and direct runtime evidence that the current agent enumerates MCP tools, invokes a command-execution tool, and reports the result to its controller.

## Overview

Researcher German Fernandez first documented N4D, reporting a generic MCP exploitation framework, eight polymorphic agent modules, more than 30 service-specific exploitation modules, automated attack-chain planning, and dedicated targeting of AI and LLM infrastructure. Datadog Security Research later obtained and executed a sample submitted after that initial reporting. The sample contacted a new IP address, `209.99.186.235`, and retrieved a second-stage agent identifying itself as `33.8-go-titan`, which installed several overlapping Linux persistence mechanisms.

This new sample doesn't represent a new campaign. It confirms that N4D's operators are still active, still evolving their delivery infrastructure, and still worth hunting.

## What happened

MCP servers can expose high-impact capabilities—including command execution, file access, database queries, and cloud administration—through a single, uniform protocol. N4D turns that uniformity into an attacker advantage: Instead of needing a distinct exploit for every MCP implementation, its scanner initializes a session, requests `tools/list`, classifies the returned tools, and invokes anything that looks dangerous (most notably `execute_command`) through `tools/call`.

This automation changes the threat model for a publicly reachable MCP server. An unauthenticated server that intentionally exposes a command execution tool doesn't need a memory corruption bug or a CVE to be compromised; the dangerous capability is already there, and N4D supplies automated discovery and abuse.

From that foothold, N4D broadens access through an unusually large, conventional exploitation library. The recovered Go binary contains paths for databases, caches, container platforms, application servers, and AI infrastructure, including Ray Dashboard and LightLLM. An operator can use one exposed MCP server as a beachhead and keep scanning or exploiting adjacent services from the compromised host.

The observed attack flow proceeds as follows:

1. **Discover exposed services.** Scanner probes MCP endpoints and common infrastructure ports. Older scanner artifacts identify the MCP client as `n4d-vps`; the current Go agent uses the shorter name `n`. Both negotiate protocol version `2024-11-05` and request `tools/list`.
2. **Classify dangerous capabilities.** Tool names associated with command execution, file access, and database queries are selected for invocation.
3. **Deliver the agent.** Shell scripts and loaders use `curl` or `wget` to retrieve an architecture-specific agent from `cdnorigin.net`, `209.99.186.73`, or the newly observed `209.99.186.235`. The newer loader tries both `/api/agent/full?arch=` and the legacy `/api/agent/binary?arch=`.
4. **Establish control.** The agent communicates with controller API paths, including `/api/register`, `/api/beacon`, `/api/task/`, `/api/result/`, and `/api/pty`, authenticated with custom `X-Mesh-Auth` and `X-Operator-Key` headers.
5. **Expand access.** The agent contains service-specific abuse paths for PostgreSQL, MySQL, Redis, Docker, Kubernetes, Nacos, Jenkins, Ray, LightLLM, and other platforms, including recovered functions named `deployRayDashboard` and `deployLightLLM`.
6. **Persist and conceal.** Observed mechanisms include hidden cron entries, shell-profile modification, watchdog scripts, systemd units, SSH key installation, execution from memory-backed directories, and process names that mimic Linux kernel workers.
7. **Add a secondary channel.** The agent can launch Cloudflare Quick Tunnels and report the assigned `*.trycloudflare.com` hostname back to its controller.

## What we found

### Sample analysis

At a publication-time refresh on July 29, 2026, the associated VirusTotal collection held 27 artifacts: 21 ELF binaries, five POSIX shell scripts, and one file of unknown type. Eighteen of the ELF files were UPX-packed. Antivirus detections across the set ranged from 0 to 29 engines, a wide enough spread that behavioral and campaign-specific artifacts are more useful for tracking than any detection-count threshold.

The same refresh found an enabled four-rule VirusTotal Livehunt ruleset covering loaders, unobfuscated agents, delivery scripts, and MCP autodeployment behavior. Its newest notifications were repeated behavior matches on the known packed agent (`fc4109f5dd1d30b65dd60e57dc639ac1d313bfa5241e36e61fbc4aabc1cda482`) and static matches on two known unobfuscated collection files (`3435cc9d4a255bfb4cfb09f2390c29b888f70a43345cfaaecf46c55bc89b814d` and `ab435e3e24cfb3a37c1f564209869fe53c8fe95bc9161d0d373fbc1c551b2c29`). These were new analyses of previously submitted files, not newly submitted campaign samples. They validate the rules but do not, by themselves, establish new campaign activity.

We inspected five representative files: an unpacked Go build, a packed ELF with the collection's highest detection count, and three downloader/watchdog scripts.

The unpacked x86-64 Go sample (`3435cc9d4a255bfb4cfb09f2390c29b888f70a43345cfaaecf46c55bc89b814d`) exposes extensive implementation detail through static strings and symbols:

- **MCP operations:** `initialize`, `tools/list`, `tools/call`, `execute_query`, and command-execution classification logic
- **Agent control:** `X-Mesh-Auth`, `.trycloudflare.com`, `/api/pty`, and the registration/tasking/result API paths
- **AI infrastructure targeting:** `main.deployLightLLM` and `main.deployRayDashboard`
- **Persistence and execution:** `/etc/cron.d/.syscheck`, `/etc/profile.d/sys_alias.sh`, `/root/.ssh/authorized_keys`, `/dev/shm/.n4d`, and kernel-worker-style process names
- **Service exploitation:** PostgreSQL `COPY TO PROGRAM`, MySQL outfile/event execution, Redis configuration abuse, Docker socket access, Kubernetes APIs, Nacos, and Jenkins

The shell samples show a compact, repeatable delivery pattern: Check a lock file or running process, map the host architecture, download to `/tmp/.agent` or `/tmp/.n4d`, `chmod +x`, and detach with `setsid`. One watchdog re-downloads the payload every 30 seconds if `/tmp/.agent` is missing. Another tries both the TLS-fronted domain and the raw IP. A third computes an architecture value but requests the `amd64` payload regardless, a sign of uneven quality across generated or operator-maintained variants.

### Isolated execution of the newer loader

We executed a newer x86-64 loader in disposable microVMs, using QEMU user-mode emulation with host-level syscall denial for runtime network connections. The password-protected archive was `77fe750d6b94b32e80e25aecb2ae7c435f0b45cfca25969dbee4dc22967b1302`; its ELF payload was `0d5f67b6d35e609e1d5eac0afd981147ea8df7da2f192ae59ac0bab7e48cc1de`. The statically linked Go loader used module path `n4d-agent/loader` and embedded both `cdnorigin.net` and `209.99.186.235`.

The loader tried four endpoint combinations across ports 80 and 8443, using both the current `/api/agent/full?arch=amd64` path and the legacy `/api/agent/binary?arch=amd64`. It wrote `/etc/cron.d/.sys-health` on a three-minute schedule and staged `/tmp/.sys-health-monitor.tmp`.

To capture the next stage without giving the loader unrestricted network access, we retrieved the exact observed URL from a separate disposable VM: `http://209.99.186.235:8443/api/agent/full?arch=amd64` returned a 7,321,596-byte UPX-packed ELF (`fc4109f5dd1d30b65dd60e57dc639ac1d313bfa5241e36e61fbc4aabc1cda482`). Unpacked with UPX 5.1.1, it grew to 19,202,174 bytes (`c422621ef824b627d74906f8d75ed8f990f4d6c708a0d263f219f9d9dd435174`), a Go binary that was also garble-obfuscated, with damaged or removed symbol metadata.

The agent identified itself in its debug log as `33.8-go-titan`, using node authentication key `n4d-2ff16c75b1d2`. We denied every outbound connection before egress and let the agent run for 120 seconds. In that window, it made **22,831 connection attempts across 742 destination IPs, 15,833 IP/port pairs, and 37 destination ports**, targeting SSH, databases, container APIs, Kubernetes, caches, and application servers. This is direct runtime evidence that the delivered agent performs broad, multi-service scanning rather than only maintaining C2.

The denied run also constrains how those targets were chosen: Scanning started without receiving controller tasking. Destinations clustered into contiguous address ranges, while the unobfuscated oracle exposes scanCIDR and scanLateral functions, as well as /24 construction artifacts. Together, that evidence supports autonomous, on-host expansion of CIDR seeds rather than 742 individually hardcoded targets. We have not recovered the seed selection algorithm from the garble-obfuscated 33.8 build, so whether those prefixes were built in or generated locally, and why the agent selected those particular public ranges, remain unresolved.

The same run surfaced the current controller API surface: `POST /api/register`, `GET /api/task/{node}`, `POST /api/result/{node}`, `POST /api/intel`, `GET /api/endpoints`, and three `/api/update/` endpoints, all issued via an embedded Go HTTP client so they never appear as curl or wget command lines.

Observed persistence included `/etc/cron.d/.s`, `/etc/cron.d/.sys-health`, `/etc/profile.d/.sys_alias.sh`, `/etc/systemd/system/sys-resource.service`, `/tmp/.wd`, `/var/tmp/.wd`, `/usr/local/bin/.sys-health-monitor`, and an appended shell startup command. It also installed the following SSH public key into `/root/.ssh/authorized_keys`:

```
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJKH4g/SD6c00i5PzlWWkwXJwIHEac+nlAjg6WeOHUq3 root@ubuntu
```

The agent also attempted to download `cloudflared` from GitHub and retained logic to launch a Quick Tunnel to a local port. Generic Quick Tunnel activity is not an N4D indicator on its own, as legitimate use is common, but temporary-path execution combined with other campaign artifacts provides the needed context.

### Offline replay against deterministic services

Connection denial established the agent's scanning breadth, but it could not show what happens after a service answers. We therefore built an offline replay lab. The sample runs in a network namespace connected only to a recorder namespace. The recorder has no default route or external interface, drops forwarding, captures original packets with `tcpdump`, transparently redirects TCP through `mitmproxy`, and sends HTTP, TLS, and WebSocket requests to deterministic `aiohttp` fixtures. The controller fixture registers a synthetic node but always returns an empty task queue. The MCP fixture advertises one inert `execute_command` tool and never executes the supplied argument. Samples and generated captures remain outside Git.

An inert probe validated DNS, HTTP, TLS, WebSocket, PCAP, flow, syscall, and filesystem capture before malware execution. That validation also exposed an instrumentation failure: Wrapping QEMU in host `strace` set a nonzero `TracerPid`, which the debug agent read from `/proc/self/status` before exiting. Removing the host tracer while retaining QEMU's guest-side syscall trace allowed the same build to remain active until the containment timeout. Dynamic-analysis instrumentation can change the behavior it is intended to measure, so the lab records the execution mode with every run.

We compared the unobfuscated `14.10-go-titan` oracle with the supplied packed `33.8-go-titan` sample. The supplied archive (`94ae566e27b176698958b4b9c216a0a2b0ac7ce11d7d81417c8add081fd3cc9c`) contained only the expected packed ELF (`fc4109f5dd1d30b65dd60e57dc639ac1d313bfa5241e36e61fbc4aabc1cda482`). VirusTotal independently relates that file to extracted payload `c422621ef824b627d74906f8d75ed8f990f4d6c708a0d263f219f9d9dd435174`.

| Capability | `14.10` debug oracle | `33.8` packed agent |
| :---- | :---- | :---- |
| Controller | Registered to a `trycloudflare.com` endpoint, sent health and host intelligence, and polled an empty task queue | Tried `209.99.186.235` on ports 80 and 8443 plus `cdnorigin.net`; registered as `33.8-go-titan`, beaconed, sent intelligence, and polled an empty task queue |
| MCP | Sent 1,654 `initialize` and 1,473 `tools/list` requests; no tool invocation was reached in the 30-second replay | Sent 3,409 `initialize`, 1,865 `tools/list`, and 1,534 `tools/call` requests in the fixture-amplified 30-second replay |
| MCP action | Enumerated the fixture's tool catalog | Called execute_command with {&quot;command&quot;:&quot;id&quot;}, then reported the tool schema, arguments, inert output, error:false, and exec_verified:false through /api/intel |
| AI services | Probed Ray Dashboard through `:8265/api/version`; static code also contains LightLLM, Ray, Marimo, Jupyter, and FastGPT deployment paths | Probed Ollama `:11434/api/tags` and `/api/generate`, ComfyUI `:8188/system_stats` and `/prompt`, and Stable Diffusion WebUI `:7860/sdapi/v1/options` |
| Cloud and infrastructure | Probed Nacos, WebLogic, XXL-Job, MinIO, rclone, Spring Gateway, cloud metadata, and many generic MCP locations | Probed Kubernetes API and kubelet ports, unauthenticated Docker APIs, Elasticsearch, cloud metadata, SSH, databases, caches, and generic MCP locations |
| Persistence | Copied itself to `/var/tmp/.n4d` and `/dev/shm/.n4d`, installed a watchdog and profile startup file, and added an SSH key | Copied itself to `/var/tmp/.a`, wrote `/var/tmp/.wd`, `/etc/profile.d/.sys_alias.sh`, `/root/.bashrc`, and `/usr/local/bin/.sys-health-monitor`, and added the same SSH key |

The request totals are not campaign scan rates. The fixture deliberately accepted every configured controller and synthetic MCP endpoint, amplifying concurrent loops and tool calls. They do establish code-path reachability. Most importantly, the packed current build did not merely contain generic MCP strings: It completed `initialize` → `tools/list` → `tools/call`, selected `execute_command`, attempted `id`, and exfiltrated the structured result. It also wrote `/tmp/.n4d_autodeploy_debug`, providing a second source of ground truth for each MCP initialization and tool-list operation.

The two result flags describe different stages. `error:false` records that the JSON-RPC tool call itself succeeded. `exec_verified:false` is implant-side validation, not controller confirmation: The agent placed it in the outbound /api/intel request before receiving a response. In this run, the fixture returned the literal text `fixture: execution disabled`, which did not resemble a real id result. We did not recover the exact verification predicate from the obfuscated build.

The AI service findings require a stricter confidence boundary. The replay dynamically confirmed current probes for Ollama, ComfyUI, and Stable Diffusion WebUI, but it did not observe exploitation of those products. The debug build reached Ray's version probe, while its `/run` deployment path remains a static capability. LightLLM /pd\_register, Marimo /terminal/ws, Jupyter, FastGPT, and the two-stage Nginx UI chain were not reached in the bounded replay. Because the controller returned an empty task queue, the replay could not exercise modules that require operator tasking. Nor can it distinguish build-version changes from response gating, fixture fidelity, or timing. The functions and protocol artifacts remain high-confidence static findings from the debug build, supported by the original research and these advisories:

- LightLLM PD-mode unsafe deserialization, [CVE-2026-26220](https://nvd.nist.gov/vuln/detail/CVE-2026-26220), covers unauthenticated binary WebSocket data passed to `pickle.loads()` in versions through 1.1.0.
- Ray job submission, [CVE-2023-48022](https://nvd.nist.gov/vuln/detail/cve-2023-48022), covers remote code execution through the job API in Ray 2.6.3 and 2.8.0. Ray's documented position is that the service was intended for a controlled network, and token authentication became available in Ray 2.52.0.
- Marimo's unauthenticated terminal WebSocket, [CVE-2026-39987](https://nvd.nist.gov/vuln/detail/CVE-2026-39987), affects versions before 0.23.0 and is in CISA's Known Exploited Vulnerabilities catalog.
- Nginx UI's unauthenticated backup disclosure, [CVE-2026-27944](https://nvd.nist.gov/vuln/detail/CVE-2026-27944), and unauthenticated MCP endpoint, [CVE-2026-33032](https://nvd.nist.gov/vuln/detail/CVE-2026-33032), match the debug build's backup and MCP-chain artifacts. We did not dynamically reproduce that chain.

The current build's runtime behavior is both broader and narrower than its static inventory suggests. It operationalises generic MCP tool abuse and adds probes for locally deployed generative-AI services, while several older CVE-specific modules did not execute during the bounded replay. We cannot yet attribute that difference to module removal or a deliberate behavioral shift; build changes, autonomous scanner prioritisation, response gating, timing, and operator tasking remain plausible explanations. The distinction is the main contribution beyond the original report: It separates code retained in an oracle build from behavior exercised by the packed agent delivered in July 2026.

The current controller surface included `POST /api/register`, `GET /api/task/{node}`, `GET /api/beacon`, `POST /api/intel`, and `POST /api/deployed`. Authentication used `X-Mesh-Auth`, and the first intelligence report attempted to collect AWS, Google Cloud, and Azure metadata, environment values, sensitive files, and SSH keys.

### Detection opportunities

Defenders can correlate campaign-specific indicators with behaviors that stay useful even after infrastructure or file hashes rotate. Here's an overview of what to look for:

**MCP server telemetry**

- An MCP `initialize` request followed by `tools/list` from client name `n4d-vps`, or from the current client name `n` when correlated with N4D request paths or persistence artifacts
- `tools/call` activity invoking command-execution, file-write, or database-query tools
- Publicly reachable MCP endpoints without authentication, especially those exposing shell or code-execution tools
- Child processes spawned by an MCP server, particularly `curl`, `wget`, shell interpreters, or executables written to temporary directories

**Host telemetry**

- Process execution from `/dev/shm`, `/tmp`, or `/var/tmp` named `kworker*` or using hidden dotfiles
- Writes to `/etc/cron.d/.s`, `/etc/cron.d/.sys-health`, `/etc/cron.d/.syscheck`, `/etc/profile.d/.sys_alias.sh`, or `/etc/systemd/system/sys-resource.service`
- Creation of `/dev/shm/.agent.lock`, `/dev/shm/.agent.pid`, `/tmp/.n4d*`, `/tmp/.agent`, `/tmp/.sys-health-monitor`, `/var/tmp/.a`, or `/var/tmp/.wd`
- Download activity referencing `/api/agent/full`, `/api/agent/binary`, `cdnorigin.net`, `209.99.186.73`, or `209.99.186.235`
- HTTP telemetry containing `X-Mesh-Auth`, `X-Operator-Key`, `/api/task/`, `/api/beacon`, `/api/register`, `/api/result/`, `/api/deployed`, or `/api/pty`
- `cloudflared` launched from a world-writable temporary directory; treat generic `*.trycloudflare.com` DNS activity as a correlation lead, not attribution on its own

**Network telemetry**

- Connections to `209.99.186.73` or `209.99.186.235`, and DNS/HTTP activity for `cdnorigin.net`
- Requests to `/api/agent/full`, `/api/agent/binary`, and other controller API paths
- Cloudflare Quick Tunnel traffic correlated with temporary-path execution or other N4D host artifacts

Here are some YARA rules for tracking the campaign: one for the newer loader, one for unobfuscated or debug agent builds, and one for delivery scripts. Packed or newly obfuscated agents may not expose enough plaintext for the second rule to match.

```
rule N4D_Linux_Loader_2026
{
    meta:
        description = "Tracks N4D Go loaders delivering architecture-specific agents"
        date = "2026-07-13"
    strings:
        $module = "n4d-agent/loader" ascii
        $full = "/api/agent/full?arch=" ascii
        $legacy = "/api/agent/binary?arch=" ascii
        $beacon = "/api/beacon?k=n4d" ascii
        $cron = "/etc/cron.d/.sys-health" ascii
        $stage = "/tmp/.sys-health-monitor.tmp" ascii
        $domain = "cdnorigin.net" ascii
    condition:
        uint32(0) == 0x464c457f and filesize < 20MB and
        $module and 3 of ($full, $legacy, $beacon, $cron, $stage, $domain)
}

rule N4D_Mesh_Agent_Unobfuscated
{
    meta:
        description = "Tracks unobfuscated N4D mesh agent builds"
        date = "2026-07-13"
    strings:
        $mcp1 = "tools/list" ascii
        $mcp2 = "tools/call" ascii
        $api1 = "/api/register" ascii
        $api2 = "/api/result/" ascii
        $tunnel = ".trycloudflare.com" ascii
        $profile = "/etc/profile.d/sys_alias.sh" ascii
    condition:
        uint32(0) == 0x464c457f and filesize < 40MB and
        all of ($mcp*, $api*, $tunnel, $profile)
}

rule N4D_Linux_Delivery_Script
{
    meta:
        description = "Tracks N4D shell downloaders and watchdogs"
        date = "2026-07-13"
    strings:
        $shell = "#!/bin/" ascii
        $full = "/api/agent/full?arch=" ascii
        $legacy = "/api/agent/binary?arch=" ascii
        $domain = "cdnorigin.net" ascii
        $new_ip = "209.99.186.235" ascii
        $watchdog = ".sys-health-monitor" ascii
        $setsid = "setsid" ascii
        $cron = "/etc/cron.d/.sys-health" ascii
    condition:
        filesize < 200KB and $shell and
        4 of ($full, $legacy, $domain, $new_ip, $watchdog, $setsid, $cron)
}
```

VirusTotal Livehunt can also apply a behavior rule to normalised sandbox results from packed files. The enabled rule correlates N4D's MCP autodeployment markers with reporting to the controller:

```
import "vt"

rule N4D_Dynamic_MCP_Autodeploy
{
    meta:
        description = "N4D MCP exploitation followed by controller reporting"
        date = "2026-07-14"
    condition:
        for any path in vt.behaviour.files_written : (
            path endswith "/tmp/.n4d_autodeploy_debug" or
            path endswith "/tmp/.n4d_nid"
        ) and
        for any http in vt.behaviour.http_conversations : (
            http.request_method == vt.Http.Method.POST and
            (http.url contains "/api/intel" or
             http.url contains "/api/deployed")
        )
}
```

Keep file and behavior rules in separate Livehunt rulesets so high-volume behavioral pivots do not exhaust notification limits for high-confidence file matches. Monitor notifications through the API, then enrich each new SHA-256 with submissions, relationships, behavior, TLSH, telfhash, vhash, and dropped-file links before clustering it.

Additional tracking opportunities include passive DNS for new resolutions of `cdnorigin.net`, certificate transparency pivots on newly observed TLS certificates, hosting/ASN changes around the controller IPs, and VirusTotal relationship pivots from new loaders to contacted URLs and downloaded files. Monitor the SSH public key above, the Go module path `n4d-agent/loader`, the version string `33.8-go-titan`, and the node key prefix `n4d-` in any newly recovered configuration. Track `*.trycloudflare.com` only when it correlates with `.cf_h`, temporary-path `cloudflared` execution, or another N4D indicator.

## Some concise threat hunting ideas

Here are a few starting points for threat hunts to check for N4D related activity in your environment:

| Idea | Detects |
| :---- | :---- |
| `n4d-c2-network-activity` | Connections or DNS lookups to `209.99.186.73`, `209.99.186.235`, or `cdnorigin.net` |
| `n4d-implant-binary-download` | `curl`/`wget`/`lwp-download` fetching the implant via `/api/agent/binary` or `/api/agent/full` |
| `n4d-local-c2-api-poll` | The implant's distinctive task-poll fingerprint (`X-Mesh-Auth`/`X-Operator-Key` headers against `/api/task/`, `/api/beacon`, `/api/register`, `/api/result/`, `/api/intel`, `/api/chains`, `/api/pty`) |
| `n4d-cloudflared-tunnel-abuse` | `cloudflared ... tunnel --url` launched from `/tmp`, `/var/tmp`, or `/dev/shm` |
| `n4d-devshm-process-masquerade` | A process named `kworker*` executing from `/dev/shm` |
| `n4d-additional-persistence-file-activity` | Writes to `/etc/cron.d/.syscheck`, `/etc/cron.d/.sys-health`, `/etc/profile.d/sys_alias.sh`, or `/etc/ld.so.preload` |
| `n4d-hardcoded-backdoor-credential-auth` | Authentication attempts using the implant's hardcoded backdoor accounts (`n4d_admin`, `n4d`) against Nacos/Postgres |

If your rules use OCSF-normalised fields, they can apply across whichever log sources you have already onboarded, instead of depending on a single integration.

## Response guidance

Isolate affected hosts before removing persistence. Preserve the process tree, executable, shell scripts, open connections, and temporary files for analysis. Remove cron, profile, systemd, SSH-key, and watchdog persistence only after evidence collection is complete. Rotate cloud credentials, SSH keys, database credentials, API tokens, and any secrets available to the MCP process or stored on the host.

For exposed MCP services, require authentication, bind local-only services to loopback, and put remote access behind an authenticated proxy. Inventory advertised tools and remove command execution, unrestricted file access, and broad database capabilities where they aren't required. Where command execution is a legitimate requirement, put it behind explicit per-tool authorisation and a strict allowlist, then run it in a short-lived, unprivileged sandbox with a read-only root filesystem, no host mounts or credentials, blocked cloud-metadata access, constrained egress, and resource and time limits. Log MCP tool invocation with the authenticated principal, client identity, tool name, arguments, resulting child process, and sandbox outcome.

## Indicators of compromise

| Type | Indicator | Context |
| :---- | :---- | :---- |
| IPv4 | `209.99.186.73` | Reported controller, payload delivery, and API infrastructure |
| IPv4 | `209.99.186.235` | Loader and live second-stage delivery infrastructure, observed July 13, 2026 |
| Domain | `cdnorigin.net` | TLS-fronted C2 and payload delivery |
| Domain pattern | `*.trycloudflare.com` | Secondary tunnel channel; legitimate use exists |
| MCP client | `n4d-vps`; `n` | Scanner client identities; treat `n` as an IOC only with other N4D behavior |
| HTTP header | `X-Mesh-Auth` | Agent authentication header |
| HTTP header | `X-Operator-Key` | Operator API authentication header |
| Credential | `n4d_admin:n4d_admin123` | Nacos backdoor re-access credential |
| Credential | `n4d:n4dmesh2024` | Controller PostgreSQL credential; indicates controller deployment, not a generic victim backdoor |
| SSH key | `AAAAC3NzaC1lZDI1NTE5AAAAIJKH4g/SD6c00i5PzlWWkwXJwIHEac+nlAjg6WeOHUq3` | Public key installed by the go-titan agent |
| SHA-256 | `77fe750d6b94b32e80e25aecb2ae7c435f0b45cfca25969dbee4dc22967b1302` | Password-protected archive containing the newer loader |
| SHA-256 | `94ae566e27b176698958b4b9c216a0a2b0ac7ce11d7d81417c8add081fd3cc9c` | Password-protected archive containing the supplied packed `33.8-go-titan` agent |
| SHA-256 | `0d5f67b6d35e609e1d5eac0afd981147ea8df7da2f192ae59ac0bab7e48cc1de` | Newer statically linked Go loader |
| SHA-256 | `fc4109f5dd1d30b65dd60e57dc639ac1d313bfa5241e36e61fbc4aabc1cda482` | UPX-packed 33.8-go-titan agent retrieved from `209.99.186.235:8443` |
| SHA-256 | `c422621ef824b627d74906f8d75ed8f990f4d6c708a0d263f219f9d9dd435174` | Unpacked form of the 33.8-go-titan agent |
| SHA-256 | `3435cc9d4a255bfb4cfb09f2390c29b888f70a43345cfaaecf46c55bc89b814d` | Unpacked Go debug build |
| SHA-256 | `d4483b7a943faeec2ce6690e508ee3a30c7fc1e546887658921c132f179b8e78` | UPX-packed Linux agent |
| SHA-256 | `749e8835bb407336742f3fff5f81ba5eb476a42dfc0246f1107d4f28f1bea708` | Agent watchdog shell script |
| SHA-256 | `79e44b8523ee1e371436ca36e91d4d6f932beb371e7d77de756ffdd8f825e763` | Architecture-aware downloader shell script |
| SHA-256 | `e09ac5e8c23a768a2370cff29aca64be0d6e210e1176ee526bb7e47f537509ae` | Agent downloader shell script |

## References

- [Original N4D Mesh Controller research](https://blog.offensive-intel.com/n4d-mesh-controller/)
- [German Fernandez's campaign summary on X](https://x.com/1ZRR4H/status/2073046686163693867)
