PowerShell 4103

166 views
Skip to first unread message

никита какдела

unread,
Jan 27, 2026, 2:52:50 PMJan 27
to Wazuh | Mailing List
Hi, I've encountered a problem: when monitoring PowerShell 4103/4104 logs, I can't see the initiator, the person who executed the script. This is completely unusable for incident investigation.

I checked the resulting log and saw that it contains a contextInfo field that contains the user I was looking for.
My question is, how can I extract this field and store it in a separate field, such as src.user ? Considering the log itself is processed by the windows_eventchannel decoder?

{ "_index": "wazuh-alerts-4.x-2026.01.27", "_id": "imCdAJwBx4tf4mdO_uDv", "_version": 1, "_score": null, "_source": { "input": { "type": "log" }, "agent": { "ip": "10.10.10.10", "name": "admin", "id": "185" }, "manager": { "name": "wazuh" }, "data": { "win": { "eventdata": { "contextInfo": " Важность = Informational Имя узла = ConsoleHost Версия узла = 5.1.22621.4391 ИД узла = 87b5c1f0-1b5e-4304-ada2-f7a4add95a2f Ведущее приложение = C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe Версия модуля = 5.1.22621.4391 ИД пространства выполнения = e196a767-4c1d-4431-b118-f9c36fb22be3 ИД конвейера = 16 Имя команды = Set-StrictMode Тип команды = Cmdlet Имя сценария = C:\\\\Users\\\\nshelamov\\\\Documents\\\\WindowsPowerShell\\\\Modules\\\\PSReadLine\\\\2.3.6\\\\PSReadLine.psm1 Путь команды = Порядковый номер = 40 Пользователь = OVP\\\\nshelamov Подключенный пользователь = ИД оболочки = Microsoft.PowerShell", "payload": "CommandInvocation(Set-StrictMode): \\\"Set-StrictMode\\\" ParameterBinding(Set-StrictMode): имя=\\\"Off\\\"; значение=\\\"True\\\"" }, "system": { "eventID": "4103", "keywords": "0x0", "providerGuid": "{a0c1853b-5c40-4b15-8766-3cf1c58f985a}", "level": "4", "channel": "Microsoft-Windows-PowerShell/Operational", "opcode": "20", "message": "\"CommandInvocation(Set-StrictMode): \"Set-StrictMode\"\r\nParameterBinding(Set-StrictMode): имя=\"Off\"; значение=\"True\"\r\n\r\n\r\nКонтекст:\r\n Важность = Informational\r\n Имя узла = ConsoleHost\r\n Версия узла = 5.1.22621.4391\r\n ИД узла = 87b5c1f0-1b5e-4304-ada2-f7a4add95a2f\r\n Ведущее приложение = C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\r\n Версия модуля = 5.1.22621.4391\r\n ИД пространства выполнения = e196a767-4c1d-4431-b118-f9c36fb22be3\r\n ИД конвейера = 16\r\n Имя команды = Set-StrictMode\r\n Тип команды = Cmdlet\r\n Имя сценария = C:\\Users\\nshelamov\\Documents\\WindowsPowerShell\\Modules\\PSReadLine\\2.3.6\\PSReadLine.psm1\r\n Путь команды = \r\n Порядковый номер = 40\r\n Пользователь = OVP\\nshelamov\r\n Подключенный пользователь = \r\n ИД оболочки = Microsoft.PowerShell\r\n\r\n\r\nДанные пользователя:\r\n\r\n\"", "version": "1", "systemTime": "2026-01-27T18:01:18.0292862Z", "eventRecordID": "1110719", "threadID": "22936", "computer": "admin.ovp.ru", "task": "106", "processID": "26020", "severityValue": "INFORMATION", "providerName": "Microsoft-Windows-PowerShell" } } }, "rule": { "firedtimes": 81, "mail": true, "level": 5, "description": "Group of Windows rules for the Powershell/Operational channel.", "groups": [ "InfoSec", " powershell" ], "id": "100058" }, "location": "EventChannel", "decoder": { "name": "windows_eventchannel" }, "id": "1769536878.2799110274", "full_log": "{\"win\":{\"system\":{\"providerName\":\"Microsoft-Windows-PowerShell\",\"providerGuid\":\"{a0c1853b-5c40-4b15-8766-3cf1c58f985a}\",\"eventID\":\"4103\",\"version\":\"1\",\"level\":\"4\",\"task\":\"106\",\"opcode\":\"20\",\"keywords\":\"0x0\",\"systemTime\":\"2026-01-27T18:01:18.0292862Z\",\"eventRecordID\":\"1110719\",\"processID\":\"26020\",\"threadID\":\"22936\",\"channel\":\"Microsoft-Windows-PowerShell/Operational\",\"computer\":\"admin.ovp.ru\",\"severityValue\":\"INFORMATION\",\"message\":\"\\\"CommandInvocation(Set-StrictMode): \\\"Set-StrictMode\\\"\\r\\nParameterBinding(Set-StrictMode): имя=\\\"Off\\\"; значение=\\\"True\\\"\\r\\n\\r\\n\\r\\nКонтекст:\\r\\n Важность = Informational\\r\\n Имя узла = ConsoleHost\\r\\n Версия узла = 5.1.22621.4391\\r\\n ИД узла = 87b5c1f0-1b5e-4304-ada2-f7a4add95a2f\\r\\n Ведущее приложение = C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\\r\\n Версия модуля = 5.1.22621.4391\\r\\n ИД пространства выполнения = e196a767-4c1d-4431-b118-f9c36fb22be3\\r\\n ИД конвейера = 16\\r\\n Имя команды = Set-StrictMode\\r\\n Тип команды = Cmdlet\\r\\n Имя сценария = C:\\\\Users\\\\nshelamov\\\\Documents\\\\WindowsPowerShell\\\\Modules\\\\PSReadLine\\\\2.3.6\\\\PSReadLine.psm1\\r\\n Путь команды = \\r\\n Порядковый номер = 40\\r\\n Пользователь = OVP\\\\nshelamov\\r\\n Подключенный пользователь = \\r\\n ИД оболочки = Microsoft.PowerShell\\r\\n\\r\\n\\r\\nДанные пользователя:\\r\\n\\r\\n\\\"\"},\"eventdata\":{\"contextInfo\":\" Важность = Informational Имя узла = ConsoleHost Версия узла = 5.1.22621.4391 ИД узла = 87b5c1f0-1b5e-4304-ada2-f7a4add95a2f Ведущее приложение = C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\WindowsPowerShell\\\\\\\\v1.0\\\\\\\\powershell.exe Версия модуля = 5.1.22621.4391 ИД пространства выполнения = e196a767-4c1d-4431-b118-f9c36fb22be3 ИД конвейера = 16 Имя команды = Set-StrictMode Тип команды = Cmdlet Имя сценария = C:\\\\\\\\Users\\\\\\\\nshelamov\\\\\\\\Documents\\\\\\\\WindowsPowerShell\\\\\\\\Modules\\\\\\\\PSReadLine\\\\\\\\2.3.6\\\\\\\\PSReadLine.psm1 Путь команды = Порядковый номер = 40 Пользователь = OVP\\\\\\\\nshelamov Подключенный пользователь = ИД оболочки = Microsoft.PowerShell\",\"payload\":\"CommandInvocation(Set-StrictMode): \\\\\\\"Set-StrictMode\\\\\\\" ParameterBinding(Set-StrictMode): имя=\\\\\\\"Off\\\\\\\"; значение=\\\\\\\"True\\\\\\\"\"}}}", "timestamp": "2026-01-27T18:01:18.784+0000" }, "fields": { "timestamp": [ "2026-01-27T18:01:18.784Z" ] }, "highlight": { "agent.name": [ "@opensearch-dashboards-highlighted-field@admin@/opensearch-dashboards-highlighted-field@" ], "data.win.system.eventID": [ "@opensearch-dashboards-highlighted-field@4103@/opensearch-dashboards-highlighted-field@" ] }, "sort": [ 1769536878784 ] }

