Windows.NTFS.ExtendedAttributes

Adversaries may use NTFS file attributes for defence evasion to hide malicious data. This artifact parses NTFS Extended attributes ($EA). The artifact firstly queries the MFT, then enriches NTFS data to check for Extended Attributes. Several filters can be applied such as file search, Extended Attribute size, name or content.

NOTE: By default an EAName exclusion has been applied to filter some common $EA names found on Windows System. Recommended hunt would be by rare name or $EA size. By default we only parse $EA and discard $EA_INFORMATION. $EA_INFORMATION typically is very small and available in NtfsMetadata field of output.


name: Windows.NTFS.ExtendedAttributes
author: "Matt Green - @mgreen27"
description: |
  Adversaries may use NTFS file attributes for defence evasion to hide malicious
  data. This artifact parses NTFS Extended attributes ($EA).
  The artifact firstly queries the MFT, then enriches NTFS data to check for
  Extended Attributes. Several filters can be applied such as file search,
  Extended Attribute size, name or content.

  NOTE:
  By default an EAName exclusion has been applied to filter some common $EA names
  found on Windows System. Recommended hunt would be by rare name or $EA size.
  By default we only parse $EA and discard $EA_INFORMATION. $EA_INFORMATION
  typically is very small and available in NtfsMetadata field of output.


reference:
  - https://attack.mitre.org/techniques/T1564/004/
  - https://posts.specterops.io/host-based-threat-modeling-indicator-design-a9dbbb53d5ea
  - http://inform.pucp.edu.pe/~inf232/Ntfs/ntfs_doc_v0.5/attributes/ea.html

parameters:
  - name: MFTDrive
    default: "C:"
  - name: HostPathRegex
    description: "Regex search over OSPath."
    default: "."
    type: regex
  - name: DateAfter
    type: timestamp
    description: "search for host files with timestamps after this date. YYYY-MM-DDTmm:hh:ssZ"
  - name: DateBefore
    type: timestamp
    description: "search for  host files with timestamps before this date. YYYY-MM-DDTmm:hh:ssZ"
  - name: AllDrives
    type: bool
    description: "Select MFT search on all attached ntfs drives."
  - name: EANameRegex
    description: "$EA Name regex filter to include in results."
    default: .
    type: regex
  - name: EANameExclusion
    description: Regex of ADS name to exclude.
    default: ^(\$KERNEL\.PURGE\.(ESBCACHE|APPXFICACHE)|\$CI\.CATALOGHINT|\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\.CSC\.\w+)$
    type: regex
  - name: EAContentRegex
    description: "$EA content to search for by regex."
    default: .
    type: regex
  - name: SizeMax
    type: int64
    description: "Total $EA attributes in the MFT under this size in bytes."
    default: 100000
  - name: SizeMin
    type: int64
    description: "Total $EA attributes in the MFT over this size in bytes."
    default: 0
  - name: UploadHits
    type: bool
    description: "Upload complete complete attribute data."

sources:
  - query: |
      LET Profile = '''[
         ["EAData", 0, [
            ["Entries", 0, "Array",{
                "type": "EA",
                "count": 99 }],
         ]],
         ["EA", "x=>x.__NextOffset", [
            ["__NextOffset", 0, "uint32"],
            ["__NameLength", 5, "uint8"],
            ["__ValueLength", 6, "uint16"],
            ["Name", 8, String, {
                length: "x=>x.__NameLength" }],
            ["Flags", 4, "uint8"],
            ["ValueLength", 6, "uint16"],
            ["Value", "x=>9 + x.__NameLength", "String",{
                term: "********** NO TERM **********",
                length: "x=>x.__ValueLength",
                max_length: 10000 }],
       ]]
       ]'''

      -- find all MFT entries with an $EA - ignore VSS
      LET mft_entries = SELECT *,
            parse_ntfs(mft=EntryNumber, device=MFTDrive ) as NtfsMetadata
        FROM Artifact.Windows.NTFS.MFT(
           MFTDrive=MFTDrive,
           Accessor='ntfs',
           PathRegex=HostPathRegex,
           DateAfter=DateAfter,
           DateBefore=DateBefore,
           AllDrives=AllDrives)
        WHERE -- NOT OSPath =~ 'HarddiskVolumeShadowCopy' AND
          NtfsMetadata.Attributes.Type =~ '^\\$EA'

      -- enrich results for size filter, dropping metadata field output as this attribute is viewable in Ntfs field.
      LET enriched_results = SELECT OSPath,NtfsMetadata,
            --{ SELECT * FROM NtfsMetadata.Attributes WHERE Type = '$EA_INFORMATION'} as _EA_INFORMATION_Metadata,
            { SELECT * FROM NtfsMetadata.Attributes WHERE Type = '$EA'} as _EA_Metadata
        FROM mft_entries
        WHERE _EA_Metadata.Size > SizeMin AND _EA_Metadata.Size < SizeMax

      -- parse EA attribute
      LET parse_ea = SELECT OSPath, NtfsMetadata, _EA_Metadata,
            parse_binary(accessor="mft",
                filename=NtfsMetadata.Device + _EA_Metadata.Inode,
                profile=Profile, struct="EAData").Entries AS EA
        FROM enriched_results

      -- flattern results and output a row for each EA parsed
      LET flatten_results = SELECT  OSPath, NtfsMetadata, EA, _EA_Metadata
        FROM flatten(
            query={
                SELECT *
                    {
                        SELECT Name,Value,Flags,ValueLength
                        FROM foreach(row=EA)
                    } as EA
                FROM parse_ea
                WHERE EA.Name =~ EANameRegex
                    AND NOT if(condition=EANameExclusion,
                            then= EA.Name =~ EANameExclusion,
                            else= False )
                    AND EA.Value =~ EAContentRegex
            })

      -- upload extended EA data
      LET upload_hits=SELECT OSPath, NtfsMetadata, EA,
            upload(file=NtfsMetadata.Device + _EA_Metadata.Inode,accessor='mft') AS Upload
            --upload(file=Ntfs.Device + _EA_INFORMATION_Metadata.Inode,accessor='mft') AS EA_INFORMATION_Upload
        FROM flatten_results

      -- return rows
      SELECT *
      FROM if(condition=UploadHits,
        then=upload_hits,
        else=flatten_results)