Windows.NTFS.ADSHunter

This artifact hunts for Alternate Data Streams on NTFS file systems. Adversaries may use NTFS file attributes for covert storage in order to evade detection. Alternate Data Streams (ADS) are additional $DATA attributes for an MFT entry in NTFS file systems. In NTFS, the primary $DATA attribute is never named but subsequent $DATA attributes must be named.

Targeting is via mix of path globs and include / exclude regex.

  • TargetGlob is a glob to target for ADS. NOTE *** is recursive. To hit C drive we need to search for C:*
  • AdsName is name in glob format: e.g , Zone.Identifier or Zone..
  • AdsNameExclusion - A regex value, common ADS added to exclusions have been added by default. The artifact also excludes NTFS system files by default.

name: Windows.NTFS.ADSHunter
author: "Matt Green - @mgreen27"
description: |
   This artifact hunts
   for Alternate Data Streams on NTFS file systems. 
   Adversaries may use NTFS file attributes for covert storage in order to evade 
   detection. 
   Alternate Data Streams (ADS) are additional $DATA attributes for an MFT entry in 
   NTFS file systems. In NTFS, the primary $DATA attribute is 
   never named but subsequent $DATA attributes must be named.
   
   Targeting is via mix of path globs and include / exclude regex. 
   
   - TargetGlob is a glob to target for ADS. NOTE **\* is recursive. To hit C drive we need to search for C:\*
   - AdsName is name in glob format: e.g *, Zone.Identifier or Zone.*. 
   - AdsNameExclusion - A regex value, common ADS added to exclusions have been 
   added by default. The artifact also excludes NTFS system files by default.

reference:
  - https://attack.mitre.org/techniques/T1564/004/
  
type: CLIENT

parameters:
 - name: TargetGlob
   description: A Glob to search for target files. **\* is recursive. To hit C drive we need to search for C:\*
   default: C:\{*,**\*}
 - name: AdsNameGlob
   description: AdsName in glob format. e.g *, Zone.Identifier or Zone.*
   default: '*'
 - name: AdsNameExclusion
   description: Regex of ADS name to exclude.
   default: 'SmartScreen|WofCompressedData|encryptable|favicon|AFP_AfpInfo|OECustomProperty|Win32App_1|com\.dropbox|icasource|\{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\}\.(MetaData|SyncRootIdentity)'
   type: regex
 - name: AdsContentRegex
   description: ADS content to search for by regex.
   default: .
   type: regex
 - name: AdsContentExclusion
   description: ADS content to exclude by regex.
   type: regex
 - name: MinSize
   description: Optional - only include alternate data streams above this size in bytes.
   type: int
 - name: MaxSize
   description: Optional - only include alternate data streams below this size in bytes.
   type: int
 - name: UploadDataStream
   description: If selected wil upload non-resident data streams.
   type: bool
   
sources:
 - query: |
      -- Collect ADS entries using glob but exclude ntfs objects that contain ads
      LET ads_entries = SELECT OSPath,
            split(string=Name,sep=':')[1] as AdsName,
            Data.mft as Inode,
            Size,
            OSPath.Dirname + split(string=Name,sep=':')[0] as HostObject,
            dict(Mtime=Mtime,Atime=Atime,Ctime=Ctime,Btime=Btime) as HostTimestampsSI
        FROM glob(globs=TargetGlob + ":" + AdsNameGlob, 
                  accessor="ntfs",
                  nosymlink='Y')
        WHERE
            NOT OSPath =~ '''[a-z]:\\(\$Extend\\|\$Secure|\$UpCase|\$BadClus|\$Bitmap|\$Repair)'''
            AND if(condition=MinSize,
                    then= Size > MinSize,
                    else= True )
            AND if(condition= MaxSize,
                    then= Size < MaxSize,
                    else= True )
            AND NOT if(condition=AdsNameExclusion,
                        then= AdsName =~ AdsNameExclusion,
                        else= False )
      
      -- Extract content and filter
      LET hits = SELECT *,
            read_file(filename=OSPath[0]+Inode, accessor="mft",offset=0,length=1024) as AdsContent -- only upload first 1k of each hit
        FROM ads_entries
        WHERE AdsContent =~ AdsContentRegex
            AND NOT if(condition=AdsContentExclusion,
                    then= AdsContent =~ AdsContentExclusion,
                    else= False )
                    
      -- upload hits
      LET upload_hits = SELECT *, 
            upload(file=OSPath,accessor='ntfs') as Upload
        FROM hits
      
      -- output rows 
      SELECT * FROM if(condition=UploadDataStream,
                        then= upload_hits,
                        else= hits)