ECHO · Write up · MetaCTF
Another Data Trail
Another Data Trail is a MetaCTF forensics challenge, solved by
Digital Orukami for ECHO Club: a virtual disk that mounts clean, a folder of two
hundred ordinary looking Windows scheduled tasks, and one file carrying a second payload
that no directory listing, Explorer window or plain signature scan will ever show you.
Written up end to end, the flag redacted below as RedactedCTF{…}.
Provenance
Another Data Trail is a Forensics challenge on the MetaCTF platform worth 250 points. Solved by Digital Orukami for ECHO Club. This is a teaching write up, not a submission log.
The flag below is redacted as RedactedCTF{…}; the method is what
is being taught, not the string.
Disclaimer. This write up documents a challenge solution completed on the MetaCTF platform for educational purposes. All analysis was performed on a controlled, offline copy of the provided evidence. ECHO Club is not responsible for the organization or administration of the MetaCTF platform.
MetaCTF · Forensics · 250 pts
Another Data Trail
The setup
The brief was deliberately thin: there seems to be some obscure data stream persistently existing in our system, and despite our efforts to investigate, we cannot detect its presence. Please help me analyze it. The only resource provided was a single archive holding one file, a virtual disk image in Microsoft’s VHDX format, about 68 megabytes once unpacked.
Nothing in the prompt named a tool or a format. The word doing all the work is stream, and the honest answer to we cannot detect its presence turned out to be: with the tool most people already reach for first.
Mounting without writing
Windows can attach a VHDX natively, so the image was mounted read only rather than touched with anything that might write back to it, the first rule of handling evidence. That produced a single GPT partition, assigned a drive letter, holding three top level items: a recycle bin, system volume information, and a folder named Tasks.
Walking that folder recursively turned up 209 files, and every one of them looked legitimate. It was, byte for byte, a copy of a Windows scheduled task library: Office update tasks, OneDrive tasks, Windows Update tasks, BitLocker, Bluetooth, language packs, all of it. Two hundred plausible looking files is not noise to skip past, it is itself the decoy, built to tire out a reviewer who checks names and calls it a day.
The one move that matters
A file’s default content is only one of its streams, and a directory listing only ever shows that one.
NTFS lets a file carry extra named streams alongside the main one a user opens, an old
feature still present on every modern Windows volume. Explorer never shows them,
dir never shows them by default, and plenty of antivirus engines never
scan them either, which is exactly why this is a documented defense evasion technique
rather than a party trick: a second payload can ride along on a completely ordinary
looking file, invisible to anyone who only checks what the folder claims to hold.
So the actual task was never read every file, it was ask every file what else it is carrying:
PS> Get-ChildItem -Recurse -Force -File | ForEach-Object {
Get-Item -Path $_.FullName -Stream * | Where-Object Stream -ne ':$DATA'
}
Of 209 files, exactly one answered back.
What it was hiding
A scheduled task named Windows-Update, sitting in the Tasks folder among
dozens of real ones with almost identical names, was carrying a second stream named
malware.ps1, 725 bytes, with nothing in the file’s size or its name
hinting it was there. Pulling that stream out on its own is just as direct as finding
it:
PS> Get-Content -Path 'Tasks\Windows-Update' -Stream 'malware.ps1' -Raw
The stream held a short PowerShell one liner, a Write Host call wrapped
around a base64 blob. Decoding it (the underlying text is UTF16, little endian,
Windows’ own native string encoding, which is why the base64 ran twice as long as
the message it held) unwrapped a short note naming the technique by its MITRE
ATT&CK ID, T1564.004, Hide Artifacts: NTFS File Attributes, and closing with
the flag.
FlagRedactedCTF{…}
The teaching point
- A directory listing is a summary, not an inventory. Every file on an NTFS
volume already carries a default stream; nothing stops it carrying others, and
neither Explorer nor a plain
dirwill ever mention them. - Enumerate streams as a routine step, not a hunch. One command across an entire volume answers the question for every file at once; there is no reason to go file by file guessing which one looks suspicious.
- A large pile of ordinary looking files is a signal, not something to skim past. The 208 genuine tasks were not filler, they were the cover, and the one that mattered looked exactly like its neighbors by name.
- Mount evidence read only. A virtual disk is easy to attach and just as easy to damage; nothing here required write access, and none was given.
- Why this is realistic: stashing a payload on a file that already has a legitimate reason to exist, a real scheduled task, survives a casual review, a name based search and most signature scanners that never open a stream at all.
- Decode a recovered payload as data, never run it as code. The stream held a script, and the point was only ever to read what it said. Pulling the base64 out as a string and decoding it gets the same answer as running the script does, without ever executing something pulled out of a piece of evidence.
Tools
Windows’ own disk imaging support to attach the VHDX, PowerShell’s stream aware cmdlets to enumerate and read, and a one line decode for the base64 and UTF16 payload. Nothing here needed a third party forensics suite.
The complete command sequence
Every command actually run, in order, so a person or a script can replay this exactly. The evidence path below is a stand in for the real local path; the flag stays redacted the same way it is above.
# locate the evidence and confirm its type by signature, not extension
find evidence -maxdepth 4
du -ah evidence | sort -h | tail -30
xxd evidence/evidence.vhdx | head -4
# mount it read only
$img = 'C:\CTF\evidence\evidence.vhdx'
$result = Mount-DiskImage -ImagePath $img -Access ReadOnly -PassThru -ErrorAction Stop
$result | Get-DiskImage | Format-List *
$result | Get-Disk | Format-List Number,FriendlyName,OperationalStatus,PartitionStyle
Get-Partition -DiskNumber (($result | Get-Disk).Number) |
Format-Table -Auto PartitionNumber,DriveLetter,Size,Type
# enumerate the mounted volume
Get-ChildItem -Path D:\ -Force
Get-ChildItem -Path D:\ -Recurse -Force -ErrorAction SilentlyContinue
# scan every file for non default NTFS streams
$files = Get-ChildItem -Path D:\ -Recurse -Force -File -ErrorAction SilentlyContinue
$adsHits = @()
foreach ($f in $files) {
$streams = Get-Item -Path $f.FullName -Stream * -ErrorAction SilentlyContinue
foreach ($s in $streams) {
if ($s.Stream -ne ':$DATA') {
$adsHits += [PSCustomObject]@{ File=$f.FullName; Stream=$s.Stream; Length=$s.Length }
}
}
}
$adsHits | Format-Table -Auto
# extract the hidden stream
$out = 'C:\CTF\evidence\extracted_malware.ps1'
Get-Content -Path 'D:\Tasks\Windows-Update' -Stream 'malware.ps1' -Raw |
Set-Content -Path $out -Encoding UTF8
Get-Content $out -Raw
# decode the payload as DATA, never execute an extracted payload as code $raw = Get-Content 'C:\CTF\evidence\extracted_malware.ps1' -Raw $b64 = ($raw -replace ".*FromBase64String\('([^']+)'.*", '$1') [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($b64)) # output: # Congratulations! you have discovered an alternate data stream. This is a Defense # Evasion tactic with MITRE ID T1564.004 Hide Artifacts: NTFS File Attributes. # And here is the flag: RedactedCTF{...}
# dismount
Dismount-DiskImage -ImagePath 'C:\CTF\evidence\evidence.vhdx'
ECHO Club Another Data Trail Write Up
Solved by Digital Orukami · MetaCTF · educational use only.