Windows.NTFS.ADSHunter

This artifact hunts for Alternate Data Streams on NTFS file systems. Adversaries may use NTFS file attributes for covert storage 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 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)