In this Sherlock, players will investigate the Demodex rootkit attributed to Salt Typhoon.
The sample is a PowerShell dropper for the Demodex rootkit. The script holds an AES-encrypted, base64-encoded payload and decrypts it with a key passed on the command line. That key is a weak password, so it falls to a wordlist attack. The decrypted second stage writes an embedded DLL into the registry as binary data, registers a new service under a custom svchost group, and starts it. The DLL is a service DLL whose ServiceMain loops with a variable sleep and a fixed delay. Everything was recovered statically; nothing was executed.
DANGER.txt. Contains the PowerShell loader onedrived.ps1$cregdata blobTimestamp note: No timestamps are involved, so there is no Forensic Timeline for this case.
The scenario names the rootkit and the actor: Demodex, attributed to Salt Typhoon. The family name is read from the brief, not from the sample, which carries no family string in the loader.
Unzip the sample and open onedrived.ps1. The class name is not written in the clear: the script keeps it as a base64 string and decodes it at run time before instantiating it. Decoding that string gives the AES implementation class from System.Security.Cryptography.
unzip -P "$(cat DANGER.txt)" danger.zip -d work/x
Tip: The format mask has the shape of a dotted namespace followed by a class name, so look for a base64 string that decodes to that.
The top of the script declares its parameter by reading the first argument: [string] $k=$args[0]. That variable is the decryption key supplied by the operator at launch, so the key is never stored in the file.
The key is not in the sample, but the algorithm is AES and the script’s plaintext has a predictable start, so a wordlist attack can be validated automatically. Run each rockyou.txt entry as the key and accept the one that yields a script beginning with $svchostdata. One candidate decrypts cleanly. This answer was found by brute force against a known plaintext prefix rather than read from an artifact.
python3 -I work/brute.py
The recovered key then decrypts the payload to the second-stage script:
python3 -I work/dec.py
Gotcha: The key length in the format mask is twelve characters, which fits a short human-chosen password. That is why the wordlist works.
From Q4 the stage-2 script is readable. Search it for $cregvalue: it is assigned a short string that is used as a registry value name for the stored DLL data.
Copy the base64 blob assigned to $cregdata in stage 2, decode it to bytes, and hash the decoded bytes, not the base64 text. The decoded bytes are the DLL that later questions analyse.
base64 -d cregdata.b64 > work/msmp4dec.dll && sha256sum work/msmp4dec.dll
Gotcha: Hashing the base64 string gives a different value. The question asks for the hash of the binary data.
Stage 2 stores the service name in $svcname and uses it for the service registration and the start call.
$svcdllpath in stage 2 holds the location the DLL is written to and the service points at. It sits in System32 under a name that imitates a Microsoft media component.
Svchost groups are defined as values under the SvcHost key. Stage 2 calls New-ItemProperty with -Name $svcgroup on that key, which is what ties the custom group to the service so svchost loads the DLL.
Tip: The format mask uses the PowerShell drive form with a colon after the hive, so answer with
HKLM:\...rather thanHKEY_LOCAL_MACHINE\....
It is the last line of stage 2, which starts the service by the name held in $svcname.
Gotcha: The format mask includes the trailing semicolon, so it belongs in the answer.
Extract the DLL from the Q6 blob and list its strings. The import names are readable, and the computer-name call is the ANSI variant.
strings -a work/msmp4dec.dll | grep -i computername
A service DLL loaded by svchost exports ServiceMain, which the Service Control Manager calls on start. The Q8 DLL exports it. This is general Windows service behaviour, confirmed against the DLL’s export table.
Disassemble ServiceMain from Q12. Its control flow is flattened, so follow the calls to Sleep rather than reading top to bottom. One passes a register holding a variable, the other loads an immediate into ecx just before the call: mov ecx,0x2710 at 0x180001fad, which is 10000 in decimal.
objdump -d -M intel work/msmp4dec.dll | grep -B3 'call.*Sleep'
Gotcha: The immediate is hex. Convert
0x2710to decimal before answering.
| Phase | Action |
|---|---|
| Execution | PowerShell loader onedrived.ps1 run with the decryption key as its first argument |
| Defense Evasion | AES-encrypted, base64-encoded second stage decrypted in memory |
| Defense Evasion | Class name hidden as a base64 string inside the loader |
| Persistence | Embedded DLL stored as base64 registry data and written to System32 |
| Persistence | Custom svchost group registered under the SvcHost key |
| Execution | Service created and started with Start-Service |
| Execution | ServiceMain in the DLL loops with a variable and a fixed 10 second sleep |
| Type | Value |
|---|---|
| File (loader) | onedrived.ps1 |
| File (DLL) | C:\Windows\System32\msmp4dec.dll |
| SHA-256 (DLL) | 3b1c251b0b37b57b755d4545a7dbbe4c29c15baeca4fc2841f82bc80ea877b66 |
| Service | MsMp4Hw |
| Registry value | midihelp |
| Registry key | HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SvcHost |
| Decryption key | password@123 |
| Technique | ID | Description |
|---|---|---|
| Command and Scripting Interpreter: PowerShell | T1059.001 | Loader and stage 2 are PowerShell |
| Obfuscated Files or Information: Encrypted/Encoded File | T1027.013 | AES-encrypted, base64-encoded payload and base64-hidden class name |
| Modify Registry | T1112 | DLL stored as binary registry data and a new svchost group value written |
| Create or Modify System Process: Windows Service | T1543.003 | MsMp4Hw service installed to load the DLL through svchost |
| System Services: Service Execution | T1569.002 | Service started with Start-Service |
A weak password protecting a payload is a weak lock. The loader’s key sat in a common wordlist and the plaintext had a known prefix, so the payload opened in seconds. Triage of encrypted droppers should try a wordlist against the known script header before reaching for heavier tooling.
New values under the SvcHost key are rare and worth alerting on. The persistence depends on adding a custom group under HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SvcHost. Monitor writes to that key and flag service creation whose ServiceDll is a recently written file in System32.
Large binary registry values are a hiding place. The DLL travelled as base64 and was stored as registry data. Flag PowerShell that decodes a big base64 string and writes it as a binary registry value, and review PowerShell script block logs for New-ItemProperty against service-related keys.
Name-matching a Microsoft component is not trust. msmp4dec.dll and MsMp4Hw imitate media components. Check the signature and the file’s origin, not the name.
The Demodex dropper turned a PowerShell script into a svchost-hosted service with an embedded DLL, and its only secret was a password that sat in a wordlist. The loader’s protection was thin; detection value lies in the SvcHost registry write and the service start.