Windows.Detection.Honeyfiles

This artifact deploys honeyfiles according to the Honeyfiles CSV parameter. It then monitors access to these files using etw. The process tracker must be enabled, we use this to enrich etw events. Honeyfiles created by this artifact are removed at exit.


name: Windows.Detection.Honeyfiles
author: Zane Gittins & Matt Green (@mgreen27).
description: |
    This artifact deploys honeyfiles according to the Honeyfiles CSV parameter. It then monitors access to these files using etw.  The process tracker must be enabled, we use this to enrich etw events. Honeyfiles created by this artifact are removed at exit.
    
type: CLIENT_EVENT

parameters:
  - name: Honeyfiles
    description: | 
      The honeyfiles to generate and monitor.
      
      * TargetPath: Location to create honeyfile.
      * Enabled: Only generate the honeyfile if this is set to 'Y'
      * MagicBytes: The starting magic bytes of the honeyfile.
      * MinSize,MaxSize: The size of the honeyfile will be a random value between MinSize and MaxSize.
    type: csv
    default: |
        TargetPath,Enabled,MagicBytes,MinSize,MaxSize
        "%USERPROFILE%\Documents\KeePass\KeePass.kdbx",Y,03D9A29A67FB4BB5,10249,20899
        "%USERPROFILE%\AppData\Local\KeePass\KeePass.config.xml",Y,3C3F786D6C,512,1024
        "%USERPROFILE%\AppData\Local\LastPass\lastpass.conf",Y,3C3F786D6C,512,1024
        "%USERPROFILE%\AppData\Roaming\LastPass\loginState.xml",Y,3C3F786D6C,512,1024
        "%USERPROFILE%\AppData\Roaming\WinSCP\WinSCP.ini",Y,5B436F6E66696775726174696F6E5D,512,1024
        "%USERPROFILE%\.aws\credentials",Y,5B64656661756C745D,512,2048
        "%USERPROFILE%\.aws\config",Y,5B64656661756C745D,512,2048
        "%USERPROFILE%\.ssh\my_id_rsa",Y,2D2D2D2D2D424547494E205253412050524956415445204B45592D2D2D2D2D,1024,4096
        "%USERPROFILE%\.gcloud\credentials.db",Y,53514c69746520666f726d6174203300,512,2048
        "%USERPROFILE%\.azure\azureProfile.json",Y,7B0D0A,512,2048
  - name: Exclusions
    description: | 
      Process/thread regex pairs to exclude legitimate system activity and reduce false positives.
      Events matching both patterns are suppressed.
    type: csv
    default: |
        ProcessRegex,ThreadRegex
        ^[A-Z]:\\Windows\\explorer\.exe$,^[A-Z]:\\Windows\\System32\\ntdll\.dll$
        ^[A-Z]:\\Windows\\explorer\.exe$,^\\Device\\.*\\Windows\\System32\\ntdll\.dll$
        ^[A-Z]:\\Windows\\explorer\.exe$,^[A-Z]:\\Windows\\System32\\shcore\.dll$
        ^[A-Z]:\\Windows\\explorer\.exe$,^\\Device\\.*\\Windows\\System32\\shcore\.dll$
        .,^[A-Z]:\\Windows\\System32\\SearchProtocolHost\.exe$
        .,^\\Device\\.*\\Windows\\System32\\SearchProtocolHost\.exe$
        ^[A-Z]:\\Windows\\System32\\svchost\.exe$,^\\Device\\.*\\Windows\\System32\\defragsvc\.dll$
        ^[A-Z]:\\Windows\\System32\\svchost\.exe$,^[A-Z]:\\Windows\\System32\\defragsvc\.dll$
        ^[A-Z]:\\Program Files\\CrowdStrike\\CSFalconService\.exe$,.
  - name: HoneyUserRegex
    description: User name regex that will be used to host honeyfiles.
    type: string
    default: "."