никита какдела

unread,
Jan 28, 2026, 4:05:37 AMJan 28
to Wazuh | Mailing List
can i do it through pipeline? i mean add new proccessor in
how do i edit pipeline correct? 
{
  "grok": {
    "field": "data.win.eventdata.contextInfo",
    "patterns": [
      "Пользователь = %{DATA:powershell.user}(?:\\r?\\n|$)"
    ],
    "ignore_missing": true,
    "ignore_failure": true
  }
}


вторник, 27 января 2026 г. в 22:52:50 UTC+3, никита какдела:

Md. Nazmur Sakib

unread,
Jan 28, 2026, 4:57:54 AMJan 28
to Wazuh | Mailing List
Hi никита какдела,

You can forward the logs using a command monitoring feature of Wazuh.
https://documentation.wazuh.com/current/user-manual/capabilities/command-monitoring/index.html

Next, you can forward the logs to Wazuh as JSON and use json plugin decoder to parse the fields.

Mixing of plugin decoders with regular expressions


I am currently testing it on my Lab and will get back to you with the test results soon.

никита какдела

unread,
Jan 28, 2026, 5:09:34 AMJan 28
to Wazuh | Mailing List
I forward PowerShell/Operational log through windows_eventchannel. I have no idea how should i send it through json, so 
  <localfile>
    <location>Microsoft-Windows-PowerShell/Operational</location>
    <log_format>eventchannel</log_format>
  </localfile>

  <localfile>
    <location>Windows PowerShell</location>
    <log_format>eventchannel</log_format>
  </localfile>

