> For the complete documentation index, see [llms.txt](https://ghostinthehive.gitbook.io/ghostinthehive-as-a-ghost-in-the-hive/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ghostinthehive.gitbook.io/ghostinthehive-as-a-ghost-in-the-hive/tradecraft-analysis-defenders-catalogue/muddywater-pt1-ghostbackdoor.md).

# MuddyWater pt1: GHOSTBACKDOOR

{% hint style="info" %}
This is the first part of a series conducting tactical, strategic intelligence analysis and threat research over MuddyWater Iranian Threat Group, the process will involve studying the actor's strategic and operational shifts, capabilities evolution, tradecraft analysis and tactics ceiling. The output will surface differently for defenders, intelligence analysts and capabilities engineers; you choose your path.
{% endhint %}

{% hint style="warning" %}
This article till not Contain IoCs — check the references section for TI sources with operational IoCs.
{% endhint %}

{% embed url="<https://youtu.be/b10vI6kBPN8>" %}

## Threat Actor Intelligence Overview

**MuddyWater** is a prolific Iranian state-sponsored threat actor, subordinate to Ministry of Intelligence and Security (MOIS), with a primary mission tied to it; <mark style="color:red;">**Long Term Cyber Espionage**</mark>*.* This threat group is known for its broad targeting of government, telecommunications, defense, and energy sectors, primarily in the Middle East but with a global operational scope including the United States and Europe.&#x20;

Recent Activity across 2025-2026, MuddyWater demonstrates a significant operational evolution, marked by the modernization of its toolkit with malware written in novel languages like Rust and Deno, the adoption of advanced C2 techniques leveraging blockchain, the abuse of legitimate Remote Monitoring and Management (RMM) tools for stealth and in some cases AI-Assisted infrastructure and toolkit development. Moreover; MuddyWater leveraged strategic integration with the cybercrime ecosystem to enhance capabilities and complicate attribution.

### MuddyWater Tradecraft Evolution

Given the current geopolitical tension in the Middle East where escalation and tradecraft evolution is noticed across all frontiers from traditional warfare, Intelligence operations, strategic adaptation and cyber warfare; MuddyWater as the case with many other Iranian APTs has shown a clear evolutionary path across its corpus.

Early operations relied heavily on classic spear-phishing with malicious Office documents containing VBA macros that executed PowerShell-based backdoors. This established a foundation of abusing native Windows internals for execution and persistence. A significant shift occurred with the adoption of legitimate Remote Monitoring and Management (RMM) tools like **Syncro** and **Atera** as a primary vector. Instead of dropping custom malware, the actor began luring victims into installing signed, legitimate RMM agents, effectively outsourcing their persistence and C2 to trusted commercial platforms. This tactic dramatically reduces their malware footprint and blends their activity with normal administrative traffic.&#x20;

Most recently, MuddyWater has demonstrated further innovation by integrating third-party Malware-as-a-Service (MaaS) like the `Tsundere botnet` and leveraging novel C2 mechanisms. This includes the `EtherHiding` technique, which uses the Ethereum blockchain as a decentralized dead-drop resolver for C2 addresses, and the use of the Telegram Bot API. The group has also diversified its execution payloads, developing backdoors like `Dindoor` and `CHAR` which utilize the Deno and Rust runtimes, respectively, likely to evade security products focused on traditional PowerShell or .NET malware. In cases where they gain administrative privileges, they have been observed creating new Windows services as a persistence technique, masquerading as the `MicrosoftVersionUpdater` service installed by the `GhostBackDoor` implant. They also use Scheduled Tasks, as seen in the **Operation Olalampo** campaign where operators manually created a task named `DailyUpdate` to re-execute their payload. However, their most effective persistence mechanism is the installation of legitimate RMM agents, which provides them with durable, SYSTEM-level access on the endpoint.

Moving to the actor stealth tradecraft which centered on masquerading and anti-analysis, payloads are often obfuscated, with reports suggesting the use of public tools like `Obfuscator.io` for their JavaScript-based malware. A key evasion technique is geo-fencing: the `Tsundere botnet`, used by MuddyWater, checks the system's language using `[System.Globalization.CultureInfo]::InstalledUICulture.Name` and terminates if it detects a CIS country, a common feature of Russian-origin malware intended to avoid domestic targeting. And on the anti-analysis side of the game, `GhostFetch` downloader includes a comprehensive suite of anti-sandbox checks, including verifying RAM size, CPU core count, mouse movement, and using `GetTickCount64` to detect timing anomalies caused by debuggers. Yet again; the primary stealth tactic, however, is the abuse of legitimate, signed software (RMMs) and services (cloud storage), which blends their activity with benign network traffic.

<figure><img src="/files/zOjZpkKP0NGAvXzXeMq9" alt=""><figcaption></figcaption></figure>

### MuddyWater Operational Chains

The Observed operational chain for MuddyWater has a **primary** RMM-abuse path and an **alternate** macro-driven path. Both branches converge on the same downstream discovery, staging, and exfiltration steps. `GhostBackdoor` with `MicrosoftVersionUpdater` service persistence sits on the alternate branch, which plays as the fallback strategy if the RMM abuse path is blocked due to environment limitation.

```mermaid
flowchart TD
    IA[Spear-phishing<br/>T1566<br/>lure: RMM installer or Office doc]

    IA -->|primary| E1[User Executes RMM Installer<br/>T1204.002]
    IA -.->|alternate| A1[PowerShell Stager Execution<br/>T1059.001<br/>from VBA macro]

    E1 --> P1[RMM Agent Persistence<br/>T1219<br/>Syncro / Atera - SYSTEM service, signed]
    A1 --> A2[Registry Run Key OR<br/>Windows Service Persistence<br/>T1547.001 or T1543.003<br/>MicrosoftVersionUpdater]

    P1 --> C1[C2 via RMM Platform<br/>T1219<br/>legitimate encrypted channel]
    A2 --> A3[Novel C2 Channel<br/>T1102<br/>EtherHiding / Telegram / Backblaze]

    C1 --> D[System Information Discovery<br/>via LOLBins<br/>T1082<br/>reg.exe query burst]
    A3 --> D

    D --> S[Data Staging<br/>T1074.001<br/>%USERPROFILE% / Public]

    S --> X[Exfiltration via Rclone<br/>T1567.002<br/>to Backblaze / Wasabi]

    classDef primary fill:#00b3a4,stroke:#00b3a4,color:#fff
    classDef alternate fill:#f2953c,stroke:#f2953c,color:#fff
    classDef shared fill:#3d8bd8,stroke:#3d8bd8,color:#fff
    classDef anchor fill:#e5407a,stroke:#e5407a,color:#fff,stroke-width:3px

    class E1,P1,C1 primary
    class A1,A3 alternate
    class IA,D,S,X shared
    class A2 anchor
```

Note that the two branches are not sequential; they are the actor's fallback structure. When the target environment blocks RMM installation for some reason be it an endpoint policy, application-control ..etc, the operator pivots to the macro-and-scripting path.

#### **The primary chain — living off legitimate RMM tooling** <a href="#id-31--the-primary-chain--living-off-legitimate-rmm-tooling" id="id-31--the-primary-chain--living-off-legitimate-rmm-tooling"></a>

The primary chain is the one that makes MuddyWater genuinely difficult to detect in the current reporting window. There's no malware to drop and no unusual binary to fingerprint at any stage of the operation.

It starts with a <mark style="color:$primary;">**spear-phishing**</mark> e-mail carrying a helpdesk-style pretext — support ticket, IT migration prompt, remote-access request — with a legitimate RMM installer as the attachment or download link. **Syncro**, **Atera**, and **ScreenConnect** are the products that surface repeatedly across multiple incidents. The victim installs the software themselves, and the agent immediately phones home — not to an unknown actor-controlled server, but to an actor-controlled tenant of the RMM platform itself.

From there, everything downstream inherits the RMM agent's cover. The agent installs itself as a SYSTEM-level service with a real Authenticode signature from the RMM vendor and auto-restart on reboot. The command-and-control channel is the RMM's own encrypted control plane — a certificate-valid TLS session to the vendor's own infrastructure. From an endpoint's telemetry POV, this looks exactly like legitimate remote-management software behaving normally, because it IS legitimate remote-management software behaving normally.

Detecting this layer isn't primarily a rules problem — it's an inventory problem. An organization needs either application control (allow-list this specific RMM vendor's binary hash) or an RMM baseline it can diff against — "*we run Atera; anything else is suspect.*" or "*we run Atera; with a studied baseline activity to flag against*". Behavioral endpoint rules against the RMM agent's process behavior will in majority produce noise, not signal.

#### **The alternate chain — macro-and-scripting — where GhostBackDoor lives** <a href="#id-32--the-alternate-chain--macro-and-scripting-and-where-ghostbackdoor-lives" id="id-32--the-alternate-chain--macro-and-scripting-and-where-ghostbackdoor-lives"></a>

When the RMM path isn't available — because the target environment blocks unauthorized RMM installs, or because the operator wants a lower-visibility footprint — the alternate chain looks a lot more like classical macro-and-scripting tradecraft. Yet with a couple of interesting twists..

The e-mail still lands, but the payload is now an Office document carrying a VBA macro. The macro decodes an embedded payload — often hidden inside a UserForm object rather than the macro body itself, which trips up signature-based scanners — then executes it via one of two runtimes. The classical route is `WScript.Shell`. The more interesting variant we can observe in several sampled events invokes `Win32_Process.Create` through WMI, with `wmiprvse.exe` as the intermediate parent. That WMI-parented variant is stealthier because it breaks the standard (Office -> macro -> child-process) chain most detection tooling keys on, although we won't focus on the invocation through WMI in this part, It is worth mentioning to have the full scope of their operations.&#x20;

Downstream of the stager, the payload varies. Three families surface repeatedly across the corpus:

* **Rust** — `CHAR` and `GhostBackdoor`
* **Deno-runtime JavaScript** — `Dindoor`

Each is likely a hedge against a specific vendor's detection posture. Traditional PowerShell and .NET signatures don't fire on Rust or Deno binaries.

<mark style="color:$primary;">**Persistence**</mark> on this branch forks based on the privilege the stager has achieved on the endpoint.

<mark style="color:yellow;">**With administrative privilege**</mark>, the payload registers itself as a Windows Service via the standard SCM API sequence — `OpenSCManagerW -> CreateServiceW -> StartServiceW`. The service name that shows up consistently in the reports is `MicrosoftVersionUpdater` - Group-IB, Operation Olalampo.

The service carries a specific compound of properties: `AUTO_START` start type, LocalSystem account, `Win32OwnProcess` service type, and an image path outside the standard Microsoft directories (typically under `%PROGRAMDATA%\...` or `%APPDATA%\...`).&#x20;

<mark style="color:yellow;">**Without administrative privilege**</mark>, the fallback is a Run key under `HKCU` . A PowerShell one-liner writes a value chaining ( `cmd.exe /c` -> `node.exe` -> a random-named `.js` file) under `%LOCALAPPDATA%\Nodejs\` — the Tsundere-botnet variant of the actor's `Node.js` loader lineage. It's noisier and less durable than the service option, but it doesn't need admin.

Worth naming as a MuddyWater habit: **redundant persistence layers**. That is simply dropping a scheduled task alongside whichever persistence they've chosen. For example:

```powershell
schtasks /create /sc daily /st 09:00 /tn "DailyUpdate" /tr "C:\Users\Public\Downloads\novaservice.exe"
```

If one layer is remediated by an incident response, the other survives quietly.

<mark style="color:$primary;">**Command-and-control**</mark> on this branch is where MuddyWater's 2025–2026 innovation shows up most visibly. Rather than reach for a traditional C2 stack — their own hosted domains, TLS certificates they pay for — the observed implants lean on public services that most enterprise proxies and IDS platforms don't flag by default:

* **EtherHiding** — the payload is embedded in blockchain-hosted content and retrieved via a public Ethereum RPC endpoint. The Ethereum network becomes the C2 resolver.
* **Telegram Bot API** — commands exchanged through Telegram's official bot channel.
* **Backblaze, Wasabi, Dropbox** — used for payload staging and downloader-hosted binaries.

This pattern isn't creative for creativity's sake — it's deliberate. Traditional C2 lookup means known-bad domains, known-bad ASN blocks, tell-tale certificate patterns. Redirecting through Ethereum RPC or the Telegram Bot API means the C2 lookup looks like a developer-tool call, because at the wire level that's what it is..

#### **The shared downstream — discovery, staging, exfiltration** <a href="#id-33--the-shared-downstream--discovery-staging-exfiltration" id="id-33--the-shared-downstream--discovery-staging-exfiltration"></a>

Both branches converge here. Regardless of how the payload arrived and how it persisted, the tail of the operation is the same.

The first thing the operator does once running on the host is fingerprint it. The tell is remarkably consistent: three specific `reg.exe query` calls, fired in a burst of at least three within a 60-second window, against a fixed triad of registry values:

* `HKLM\HARDWARE\DESCRIPTION\System\BIOS`
* `HKLM\SOFTWARE\Microsoft\Cryptography!MachineGuid`
* `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion!ProductId`

Individually, each of these queries won't mean anything really. Together, in that time window, they're one of the actor's most reliable behavioral signatures that we can use for detection.

Once the operator has selected what to exfiltrate, the collected data is copied to a user-writable staging location before it leaves the endpoint. The consistent choices are `C:\Users\Public\Documents\` (with `.log`-suffixed files like `ManagerProc.log`, `PhotoAcq.log`, `CertificationKit.ini`) or `%USERPROFILE%\Downloads\`. Notably, these are frequently the same paths that hosted the persistence-payload binaries earlier in the chain — the operator reuses their working directory rather than picking a fresh one.

The final step here is `Rclone` and their <mark style="color:$primary;">**Exfiltration**</mark> pattern. The operator drops `rclone.exe` (typically renamed to blend with legitimate artifacts in the staging directory) and uses it to push the staged data to actor-controlled cloud storage. `Backblaze B2` and `Wasabi` are the two providers that show up most consistently — both offer S3-compatible APIs, low friction to spin up new accounts, and don't automatically flag high-volume writes from unfamiliar clients.

## GhostBackdoor Corpus as a Capability <a href="#id-4--ghostbackdoor-as-a-tradecraft-example" id="id-4--ghostbackdoor-as-a-tradecraft-example"></a>

Now in this article we're going to take **GhostBackdoor** as our worked example and trace how it obtains persistence on a Windows endpoint — the process it runs, the registry state it leaves behind, the WMI hooks it can be validated against. Rather than eyeballing every MuddyWater capability at surface level, we're going to sit with this one specific implant and follow the persistence chain end-to-end, then build a detection around what we find.

### The tradecraft — `MicrosoftVersionUpdater` service persistence <a href="#the-tradecraft--microsoftversionupdater-service-persistence" id="the-tradecraft--microsoftversionupdater-service-persistence"></a>

During my Threat Research around MuddyWater, one behaviour kept surfacing; `GhostBackdoor`, once it has administrative privilege on the endpoint, calls the Windows Service Control Manager and asks to register itself as a service named `MicrosoftVersionUpdater`. That single design decision carries a lot of intent, and it's worth unpacking why an actor at MuddyWater's maturity level reaches for it specifically.

Windows Service persistence sits inside a small set of techniques the actor reaches for when they have already obtained administrative privilege and want persistence that survives user-logoff, reboot, and — in most environments — the standard on-endpoint remediation playbook. Two properties make it attractive:

* **Auto-start as LocalSystem** — no user session required, higher privilege than a standard Run key.
* **Blends with a noisy surface** — Windows carries hundreds of services; a well-named entry hides in the middle of the list until someone specifically looks. In other words, a single good choice of a service name, can fly them under the radar if no one is carefully looking. Which if you think about it is a very smart bet.

Now looking at how MuddyWater actually executes this technique, we can notice three tells that show up consistently:

1. **User-writable image path** — the service binary lives outside `C:\Windows\` and `C:\Program Files\`, typically under `%PROGRAMDATA%\..` or `%APPDATA%\..`. No legitimate Microsoft service does this.
2. **Service name masquerading a Microsoft component** — the string `MicrosoftVersionUpdater` is chosen specifically to blend into the Microsoft-update service naming surface. Though apparently if we look it up, it's not a real Microsoft service.
3. **`AUTO_START` + `LocalSystem` + `Win32OwnProcess` triad on a first-time service install** — any single property is common; the combination on a name never seen in the environment before is not.

Now the install itself is executed through the SCM APIs — `OpenSCManagerW -> CreateServiceW -> StartServiceW`. Neither the API sequence nor the resulting service registration is inherently malicious; both fire many times daily on any managed endpoint. What flags is the **COMPOUND**: a service being installed for the first time, running as `LocalSystem`, `AUTO_START`, `Win32OwnProcess`, from a path outside the standard Microsoft directories, and carrying a service name that does not exist in the Microsoft services baseline. That compound is we can mark as suspicious and build a rule against — and importantly, it's what survives even if the attacker changes the specific service name. It's the behaviour that saves the actor in a detection environment that's geared toward signatures.

### The GhostBackdoor persistence flow — process, registry, WMI

Before we build a PoC as a reproducible tradecraft, let's look at the full shape of what `GhostBackdoor` does at install time. There are three phases worth naming separately:

{% hint style="info" %}
Although `GhostBackdoor` is written in Rust in the wild. I'll be using **C** for the API walkthrough in this article because the Windows Service Control Manager APIs read cleanest in C — each `CreateServiceW` argument maps one-to-one to a behavioral property we care about. The Rust equivalent (via the `windows-service` crate) produces identical runtime behavior.
{% endhint %}

```c

// PHASE 1: Process --- the implant asks SCM to register a service ---
SC_HANDLE scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);

SC_HANDLE svc = CreateServiceW(
    scm,
    L"MicrosoftVersionUpdater",              // service name  -> masquerade
    L"Microsoft Windows Version Updater",    // display name
    SERVICE_ALL_ACCESS,
    SERVICE_WIN32_OWN_PROCESS,               // standalone binary
    SERVICE_AUTO_START,                      // survives reboot, no login
    SERVICE_ERROR_NORMAL,
    L"C:\\ProgramData\\...\\payload.exe",    // user-writable image path
    NULL, NULL, NULL,
    NULL,                                    // NULL account -> LocalSystem
    NULL);

StartServiceW(svc, 0, NULL);

// PHASE 2: Registry --- SCM writes the service definition FOR us ---
//   The implant itself never touches the registry. services.exe does,
//   on the implant's behalf, in response to CreateServiceW above.
//   State that lands on disk:
//
//     HKLM\System\CurrentControlSet\Services\MicrosoftVersionUpdater\
//       ImagePath   = C:\ProgramData\...\payload.exe   <- REG_EXPAND_SZ
//       Start       = 2                                <- AUTO_START
//       Type        = 0x10                             <- Win32OwnProcess
//       ObjectName  = LocalSystem
//       DisplayName = Microsoft Windows Version Updater
RegOpenKeyExW(HKEY_CURRENT_USER,
                 L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
                 0,
                 KEY_SET_VALUE,
                 &hKey)
                 //
RegSetValueExW(hKey,
                  L"WindowsUpdateService",    // masquerade value name
                  0,
                  REG_SZ,
                  (BYTE*)L"cmd.exe /c node.exe %LOCALAPPDATA%\\Nodejs\\loader.js",
                  cbData)
                  //
RegCloseKey(hKey)

// PHASE 3: WMI --- optional post-install validation ---
//   Not required for persistence, but seen in the corpus as the actor's
//   way to confirm the service is registered and running before moving on.

IWbemServices *pSvc = /* connect to ROOT\CIMV2 */;
IEnumWbemClassObject *pEnum = NULL;

pSvc->ExecQuery(
    L"WQL",
    L"SELECT * FROM Win32_Service WHERE Name='MicrosoftVersionUpdater'",
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    NULL,
    &pEnum);

// pEnum now yields the Win32_Service instance if the install succeeded.
```

Three things worth naming from that flow, because each has a different defender implication:

* **Phase 1 (process)** is where all the observable intent lives. Six properties get baked into the `CreateServiceW` call in a single instruction.
* **Phase 2 (registry)** is a side-effect of Phase 1. `services.exe` performs these registry calls in response to the SCM call.&#x20;
* **Phase 3 (WMI)** is post-install validation, not part of persistence itself.

Given laying out the execution flow, the next move is straightforward; a minimum benign PoC that reproduces exactly what we see here, then walk through how to detect it.

## Reproducing the Tradecraft

Everything up to this point has been observation and analysis of what the corpus tells us. Now we cross the line and rebuild the thing. So the next step is a small, benign PoC that reproduces the persistence flow exactly, without doing anything else. Simply to set up the workflow for building detections.

### Phase 1 — The SCM chain

The heart of the tradecraft is a four-step sequence: place the future service binary somewhere user-writable, open a handle to the Service Control Manager, create the service with specific properties, start it. Each step has its own narrative.

**Self-copy to a user-writable path**

Before we touch SCM at all, we place the future service binary somewhere that satisfies — a user-writable path outside `C:\Windows\` and `C:\Program Files\`. In the PoC we use `C:\ProgramData\ghostbackdoor-poc\`:

```c
CreateDirectoryW(INSTALL_DIR, NULL);

wchar_t self[MAX_PATH], dst[MAX_PATH];
GetModuleFileNameW(NULL, self, MAX_PATH);
swprintf(dst, MAX_PATH, L"%ls\\%ls.exe", INSTALL_DIR, svc_name);
CopyFileW(self, dst, FALSE);
audit(L"COPY_BINARY", dst);
```

This is where the observable trail begins. **Sysmon EID 11 (`FileCreate`)** fires for the copy destination. Alone it isn't detectable tradecraft — thousands of legitimate installers do the same — but it's the first breadcrumb the compound detection will later stitch for a better detection.

&#x20;**`OpenSCManagerW` — asking for a handle**

```c
SC_HANDLE scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (!scm) {
    wprintf(L"[!] OpenSCManagerW failed: %lu (elevated?)\n", GetLastError());
    return 1;
}
```

Three arguments, all deliberate:

* `lpMachineName = NULL` — the local host. Remote SCM would need SMB/RPC and additional network permissions we don't have and don't want to advertise.
* `lpDatabaseName = NULL` — defaults to `SERVICES_ACTIVE_DATABASE`
* `dwDesiredAccess = SC_MANAGER_CREATE_SERVICE` — the minimal privilege for what comes next. `SC_MANAGER_ALL_ACCESS` would work too, but it's louder — grants more privilege than we need.

Returns a valid `SC_HANDLE` on success, `NULL` on failure. If the caller isn't an administrator, `GetLastError()` returns `ERROR_ACCESS_DENIED` (5) — the expected failure mode for an unelevated run.

**`CreateServiceW` — six properties in one call**

```c
SC_HANDLE svc = CreateServiceW(
    scm,
    svc_name,                            // service key name
    DEFAULT_DISP_NAME,                   // display name (masquerade)
    SERVICE_ALL_ACCESS,
    SERVICE_WIN32_OWN_PROCESS,           
    SERVICE_AUTO_START,                  // boot persistence
    SERVICE_ERROR_NORMAL,
    dst,                                 // image path
    NULL, NULL, NULL,
    NULL,                                // NULL => LocalSystem
    NULL);
```

Walking through the arguments in the order they matter:

1. **`lpServiceName` (masquerade)** — the string `MicrosoftVersionUpdater`. Visible in `services.msc`, in `HKLM\System\CurrentControlSet\Services\`, in every downstream telemetry field. This is the exact string legacy name-based rules key on — and the one an operator changes to bypass them.
2. **`dwServiceType = SERVICE_WIN32_OWN_PROCESS` (standalone binary)** — value `0x10`. Roughly legitimate Microsoft services are `SERVICE_WIN32_SHARE_PROCESS` (svchost-hosted). Own-process is what a self-contained implant would need.
3. **`dwStartType = SERVICE_AUTO_START` (boot persistence)** — value `0x2`. Runs on every boot with no user login required.
4. **`lpBinaryPathName` (user-writable image path)** — the `dst` path we computed above under `ProgramData`. Legitimate Microsoft services live in `C:\Windows\System32\` or under `C:\Program Files\`. Anything outside those two roots is the strongest single anomaly signal in the compound.
5. **`lpServiceStartName = NULL` (LocalSystem)** — passing `NULL` for the account defaults to LocalSystem, the SYSTEM privilege.

If we're going to build a detection that survives `GhostBackdoor` changing the service name in the future, this is the layer to target.

### Phase 2 — The registry side-effect <a href="#id-53--phase-2--the-registry-side-effect" id="id-53--phase-2--the-registry-side-effect"></a>

Here's the thing that can catch you off-guard: **the implant itself never writes to the registry.** The three **SCM** calls above produce a burst of registry writes under `HKLM\System\CurrentControlSet\Services\MicrosoftVersionUpdater\`, but `services.exe` performs them — on behalf of our process, in response to `CreateServiceW`.

If we peek at the resulting registry state (via `regedit`, Sysmon EID 13, or MDE `DeviceRegistryEvents`), we see:

```
HKLM\System\CurrentControlSet\Services\MicrosoftVersionUpdater\
    ImagePath    = C:\ProgramData\ghostbackdoor-poc\MicrosoftVersionUpdater.exe  [REG_EXPAND_SZ]
    Start        = 2                                                              [REG_DWORD]  <- AUTO_START
    Type         = 0x10                                                           [REG_DWORD]  <- Win32OwnProcess
    ObjectName   = LocalSystem                                                    [REG_SZ]
    DisplayName  = Microsoft Windows Version Updater                              [REG_SZ]
    ErrorControl = 1                                                              [REG_DWORD]
```

Each of those values fires a Sysmon EID 13 event (`RegistryEvent SetValue`), and the call stack shows the writing process as `services.exe` — not our PoC binary.

This is why a defender-side rule keyed on *"unusual process writes to `HKLM\...\Services\`"* would miss the tradecraft entirely — from the registry's POV this is a routine SCM operation. The write pattern is a downstream *effect*, and the detection needs to key on the *cause* one layer up (the `ServiceInstalled` event fired by SCM itself) to have the full context.

### Phase 3 — WMI validation <a href="#id-54--phase-3--wmi-validation-brief" id="id-54--phase-3--wmi-validation-brief"></a>

The `install()` function in the PoC stops at `StartServiceW`. The observation, however, shows the actor occasionally following up with a WMI query to validate the install succeeded before moving on:

```
ImplantExecQuery WQL
SELECT * FROM Win32_Service
WHERE Name = MicrosoftVersionUpdaterwmiprvse.exe
under svchost -k netsvcs
```

```c
IWbemServices *pSvc = /* connect to ROOT\CIMV2 — full COM setup */;
IEnumWbemClassObject *pEnum = NULL;

pSvc->ExecQuery(
    L"WQL",
    L"SELECT * FROM Win32_Service WHERE Name='MicrosoftVersionUpdater'",
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    NULL, &pEnum);
```

WMI's full COM initialization is a lot heavier than the SCM sequence above — roughly 8 API calls just to run one query. It is worth naming here because it gives us a bonus hunt surface: if we see `SELECT * FROM Win32_Service WHERE Name = <recently-installed-service-name>` immediately after a `ServiceInstalled` event, that's a strong signal.

The tradecraft footprint of the query itself though is minimal — WMI queries don't fire a dedicated telemetry event. What *is* visible is the process chain: `wmiprvse.exe` spawns under `svchost.exe -k netsvcs -p -s Winmgmt` to service the ad-hoc query. Combined with the preceding `ServiceInstalled` event and matching service name. In other words, this can fall as the second signal right after the service creation signal, to build an event correlation rule around.

## Building Defensive Capabilities

With the tradecraft understood and reproduced, we can now turn each observation into a detection primitive.

### IoC detection — service name match <a href="#id-61--ioc-detection--service-name-match" id="id-61--ioc-detection--service-name-match"></a>

We can catch known MuddyWater service-name strings immediately. Quick to author and deploy, fast to fire, but easy to bypass.

<figure><img src="/files/XwxxTLBvFbiJDlX3umsk" alt=""><figcaption></figcaption></figure>

**Telemetry**:

* Windows Event Log — event ID `7045` (service installed)
* Sysmon — event ID `13` (registry SetValue on the service key)

```pseudo
if EventID == 7045
   and ServiceName in [
       "MicrosoftVersionUpdater",   // Group-IB Olalampo
       "DailyUpdate",               // scheduled-task variant
       CertificationKit.{ini,exe},
       MicrosoftWordUser.exe,
       MicrosoftExcelUser.exe,
       ...                          // other known MuddyWater artifacts
   ]
then fire alert (medium confidence)
```

**Trade-off**: fires the first time, and useless the instant the operator rotates the string. Any variant with a different `ServiceName` walks past this rule.

### IoC detection — registry path match <a href="#id-62--ioc-detection--registry-path-match" id="id-62--ioc-detection--registry-path-match"></a>

Same way we can also catch registry-write bursts targeting known MuddyWater service-key names. Same-flavor IoC as previous detection, just a different vantage point.

<figure><img src="/files/iSFEakiFfL29T2lOtfQr" alt=""><figcaption></figcaption></figure>

**Telemetry**:

* Sysmon — event ID `13` (registry SetValue)
* Sysmon — event ID `12` (registry key create)

```pseudo
if EventID == 13
   and TargetObject matches
       "HKLM\System\CurrentControlSet\Services\<known-name>\*"
then fire alert (medium confidence)
```

**Trade-off**: identical to service name match. The moment the operator picks a service name we haven't seen before, the registry path changes too — this rule goes silent.

### Compound IoC detection — service creation properties <a href="#id-63--behavioral-detection--wi-010-six-property-compound" id="id-63--behavioral-detection--wi-010-six-property-compound"></a>

Here we catch the properties that makes up the service, not the string. Fires on any first-observation service install that simultaneously satisfies the properties we identified in the [`CreateServiceW` SCM Chain](#phase-1-the-scm-chain). Survives arbitrary renames because none of the six properties key on a specific service name string.

<figure><img src="/files/8qedoaOOjhj37zHw75gk" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/WgY45npQGKa2BzxlEkwP" alt=""><figcaption></figcaption></figure>

**Telemetry**:

* Windows Event Log — event ID `7045` (primary — carries all six fields in one event)
* Sysmon — event ID `13` (reinforcing signal on the registry-write side)

```pseudo
if EventID == 7045
   and ServiceType == "user mode service"                       // property 1
   and StartType   == "auto start"                              // property 2
   and AccountName == "LocalSystem"                             // property 3
   and ImagePath   NOT startswith "C:\Windows\"                 // property 4
   and ImagePath   NOT startswith "C:\Program Files\"
   and ImagePath   NOT startswith "C:\Program Files (x86)\"
   and ServiceName NOT IN microsoft_services_allowlist          // property 5
   and ServiceName NOT IN historical_service_names[this_host]   // property 6 (first observation)
then fire alert (high confidence)
```

Two tuning opportunities that would matter:

* **Microsoft-services allow-list**
* **First-observation window**

### Behavioral correlation — service install followed by WMI validation <a href="#id-64--behavioral-correlation--service-install-followed-by-wmi-validation" id="id-64--behavioral-correlation--service-install-followed-by-wmi-validation"></a>

From our analysis, we have a chance to catch the operator's hand-on-keyboard follow-through. we noted that the actor sometimes runs `SELECT * FROM Win32_Service WHERE Name = <installed-service>` immediately after registering the service as validation that the install succeeded before the operator moves on to the next stage.

That two-event sequence — service install followed within a short window by a WMI query for that exact service name — is a genuinely behavioral signal: two distinct events, causally linked, within a temporal bound. This is what "behavioral" means in the strictest sense, as opposed to the previous detection, which is a compound of static properties evaluated on one event.

**Telemetry**:

* Windows Event Log — event ID `7045` (service install — Event A)
* Sysmon — event ID `1` (process creation for `wmiprvse.exe` under `svchost.exe -k netsvcs -p -s Winmgmt`, spawned to service the ad-hoc WMI query — Event B)

```pseudo
Event A:
  EventID == 7045                              // service install
  and satisfies the wi-010 compound (§6.3)     // suspicious install
  capture ServiceName as $name

then within 60 seconds on the same host:

Event B:
  Sysmon EventID == 1                          // process create
  and ProcessName == "wmiprvse.exe"
  and ParentProcessName contains "svchost.exe"
  and Command executing a WQL query
      referencing Win32_Service where Name = $name

if sequence [A -> B] matches:
  fire alert (high confidence — operator hand-on-keyboard signal)
```

**Trade-off**: only fires when the operator actually performs the validation step, which they don't always do — some payloads register the service and move on without checking. This rule is **complementary to the** [**compound IoC** ](#id-63--behavioral-detection--wi-010-six-property-compound)[**detction**](#id-63--behavioral-detection--wi-010-six-property-compound)**, not a replacement**. Deploying both together layers a structural detection with a truly-behavioral one along with the first two signature hits, this is effectively applying a defense-in-depth approach.

Now that the theme started to show up, it is worth mentioning that building detections is a recursive process of `analyzing the tradecraft -> investigtating the telemetry -> drating a primitive detection -> rationalizing the logic -> calculating tradeoffs -> pivoting to a new signal` and so on and so forth. Until a proper framework for building defensive capabilities of various kinds is consolidated, everything else is speculation and hoping for the best outcome.

In later parts we will continue working on **MuddyWater** and climb up their capabilities evolution ladder and see what we can learn from threat actors when survival and operationality are at stake.&#x20;

***

**References:**

<https://blog.checkpoint.com/research/what-defenders-need-to-know-about-irans-cyber-capabilities/>\
<https://research.checkpoint.com/2026/iranian-mois-actors-the-cyber-crime-connection/>\
<https://www.esentire.com/blog/muddywater-apt-tsundere-botnet-etherhiding-the-c2>\
<https://www.group-ib.com/blog/muddywater-operation-olalampo/>\
<https://hunt.io/blog/iranian-apt-infrastructure-state-aligned-clusters>\
<https://www.security.com/threat-intelligence/iran-cyber-threat-activity-us>\
<https://securityaffairs.com/189060/apt/iran-linked-muddywater-deploys-dindoor-malware-against-u-s-organizations.html>\
<https://www.security.com/threat-intelligence/iran-cyber-threat-activity-us>\
<https://hunt.io/blog/iranian-apt-infrastructure-state-aligned-clusters>\
<https://www.security.com/threat-intelligence/iran-cyber-threat-activity-us>
