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.

  • 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.

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.

    * 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: CLIENT_EVENT

parameters:
  - name: Honeyfiles
    description: The honeyfiles to generate and monitor.
    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: ProcessExceptionsRegex
    description: Except these processes from detections when they access honeyfiles.
    type: string
    default: "SearchProtocolHost.exe|Explorer.exe"
  - name: HoneyUserRegex
    description: User name regex that will be used to host honeyfiles.
    type: string
    default: "."
export: |
   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

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

    query: |
       LET _ <= atexit(query={ SELECT * FROM remove_honeyfiles })
       
       LET WatchFiles <= to_dict(item={
           SELECT TargetPath AS _key,
                  IsHoneyFile AS _value
           FROM add_honeyfiles
           WHERE IsHoneyFile
         })
       
       LET Keyword <= 5264
       
       LET CurrentPid <= getpid()
       
       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,
           process_tracker_get(id=System.ProcessID).Data AS ProcInfo,
           join(array=process_tracker_callchain(id=System.ProcessID).Data.Name,
                sep="->") AS CallChain,
           (System.ProcessID + EventData.FileName) AS DedupKey
         FROM AuditEvents
         WHERE NOT ProcInfo.Exe =~ ProcessExceptionsRegex
       
       SELECT Timestamp,
              Pid,
              TargetPath,
              ProcInfo,
              CallChain
       FROM dedup(query={
           SELECT *
           FROM delay(query=Events, delay=5)
         },
                  key="DedupKey",
                  timeout=2)

   
  - precondition:
      SELECT OS From info() where OS = 'windows' AND version(plugin="dedup") = NULL
      
    query: |
       LET _ <= atexit(query={ SELECT * FROM remove_honeyfiles })
       
       LET KernelVolumes <= SELECT *,
                                   regex_replace(source=Name,
                                                 replace='',
                                                 re="^\\\\GLOBAL\\?\\?\\\\") AS UserDrive,
                                   regex_replace(
                                     source=SymlinkTarget,
                                     replace='',
                                     re="^\\\\Device\\\\") AS KernelDrive
         FROM winobj()
         WHERE SymlinkTarget =~ "Volume"
          AND Name =~ "[a-z]:$"
       
       LET WatchFiles <= to_dict(
           item={
           SELECT
           KernelPath AS _key,
           IsHoneyFile AS _value
           FROM foreach(
             row=KernelVolumes,
             query={
           SELECT
           *,
           regex_replace(
             source=TargetPath,
             replace=SymlinkTarget,
             re="[A-Z]+\:") AS KernelPath
           FROM add_honeyfiles
           WHERE TargetPath =~ UserDrive
         })
         })
       
       LET Keyword <= 5264
       
       LET CurrentPid <= getpid()
       
       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,
           IsHoneyFile,
           System.ProcessID AS Pid,
           EventData.FileName AS TargetPath,
           process_tracker_get(
             id=System.ProcessID).Data AS ProcInfo,
           join(
             array=process_tracker_callchain(
               id=System.ProcessID).Data.Name,
             sep="->") AS CallChain
         FROM AuditEvents
         WHERE NOT ProcInfo.Exe =~ ProcessExceptionsRegex
       
       SELECT
           *
       FROM delay(
         query=Events,
         delay=5)