My next class:
Reverse-Engineering Malware: Advanced Code AnalysisOnline | British Summer TimeJul 28th - Aug 1st 2025

Keylogger Data Stored in an ADS

Published: 2025-07-15. Last Updated: 2025-07-15 07:32:44 UTC
by Xavier Mertens (Version: 1)
0 comment(s)

If many malware samples try to be "filess" (read: they try to reduce their filesystem footprint to the bare minimum), another technique remains interesting: Alternate Data Streams or "ADS"[1]. This NTFS feature allows files to contain multiple data streams, enabling hidden or additional metadata to be stored alongside the main file content without being visible in standard file listings. A common usage of ADS is the "Mark of the Web"[2] that helps to flag files as suspicious or not depending on their origin.

I found a simple Python keylogger that implements an ADS to store the captured keystrokes:

hidden_dir = os.path.join(os.environ['APPDATA'], 'Microsoft\\Windows\\Cache')
os.makedirs(hidden_dir, exist_ok=True)
log_host_file = os.path.join(hidden_dir, "syscache.dat")
log_file = log_host_file + ":logdata"

A second layer of protection is implemented to hide the file using SetFileAttributesW()[3] with the flag 0x02:

try:
    FILE_ATTRIBUTE_HIDDEN = 0x02
    ctypes.windll.kernel32.SetFileAttributesW(log_host_file, FILE_ATTRIBUTE_HIDDEN)
except Exception as e:
    print(f"Failed to hide host file: {e}")

The script is a classic keylogger but it also implements a clipboard monitor to capture all text content:

try:
    win32clipboard.OpenClipboard()
    if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_TEXT):
        data = win32clipboard.GetClipboardData()
        win32clipboard.CloseClipboard()

The script (SHA256:9927159c39a0201e2fcd558c4716fc5cab7e1c6ab69a311f7a21cab3c5667980) has a low VT score (only 3/64) even if not obfuscated. The script does not have an exfiltration mechanism, therefore I presume that another one will take care of this!

How to detect if files have ADS on your file system? This can be achieve with a few lines of PowerShell:

Get-ChildItem -Recurse -Path C:\ | ForEach-Object {
    $streams = Get-Item $_.FullName -Stream * -ErrorAction SilentlyContinue
    if ($streams.Count -gt 1) { 
        $streams 
    }
}

Example:

PS C:\Users\REM> C:\Users\REM\Documents\ads_search.ps1

PSPath        : Microsoft.PowerShell.Core\FileSystem::C:\Users\REM\Desktop\PURCHASE_ORDER.exe::$DATA
PSParentPath  : Microsoft.PowerShell.Core\FileSystem::C:\Users\REM\Desktop
PSChildName   : PURCHASE_ORDER.exe::$DATA
PSDrive       : C
PSProvider    : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
FileName      : C:\Users\REM\Desktop\PURCHASE_ORDER.exe
Stream        : :$DATA
Length        : 1044992

PSPath        : Microsoft.PowerShell.Core\FileSystem::C:\Users\REM\Desktop\PURCHASE_ORDER.exe:Zone.Identifier
PSParentPath  : Microsoft.PowerShell.Core\FileSystem::C:\Users\REM\Desktop
PSChildName   : PURCHASE_ORDER.exe:Zone.Identifier
PSDrive       : C
PSProvider    : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
FileName      : C:\Users\REM\Desktop\PURCHASE_ORDER.exe
Stream        : Zone.Identifier
Length        : 608

[1] https://infosecwriteups.com/ntfs-filesystem-alternate-data-stream-ads-c0e4a2402563
[2] https://en.wikipedia.org/wiki/Mark_of_the_Web
[3] https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileattributesw
 

Xavier Mertens (@xme)
Xameco
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key

0 comment(s)
My next class:
Reverse-Engineering Malware: Advanced Code AnalysisOnline | British Summer TimeJul 28th - Aug 1st 2025

Comments


Diary Archives