Xpath Event Log Filtering

So I’ve been working on some stuff lately with Event Log Forwarding and Auditing in general and have come to realize the goodness of using Xpath filtering… you know that “XML” tab you never look at when filtering your event logs Smile

image

There are other great blog articles such as https://blogs.technet.com/b/askds/archive/2011/09/26/advanced-xml-filtering-in-the-windows-event-viewer.aspx that go into detail on how to use this functionality.  Unfortunately they don’t seem to address very well what happens when I need to filter on a more complex field such as say “Accesses” in the following event 4656

image

You would think you could just go to the XML data of the event and find the appropriate data and paste it in my filter, i.e. on the XML view of the event I’m interested in the AccessList data

image

so my xpath filter might look like this…

<QueryList>
           <Query Id="0" Path="Security">
              <Select Path="Security">
                 *[EventData[Data[@Name='AccessList'] and (Data='">%%1538 %%4432 %%4435 %%4436')]]
                </Select>
           </Query>
< /QueryList>

Unfortunately that does not work as there are Carriage Returns and New Lines buried in that data field that are not showing up when you view the XML of the data.  There was a good forum post with an answer to this at https://social.technet.microsoft.com/Forums/windowsserver/en-US/bd136cf0-fb9e-48a1-ae2f-3cd4290ab973/issue-with-custom-build-xml-query-in-event-viewer?forum=winserverpowershell which gives some powershell on how to locate those new lines and tabs however it’s kind of a manual process and tedious so I decided to try to throw that concept into a script with a little more logic to help me more easily find the right text to throw in my Xpath filters.

The following is what I came up with…

 #Script to export properly formatted text from an audit event xml field to use in an xpath event log filter
 #Useful for direct filtering of event logs or in creation of xpath filters for event log forwarding subscriptions
  
 #Define following variables to do the search/conversion of the xml field you need
 $logname = "Security"
 $eventid = "4656"
  
 #Getting the first event in event log that matches variables defined above
 $evt = Get-WinEvent -FilterHashtable @{LogName=$logname;Id=$eventid} -MaxEvents 1
 [xml]$evtx = $evt.ToXml()
 foreach ($dataobj in $evtx.Event.EventData.Data)
     {
     if ($dataobj.Name -EQ "AccessList")
         {
             $dataobjstring = $dataobj."#text".ToString()
             $dataobjtext = $dataobjstring.ToCharArray()
             foreach ($character in $dataobjtext)
                 {
                 $decchar = [Byte[]][Char[]]$character
                 #Finding and replacing Carriage Return/New Line symobls with HTML code
                 if ($decchar -eq 10) {Write-Host "&#xD;&#xA;" -NoNewline}
                 #Finding and replacing Tabs with HTML code
                 elseif ($decchar -eq 9) {Write-Host "&#x09;" -NoNewline}
                 #Write all other characters to the line as is
                 else {Write-Host $character -NoNewline}
                 }
         }
     }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

Basically you put in the logname and the eventid you are interested in or maybe add some more fields possibly and add them to the filterhashtable.  It will get the latest event in your log that matches this (need to run as admin btw to query Security log) and will parse through and find the new lines and tabs and replace them with the appropriate characters.  In my example here my Xpath filter changed from

This (No Worky)

<QueryList>
           <Query Id="0" Path="Security">
              <Select Path="Security">
                 *[EventData[Data[@Name='AccessList'] and (Data='">%%1538 %%4432 %%4435 %%4436')]]
                </Select>
           </Query>
< /QueryList>

To This (Works)

<QueryList>
           <Query Id="0" Path="Security">
              <Select Path="Security">
                  *[EventData[Data[@Name='AccessList'] and (Data='%%1538&#xD;&#xA;&#x09;&#x09;&#x09;&#x09;%%4432&#xD;&#xA;&#x09;&#x09;&#x09;&#x09;%%4435&#xD;&#xA;&#x09;&#x09;&#x09;&#x09;%%4436&#xD;&#xA;&#x09;&#x09;&#x09;&#x09;')]]
                </Select>
           </Query>
< /QueryList>

Hopefully this helps someone else out as well Smile enjoy.

[UPDATE}

Newer better version of script.  Instead finding the last event id 4656 in the security event log instead you can use the EventRecordID of a specific event which you want to parse.  i.e  open the event look at Details Tab and switch to XML View

image

and get the EventRecordID number as highlighted above and plug into the following script.  If the Data Name field you want to parse is not “PrivilegeList” as in this example you would want to change that to the appropriate field you are trying to parse.

$evt = Get-WinEvent -LogName "Security" -FilterXPath "*[System[EventRecordID=142546]]"

#Converting event to XML

[xml]$evtx = $evt.ToXml()

foreach ($dataobj in $evtx.Event.EventData.Data)

{

if ($dataobj.Name -EQ "PrivilegeList")

{

$dataobjstring = $dataobj."#text".ToString()

$dataobjtext = $dataobjstring.ToCharArray()

foreach ($character in $dataobjtext)

{

$decchar = [Byte[]][Char[]]$character

#Finding and replacing Carriage Return/New Line symobls with HTML code

if ($decchar -eq 10) {Write-Host "&#xD;&#xA;" -NoNewline}

#Finding and replacing Tabs with HTML code

elseif ($decchar -eq 9) {Write-Host "&#x09;" -NoNewline}

#Write all other characters to the line as is

else {Write-Host $character -NoNewline}

}

}

}