export: |
  LET ComputerName <= dict(H={ SELECT Hostname FROM info() }).H[0].Hostname
  
  LET RandomChars(size) = SELECT
      format(format="%02x", args=rand(range=256)) AS HexByte
    FROM range(end=size)
  
  LET check_exist(path) = SELECT
      OSPath,
      Size,
      IsDir,
      if(condition=read_file(filename=OSPath)[-7:] =~ 'VRHoney',
         then=True,
         else=False) AS IsHoneyFile
    FROM stat(filename=path)
  
  LET enumerate_path = SELECT
      regex_replace(source=TargetPath,
                    re='''\%USERPROFILE\%''',
                    replace=Directory) AS TargetPath,
      *,
      check_exist(path=regex_replace(source=TargetPath,
                                     re='''\%USERPROFILE\%''',
                                     replace=Directory))[0] AS Exists,
      MaxSize - rand(range=(MaxSize - MinSize)) - len(
        list=unhex(string=MagicBytes)) - 7 AS _PaddingSize
    FROM Honeyfiles
  
  LET target_users = SELECT Name,
                            Directory,
                            UUID
    FROM Artifact.Windows.Sys.Users()
    WHERE NOT UUID =~ '''^(S-1-5-18|S-1-5-19|S-1-5-20)$'''
     AND Name =~ HoneyUserRegex
  
  LET show_honeyfiles = SELECT TargetPath,
                               Enabled,
                               MagicBytes,
                               MinSize,
                               MaxSize,
                               _PaddingSize,
                               Exists.Size AS Size,
                               Exists.IsHoneyFile AS IsHoneyFile
    FROM foreach(row=target_users, query=enumerate_path)
  
  LET copy_honeyfiles = SELECT
      *, if(condition=Enabled =~ "^(Y|YES)$"
             AND (NOT Size OR IsHoneyFile),
            then=log(message="Creating file %v", dedup=-1, args=TargetPath)
             && copy(dest=TargetPath,
                     create_directories='y',
                     accessor='data',
                     filename=unhex(
                       string=MagicBytes + join(
                         array=RandomChars(size=_PaddingSize).HexByte) +
                         format(format='%x', args='VRHoney'))),
            else="File does not exist") AS CreateHoneyFile
    FROM show_honeyfiles
  
  LET remove_honeyfiles = SELECT
      *, _PaddingSize,
      if(condition=IsHoneyFile,
         then=log(message="Removing %v", args=TargetPath, dedup=-1)
          && rm(filename=TargetPath),
         else="File does not exist") AS RemoveHoneyFile
    FROM show_honeyfiles
  
  LET add_honeyfiles = SELECT
      TargetPath,
      Enabled,
      MagicBytes,
      MinSize,
      MaxSize,
      check_exist(path=TargetPath)[0].Size AS Size,
      check_exist(path=TargetPath)[0].IsHoneyFile AS IsHoneyFile
    FROM copy_honeyfiles
  
  LET ThreadInfo(tPid, tTid) = SELECT filename,
                                      memory_basic_info.RegionSize AS RegionSize
    FROM threads(pid=tPid)
    WHERE tid = tTid
    LIMIT 1
  
  LET get_auth_cache(Image) = authenticode(filename=Image)
  
  LET get_hash_cache(Image) = hash(path=Image, hashselect=["SHA256"]).SHA256
  
  LET WatchFiles <= to_dict(item={
      SELECT TargetPath AS _key,
             IsHoneyFile AS _value
      FROM add_honeyfiles
      WHERE IsHoneyFile
    })
  
  LET Keyword <= 5264
  
  LET CurrentPid <= getpid()
  
  LET ExclusionRules <= SELECT *
    FROM Exclusions
  
  // Function to check if an event should be excluded
  LET ShouldExclude(Process, Thread) = len(
      list=array(_={
      SELECT *
      FROM foreach(row=ExclusionRules)
      WHERE Process =~ ProcessRegex
       AND Thread =~ ThreadRegex
      LIMIT 1
    })) > 0

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows' AND version(plugin="dedup") >= 0

    query: |
      LET _ <= atexit(query={ SELECT * FROM remove_honeyfiles })
      
      LET TargetEvents = SELECT *
        FROM watch_etw(guid='{edd08927-9cc4-4e65-b970-c2560fb5c289}',
                       description="Microsoft-Windows-Kernel-File",
                       any=Keyword)
        WHERE System.ID = 12
         AND System.ProcessID != CurrentPid
      
      LET AuditEvents = SELECT
          timestamp(string=System.TimeStamp) AS Timestamp,
          get(item=WatchFiles, field=EventData.FileName) AS IsHoneyFile,
          *
        FROM TargetEvents
        WHERE IsHoneyFile != NULL
      
      LET Events = SELECT
          Timestamp,
          System.ProcessID AS Pid,
          EventData.FileName AS TargetPath,
          atoi(string=EventData.IssuingThreadId) AS Tid,
          (str(str=System.ProcessID) + EventData.FileName) AS DedupKey
        FROM AuditEvents
      
      LET DedupEvents = SELECT *, process_tracker_get(id=Pid).Data AS ProcInfo,
                               ThreadInfo(tPid=Pid, tTid=Tid)[0] AS ThreadDetails,
                               join(
                                 array=process_tracker_callchain(id=Pid).Data.Name,
                                 sep="->") AS CallChain
        FROM dedup(query=Events, key="DedupKey")
      
      SELECT Timestamp,
             ComputerName,
             Pid,
             Tid,
             TargetPath,
             ProcInfo.Exe AS Image,
             ProcInfo.CommandLine AS Commandline,
             ProcInfo.Username AS User,
             ThreadDetails.filename AS ThreadFilename,
             ThreadDetails.RegionSize AS ThreadRegionSize,
             CallChain,
             cache(period=3600,
                   func=get_auth_cache(Image=ProcInfo.Exe),
                   key=str(str=ProcInfo.Exe),
                   name="auth") AS Authenticode,
             cache(period=3600,
                   func=get_hash_cache(Image=ProcInfo.Exe),
                   key=str(str=ProcInfo.Exe),
                   name="hash") AS Hash
      FROM DedupEvents
      WHERE NOT ShouldExclude(
        Process=if(condition=ProcInfo.Exe, then=ProcInfo.Exe, else=""),
        Thread=if(condition=ThreadDetails.filename,
                  then=ThreadDetails.filename,
                  else=""))