← Back to blog

Alternate Data Streams: hidden $DATA attributes on NTFS

· 2 min read

NTFS lets a file have more than one $DATA attribute. The first one is unnamed and holds the file's main contents. Any additional $DATA attribute is named and acts as a parallel stream attached to the same file. These extra streams are Alternate Data Streams (ADS).

The syntax

You can read and write ADS from PowerShell or cmd:

echo hidden text > notes.txt:secret
type notes.txt:secret

notes.txt looks empty in Explorer and dir — its primary $DATA is zero bytes. But the named secret stream holds your text. From a user's perspective the file behaves normally; the alternate stream rides along.

Where you legitimately see ADS

Windows uses ADS for its own bookkeeping. The most common one is Zone.Identifier, attached to every file downloaded from the internet. It carries a short text block describing the origin URL and security zone. This is what triggers the "this file came from another computer" warning.

Other legitimate uses include:

  • $KSP for keyboard layout files
  • OECustomProperty for Outlook attachments
  • Various thumbnail and search-index streams

Why attackers like ADS

Anything that lives off the main stream tends to be invisible to:

  • Folder views that show only file sizes (sizes are reported for the unnamed stream)
  • Antivirus engines that scan only the default stream
  • Manual review by an analyst who has not asked for streams explicitly

A common pattern is dropping a payload into legit-document.docx:payload.exe and launching it via WMI. The file looks like a Word document and stays that way until you ask the right questions.

Detecting them

dir /R lists every stream on every file in a directory. The output shows the stream size next to the named stream:

1,234,567 legit-document.docx
   45,056 legit-document.docx:payload.exe:$DATA

PowerShell offers the cleaner equivalent:

Get-Item legit-document.docx -Stream *

Why $MFT is the better lens

Individual file enumeration can miss streams that exist on files you did not think to scan. $MFT does not have this problem: every attribute is right there in the record. Walking the MFT and listing each entry's $DATA attributes — both named and unnamed — gives you a complete inventory of every stream on the volume in one pass.

For triage, that is the difference between "I'd better remember to check ADS" and "I already checked every one."

External resources