Hi,
Based on your requirement, I tried to replicate this on my end, and it seems that using the Wazuh command wodle to execute a script for collecting the active window details from endpoints has some permission and session restriction issues. Because of this, it is not possible to reliably monitor the active windows on the endpoint using direct wodle execution.
Wazuh services run under Session 0, while user graphical sessions run under Session 1 or higher. Due to this session isolation, the agent cannot access the active user session window information directly.
However, you can achieve this using another workaround. You can follow the below steps.
I created a PowerShell script and configured Windows Task Scheduler to run the script every one minute. The script runs in the user session and starts automatically when the user logs in. Every one minute, it collects the active window details and writes them into a JSON log file: C:\WazuhLogs\ActiveWindow\active_window.json
Then, using the localfile configuration, Wazuh monitors the log file, forwards the logs to the Wazuh manager for analysis, and triggers alerts using custom rules.
On the Windows endpoint, run the below commands in PowerShell as Administrator.
Run the below command to create the directory and grant permissions:
$OutFolder = "C:\WazuhLogs\ActiveWindow"
New-Item -ItemType Directory -Path $OutFolder -Force
icacls $OutFolder /grant "Users:(OI)(CI)M" /T
Run the below command to create the PowerShell script:
$ScriptPath = "C:\WazuhLogs\ActiveWindow\active_window_service.ps1"
$ScriptContent = @'
$mutexName = "Global\WazuhActiveWindowMonitorMutex"
$createdNew = $false
$mutex = New-Object System.Threading.Mutex($true, $mutexName, [ref]$createdNew)
if (-not $createdNew) { exit }
Add-Type @"
using System;
using System.Text;
using System.Runtime.InteropServices;
public class Win32 {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
}
"@
$OutFolder = "C:\WazuhLogs\ActiveWindow"
$OutFile = "$OutFolder\active_window.json"
if (!(Test-Path $OutFolder)) { New-Item -ItemType Directory -Path $OutFolder -Force }
while ($true) {
try {
$hwnd = [Win32]::GetForegroundWindow()
if ($hwnd -ne [IntPtr]::Zero) {
$buffer = New-Object System.Text.StringBuilder 1024
[void][Win32]::GetWindowText($hwnd, $buffer, $buffer.Capacity)
$pidNumber = 0
[void][Win32]::GetWindowThreadProcessId($hwnd, [ref]$pidNumber)
$proc = Get-Process -Id $pidNumber -ErrorAction SilentlyContinue
if ($null -ne $proc) {
$event = [ordered]@{
timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
sai_log_source = "active_window_monitor"
event = @{ kind = "event"; category = "endpoint"; type = "active_window"; action = "observed" }
endpoint = @{ name = $env:COMPUTERNAME }
process = @{ name = $proc.ProcessName; pid = $proc.Id; path = $proc.Path }
window = @{ title = $buffer.ToString() }
}
$json = $event | ConvertTo-Json -Compress -Depth 6
$fs = [System.IO.File]::Open($OutFile, [System.IO.FileMode]::Append, [System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite)
$writer = New-Object System.IO.StreamWriter($fs, [System.Text.Encoding]::UTF8)
$writer.WriteLine($json)
$writer.Close()
$fs.Close()
}
}
}
catch {}
Start-Sleep -Seconds 60
}
$mutex.ReleaseMutex()
'@
Set-Content -Path $ScriptPath -Value $ScriptContent -Encoding UTF8
This creates the custom PowerShell script active_window_service.ps1, which collects the active window details and writes them in JSON log format.
You can change the execution interval by modifying:
Start-Sleep -Seconds 60Run the below command to create a VBScript launcher that runs the PowerShell script silently in the background without displaying a PowerShell window:
$VbsPath = "C:\WazuhLogs\ActiveWindow\start_silently.vbs"
$VbsContent = 'Set WshShell = CreateObject("WScript.Shell")' + "`r`n" + 'WshShell.Run "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\WazuhLogs\ActiveWindow\active_window_service.ps1", 0, False'
Set-Content -Path $VbsPath -Value $VbsContent -Encoding ASCII
Run the below command to launch the script automatically whenever a user logs in:
$action = New-ScheduledTaskAction -Execute "wscript.exe" -Argument '"C:\WazuhLogs\ActiveWindow\start_silently.vbs"'
$trigger = New-ScheduledTaskTrigger -AtLogOn
$principal = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Users" -RunLevel Highest
Register-ScheduledTask -TaskName "WazuhActiveWindowMonitor" -Action $action -Trigger $trigger -Principal $principal -Force
Start-ScheduledTask -TaskName "WazuhActiveWindowMonitor"
Then, on the Wazuh agent ossec.conf file, add the below configuration block to monitor the generated log file:
<localfile>
<location>C:\WazuhLogs\ActiveWindow\active_window.json</location>
<log_format>json</log_format>
</localfile>
Then restart the Wazuh agent service.
After that, on the Wazuh manager, use a custom rule to trigger alerts. You can use the below sample rule:
<group name="windows,active_window_monitor,">
<rule id="109500" level="0">
<decoded_as>json</decoded_as>
<field name="sai_log_source">^active_window_monitor$</field>
<description>Active window monitor event detected.</description>
</rule>
<rule id="109501" level="3">
<if_sid>109500</if_sid>
<field name="event.type">^active_window$</field>
<field name="event.action">^observed$</field>
<description>Active window observed on $(endpoint.name). Process: $(process.name). Window: $(window.title)</description>
<group>active_window,endpoint_activity,</group>
</rule>
</group>
Then restart the Wazuh manager service:
systemctl restart wazuh-manager
You can further fine-tune the custom rules and script based on your requirements.
For rule creation, you can refer to the Wazuh rule syntax documentation for more details.
I tested this setup on my end, and it is working fine.
