Windows.ETW.ScreenshotTaken

This artifact detects screen captures by correlating events from the Microsoft-Windows-Win32k ETW provider which are triggered by common Windows API calls made when taking a screenshot. This can be useful for detecting remote access trojans, infostealers, and data exfiltration. Tested against Sliver, Meterpreter, and Empire. This will also trigger on legitimate tools such as ZoomIt, Greenshot, MsTeams, etc. which can be excluded on a case by case basis via the ProcessExceptionsRegex parameter.


name: Windows.ETW.ScreenshotTaken
author: Zane Gittins
description: |
   This artifact detects screen captures by correlating events from the Microsoft-Windows-Win32k ETW provider which are triggered by common Windows API calls made when taking a screenshot. This can be useful for detecting remote access trojans, infostealers, and data exfiltration. Tested against Sliver, Meterpreter, and Empire. This will also trigger on legitimate tools such as ZoomIt, Greenshot, MsTeams, etc. which can be excluded on a case by case basis via the ProcessExceptionsRegex parameter.

# Can be CLIENT, CLIENT_EVENT, SERVER, SERVER_EVENT or NOTEBOOK
type: CLIENT_EVENT

parameters:
  - name: ProcessExceptionsRegex
    description: Except these processes.
    type: string
    default: "Explorer.exe"
  - name: ScreenshotMaxRows
    description: Maximum number of screenshot events to store in internal fifo queue for correlation.
    type: int
    default: 500
  - name: ScreenshotMaxAge
    description: Maximum age in seconds to retain screenshot events to store in internal fifo queue for correlation.
    type: int
    default: 90
sources:
  - precondition:
      SELECT OS From info() where OS = 'windows' AND version(plugin="dedup") >= 0

    query: |
       // WindowUpdate(1) - Triggered by BitBlt() we delay this by 5 seconds due to a race condition in correlating with screenshot events.
       LET WindowUpdateEvents = SELECT
           *
         FROM delay(
           query={
           SELECT
           *
           FROM dedup(
             query={
             SELECT
             *, System.ProcessID AS FirstPid
             FROM watch_etw(
               guid="{8c416c79-d49b-4f01-a467-e56d3aa8234c}",
               level=4,
               any=5120,
               description="Microsoft-Windows-Win32k")
             WHERE System.ID = 1
               AND EventData.Hwnd = "0x0"
                 AND (EventData.Type = "2147483654" OR EventData.Type = "2147483655")
         },
             timeout=5,
             key="FirstPid")
         },
           delay=5)
       
       // GdiSysMemToken(33) - Created by EtwGdiSysMemToken() in win32kbase.sys
       // PhysicalSurfCreate(52) - This is triggered by CreateCompatibleBitmap. Created by EtwPhysicalSurfCreateEvent() in win32kbase.sys
       LET ScreenshotEvents = SELECT
           *
         FROM dedup(
           query={
           SELECT
           *, System.ProcessID AS Pid
           FROM watch_etw(
             guid="{8c416c79-d49b-4f01-a467-e56d3aa8234c}",
             level=4,
             any=5120,
             description="Microsoft-Windows-Win32k")
           WHERE System.ID = 33 OR System.ID = 52
         },
           timeout=5,
           key="Pid")
       
       LET LastEvents = SELECT *, System.ProcessID AS ScreenshotPid
         FROM fifo(query=ScreenshotEvents,
                   max_rows=ScreenshotMaxRows,
                   max_age=ScreenshotMaxAge)
       
       LET Track = SELECT *
         FROM foreach(row=WindowUpdateEvents,
                      query={
           SELECT *, process_tracker_get(id=System.ProcessID).Data AS ProcInfo,
                  count(items=Pid) AS Count
           FROM LastEvents
           WHERE ScreenshotPid = FirstPid
           GROUP BY Pid
         })
         WHERE Count >= 1
       
       SELECT timestamp(string=System.TimeStamp) AS Timestamp,
              ProcInfo,
              ScreenshotPid AS Pid,
              Count
       FROM Track
       WHERE NOT ProcInfo.Exe =~ ProcessExceptionsRegex