Can i upgrade my pipeline.json with a
  {
"grok": {
"field": "data.win.system.message",
"patterns": [
"(?s).*\\bUserId=%{DATA:powershell.user}\\s+.*\\bHostApplication=%{DATA:powershell.host_application}\\s+.*",
"(?s).*Пользователь = %{DATA:powershell.user}\\s+.*Ведущее приложение = %{DATA:powershell.host_application}\\s+.*"

],
"ignore_missing": true,
"ignore_failure": true
}
}

If i can, please tell how should i do it correctly so as not to disrupt the operation of wazuh  

среда, 28 января 2026 г. в 12:57:54 UTC+3, Md. Nazmur Sakib:

Md. Nazmur Sakib

unread,
Jan 28, 2026, 7:33:00 AMJan 28
to Wazuh | Mailing List

I am not sure if this is possible by changing the filebeat pipeline. But you can follow this workaround to achieve something similar.


I have used command monitoring to collect Windows PowerShell event channel logs.


This is the script I am using for command monitoring.

$log = 'Microsoft-Windows-PowerShell/Operational'

$stateFile = "$env:TEMP\psh_last_time.txt"


# Determine start time

$startTime = if (Test-Path $stateFile) {

    Get-Content $stateFile

} else {

    (Get-Date).AddDays(-1).ToString('o')

}


# Get events

$events = Get-WinEvent -FilterHashtable @{

    LogName   = $log

    StartTime = [datetime]$startTime

} -ErrorAction SilentlyContinue


# Output each event as a separate JSON object

if ($events) {

    foreach ($event in $events) {

        $event | ConvertTo-Json -Depth 5 -Compress

    }

    # Update the state file

    (Get-Date).ToString('o') | Out-File $stateFile

}




Next, configure the Windows agent to run this script in the command monitoring. Add this at the end of the ossec.conf file of your agent.

<ossec_config>

  <wodle name="command">

    <disabled>no</disabled>

    <tag>PowerShell_Operational_logs</tag>

    <command>powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Users\User\Documents\psh-new-events.ps1"</command>

    <interval>1m</interval>

    <run_on_start>yes</run_on_start>

  </wodle>

</ossec_config>

Ref: https://documentation.wazuh.com/current/user-manual/capabilities/command-monitoring/index.html


Now custom decoders to decode the user field.
In my log, I had this nested field data.Properties.Value

"Properties":[{"Value":"        Severity = Informational\r\n        Host Name = ConsoleHost\r\n        Host Version = 5.1.26100.7462\r\n        Host ID = 29900246-2e11-4d19-9660-82764f433198\r\n        Host Application = C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\r\n        Engine Version = 5.1.26100.7462\r\n        Runspace ID = 27e0bed8-293c-4321-a82b-a9725df7f51d\r\n        Pipeline ID = 251\r\n        Command Name = Add-Type\r\n        Command Type = Cmdlet\r\n        Script Name = \r\n        Command Path = \r\n        Sequence Number = 48\r\n        User = SAKIB-HOME\\User\r\n        Connected User = \r\n        Shell ID = Microsoft.PowerShell\r\n"}


I have decoded this value as a single field User = SAKIB-HOME\\\User

Add this decoder to your custom decoder file.

<decoder name="json">

  <prematch>^{\s*"</prematch>

</decoder>


<decoder name="ps_child">

  <parent>json</parent>

    <regex>User = (\.+)\\r\.+Connected User = \.+</regex>

    <order>user_name</order>

</decoder>


<decoder name="ps_child">

  <parent>json</parent>

  <plugin_decoder>JSON_Decoder</plugin_decoder>

</decoder>




Now add this custom rule for tigger alert for event ID 4103.

<group name="windows,powershell,">

  <rule id="100301" level="5">

    <decoded_as>json</decoded_as>

    <field name="LogName">Microsoft-Windows-PowerShell/Operational</field>

    <match>4103</match>

    <description>Microsoft-Windows-PowerShell/Operational log: Event ID - 4103</description>

  </rule>

</group>




After that, reload the rule engine or restart the wazuh-manager to load the new decoders and rules.

Now in ruleset test it was working fine and triggering alert, but I was having filebeat mapping error for some fields, and I processed those fields from filebeat before forwarding it indexer.

For that,

Backup the existing pipeline file:

cp /usr/share/filebeat/module/wazuh/alerts/ingest/pipeline.json /tmp/pipeline.json

Open the pipeline file:

vi /usr/share/filebeat/module/wazuh/alerts/ingest/pipeline.json

Locate the processors section, and insert the following snippet after the "date_index_name" section and before the first remove block.




  {
"script": {

    "if": "ctx?.location == 'command_PowerShell_Operational_logs' && ctx?.data?.UserId != null && ctx.data.UserId instanceof Map",

    "lang": "painless",

    "source": "if (ctx.data.UserId.containsKey('AccountDomainSid')) { ctx.data.UserId.remove('AccountDomainSid'); }"

  }

},

{

  "script": {

    "if": "ctx?.location == 'command_PowerShell_Operational_logs' && ctx?.data?.Properties != null && ctx.data.Properties instanceof List",

    "lang": "painless",

    "source": "for (def p : ctx.data.Properties) { if (p != null && p instanceof Map && p.containsKey('Value') && p.Value != null) { p.Value = p.Value.toString(); } }"

  }

},



Check the screenshot for reference.
2026-01-28 17 52 47.png


Save the configuration and apply the pipeline:

filebeat setup --pipelines

systemctl restart filebeat

After that, try to reproduce event 1403 and check if you are able to see the alert with the desired fields in your dashboards.

Check the screenshot for reference.

2026-01-28 18 31 09.png

You can do furter modification based on your needs. Let me know if this works for you.

никита какдела

unread,
Jan 29, 2026, 7:04:17 AMJan 29
to Wazuh | Mailing List
Do you have any user info in 4104 event id? It is important

среда, 28 января 2026 г. в 15:33:00 UTC+3, Md. Nazmur Sakib:

Md. Nazmur Sakib

unread,
Feb 5, 2026, 6:57:13 AMFeb 5
to Wazuh | Mailing List
I was not able to find the user information from the event ID 4104.
I am sharing a screenhsot form my event channel.

2026-02-05 14 07 34.png

Please recheck from your endpoint's event channel, if you can see the user infromation.

никита какдела

unread,
Feb 5, 2026, 8:18:49 AMFeb 5
to Wazuh | Mailing List
We both have Security UserID, which we can later convert into username, but this field does not reach wazuh.

четверг, 5 февраля 2026 г. в 14:57:13 UTC+3, Md. Nazmur Sakib:

Md. Nazmur Sakib

unread,
Feb 6, 2026, 4:17:43 AMFeb 6
to Wazuh | Mailing List

The Security UserID value is present in the alerts, same as the event viewer, check the data.UserId.Value filed I am sharing the screenshot for your reference. 2026-02-06 15 10 29.png
2026-02-06 15 12 46.png

I am using these rules.

<group name="windows,powershell,">

  <rule id="100301" level="5">

    <decoded_as>json</decoded_as>

    <field name="LogName">Microsoft-Windows-PowerShell/Operational</field>

    <regex>"Id":4103</regex>

    <description>Microsoft-Windows-PowerShell/Operational log: Event ID - 4103</description>

  </rule>


  <rule id="100302" level="5">

    <decoded_as>json</decoded_as>

    <field name="LogName">Microsoft-Windows-PowerShell/Operational</field>

    <regex>"Id":4104</regex>

    <description>Microsoft-Windows-PowerShell/Operational log: Event ID - 4104</description>

  </rule>


</group>

никита какдела

unread,
Feb 6, 2026, 10:02:51 AMFeb 6
to Wazuh | Mailing List
But why can't this field be passed through the built-in construct:


  <localfile>
    <location>Microsoft-Windows-PowerShell/Operational</location>
    <log_format>eventchannel</log_format>
  </localfile>


Do I really need to make a separate powershell script to download the log in JSON format?

пятница, 6 февраля 2026 г. в 12:17:43 UTC+3, Md. Nazmur Sakib:

Md. Nazmur Sakib

unread,
Feb 9, 2026, 1:43:17 AMFeb 9
to Wazuh | Mailing List

After checking this behavior, I found out that the lines below are intended to extract the userID field for the event channel logs with eventchannel decoder, and the field is incorrectly referenced as a label value and, as it is an attribute, is never extracted.

} else if (!strcmp(child_attr[p]->element, "Channel")) { 

     cJSON_AddStringToObject(json_system_in, "channel", child_attr[p]->content); 

     if(child_attr[p]->attributes && child_attr[p]->values && !strcmp(child_attr[p]->values[0], "UserID")){ 

         cJSON_AddStringToObject(json_system_in, "userID", child_attr[p]->values[0]); 

     } 

 } else if (!strcmp(child_attr[p]->element, "Security")) { 

     if(child_attr[p]->attributes && child_attr[p]->values && !strcmp(child_attr[p]->values[0], "UserID")){ 

         cJSON_AddStringToObject(json_system_in, "securityUserID", child_attr[p]->values[0]); 

     }


https://github.com/wazuh/wazuh/blob/4.14.3/src/analysisd/decoders/winevtchannel.c#L199



Referencing the userID as an attribute is enough to extract the field correctly:

EX:

       } else if (!strcmp(child_attr[p]->element, "Channel")) {

            cJSON_AddStringToObject(json_system_in, "channel", child_attr[p]->content);

            if(child_attr[p]->attributes && child_attr[p]->values && !strcmp(child_attr[p]->attributes[0], "UserID")){

                cJSON_AddStringToObject(json_system_in, "userID", child_attr[p]->values[0]);

            }

        } else if (!strcmp(child_attr[p]->element, "Security")) {

            if(child_attr[p]->attributes && child_attr[p]->values && !strcmp(child_attr[p]->attributes[0], "UserID")){

                cJSON_AddStringToObject(json_system_in, "securityUserID", child_attr[p]->values[0]);

            }

This is a bug, and we are aware of it. We are already working on our newer version 5.0 with a new rule engine, and these issues will be resolved on the new rule engine.

For now, you have two options: you can either install Wazuh Manager from the source code and modify these and recomplaine package using the modified source code.

https://documentation.wazuh.com/current/deployment-options/wazuh-from-sources/wazuh-server/index.html

Or you can follow the workaround with the script I shared before.

Reply all
Reply to author
Forward
0 new messages