Askhat - stock.adobe.com

Tip

How to filter Security log events for signs of trouble

Certain accounts, such as company executives, will draw unwanted attention from hackers. Learn how to catch these targeted attacks by checking Windows event logs.

When it comes to investigating suspicious activity on Windows, there is no better place to look than in the Windows event logs.

After enabling auditing, you can collect a huge amount of information on your server or even applications, such as Active Directory or Hyper-V. But all this data won't help unless you know how to work with it. Once you learn to build some queries to find some potentially malicious activity, you can then add that query in a script to automatically respond to or report on these events.

How to check the event log for specific scenarios

There are three events in the Windows event log typically worth examining to see if they originated from a user or a breach attempt: locked accounts, creation of new accounts and failed logons.

To build queries for these scenarios, know how to find related event IDs:

Scenario Event Log name Event ID

Account locked out

Security

4740

Account created

Security

4720

Logon failure

Security

4625

How to build a Security log query

It may sound counterintuitive, but one of the easier ways to learn how to build queries for PowerShell scripts is through the Event Viewer UI.

To start, open the Event Viewer and navigate to the Security log. Next, click on the Filter Current Log option on the right.

Filter Current Log option
Open the Event Viewer, find the Security log section, then select Filter Current Log to start building your PowerShell script.

In the Filter Current Log window, you can build a filter on the Filter tab. For example, to look for failed login attempts in the last day, set the Logged dropdown to Last 24 hours and filter for event 4625.

failed logon attempts
Use event 4625 to track logon failures in the Windows event log.

Click OK to close the filter window and verify expected events are showing up. Next, re-open the Filter Current Log window and go to the XML tab to see the XPath query in the Select element.

Event Viewer XPath query
The XML section in the Event Viewer shows the XPath query generated by the search for failed logon attempts.

The Event Viewer generates the following XPath expression:

<Select Path="Security">*[System[(EventlD=4625) and TimeCreated[timediff(@SystemTime &It;= 86400000 </Select>

The timediff expression inside the TimeCreated expression uses milliseconds. The value here equals one day or 86,400,000 milliseconds.

To use this in PowerShell, check the Edit query manually box, highlight the portion inside the Select element and use Ctrl+C to copy to the clipboard.

*[System[(EventlD=4625) and TimeCreated[timediff(@SystemTime &It;= 86400000

Some of the text uses an ampersand to signify what's called an escaped character. XML uses escaped characters to denote the difference between markup and text. Angle brackets, quotes and ampersands use escaped characters as shown in the following chart.

Character Replacement

&gt;

&lt;

"

&quot;

&

&amp;

The following code has been updated with the escaped characters replaced.

$xpath = '*[System[(EventID=4625) and TimeCreated[timediff(@SystemTime) <= 86400000]]]'

How to filter Security log events with XPath and PowerShell

Using PowerShell and its Get-WinEvent cmdlet with the XPath query can check the event logs for signs of trouble. To start, specify the name of the log with LogName and pass the XPath filter to the FilterXPath parameter.

$xpath = '*[System[(EventID=4625) and TimeCreated[timediff(@SystemTime) <= 86400000]]]'
Get-WinEvent -LogName 'Security' -FilterXPath $xpath
Get-WinEvent output
The output from the Get-WinEvent command shows the matches for failed account logons.

How to monitor logged events and automate a response

Armed with the code to retrieve events, the next step is to learn how to monitor and respond to them. There are many ways to react to events. But the most flexible method is to schedule a PowerShell script to check for a type of event and then take action. For instance, you can write a script that checks the event logs on the domain controller for account lockouts and notifies an admin if there are any.

The XPath query checks for events with ID 4740 that happened in the last five minutes. If you use the Event Viewer and then replace the &lt; with a <, then your basic query will look like the following code.

$xpath = '*[System[(EventID=4740) and TimeCreated[timediff(@SystemTime) <= 300000]]]'
Get-WinEvent -LogName 'Security' -FilterXPath $xpath

The 300,000 figure is five minutes in milliseconds.

If the script finds ID 4740, then it can also send an alert by email or to a Microsoft Teams channel with Azure Logic Apps.

In addition to finding specific events, the PowerShell script can build a basic HTML report and send it to the Azure Logic App to post it to Teams.

$xpath = '*[System[(EventID=4740) and TimeCreated[timediff(@SystemTime) <= 300000]]]'
$events = Get-WinEvent -LogName 'Security' -FilterXPath $xpath
$uri = 'logicappurl'
$html = @"
<h1>Accounts locked out in the past 5 minutes:</h1>
<table>
    <tr>
        <th>User</th>
        <th>Date</th>
        <th>Computer</th>
    </tr>
    {users}
<table>
"@
if ($events.Count -gt 0) {
    $replaceText = foreach ($event in $events) {
        @"
    <tr>
        <td>$($event.properties[0].value)</td>
        <td>$($event.TimeCreated)</td>
        <td>$($event.properties[1].value)</td>
    </tr>
"@
    }
    $body = @{
        Channel = 'General'
        Message = $html -replace '\{users\}', $replaceText
    }
    $headers = @{
        'Content-Type' = 'application/json'
    }
    $splat = @{
        Uri     = $uri
        Method  = 'POST'
        Headers = $headers
        Body    = $body | ConvertTo-Json
    }
    Invoke-RestMethod @splat
}
Microsoft Teams message
The PowerShell script sends a message to a Microsoft Teams channel to inform administrators about locked accounts.

If the basic format of the message needs to be tweaked, then it's easy enough to adjust the HTML to your preference.

Posting account lockouts to Microsoft Teams isn't the only thing you could do with events collected by this script. You could also create tickets in your help desk system or, if you'd rather handle them automatically, make the script unlock the accounts as long as certain reasonable conditions are met with the support of your security team.

How to track failed login attempts for executive-level accounts

You can use the same approach to track failed login attempt for high-value accounts, such as C-suite executives, using event ID 4625. For instance, using a similar script to our Microsoft Teams notifications for lockouts, you can add a check for group members to filter for these VIP accounts and adjust the table to get a result tailored for this alert.

account hack attempt
The PowerShell script can focus on higher level employees and send a message if someone is trying to hack into an executive's account.

This script is only slightly more complex, with two additional lines used to query an Active Directory group for its members and then filter only for events that contain those users.

$xpath = '*[System[(EventID=4625) and TimeCreated[timediff(@SystemTime) <= 300000]]]'
$events = Get-WinEvent -LogName 'Security' -FilterXPath $xpath
# Filter for VIP Accounts
$vipAccounts = Get-AdGroupMember 'VIPs'
$events = $events | Where-Object { $vipAccounts.SamAccountName -contains $_.properties[5].value }
$uri = 'logicappurl'
$html = @"
<h1>VIP Accounts with failed passwords in the last 5 minutes:</h1>
 <table>
     <tr>
         <th>Target User</th>
         <th>Date</th>
         <th>Source Computer</th>
         <th>Source IP</th>
     </tr>
     {users}
<table>
 "@
if ($events.Count -gt 0) {
    $replaceText = foreach ($event in $events) {
        @"
    <tr>
        <td>$($event.properties[5].value)</td>
        <td>$($event.TimeCreated)</td>
        <td>$($event.properties[13].value)</td>
        <td>$($event.properties[19].value)</td>
    </tr>
"@
    }
    $body = @{
        Channel = 'General'
        Message = $html -replace '\{users\}', $replaceText
    }
    $headers = @{
        'Content-Type' = 'application/json'
    }
    $splat = @{
        Uri     = $uri
        Method  = 'POST'
        Headers = $headers
        Body    = $body | ConvertTo-Json
    }
    Invoke-RestMethod @splat
}

How to track new account activity

This final example covers tracking accounts created in Active Directory each day. This may not sound suspicious, but it's not uncommon for a threat actor to create a dedicated account for malicious use or a rogue help-desk employee to make accounts and gain access to things they shouldn't.

To track the accounts created each day on each domain controller, take the same approach as the previous examples. Start by creating our XPath query in the Event Viewer, select event ID 4720 and then select the option for events created in the last 24 hours.

$XPath = '*[System[(EventID=4720) and TimeCreated[timediff(@SystemTime) <= 86400000]]]'

Then create a script to monitor this and send notifications using a Logic App:

$XPath = '*[System[(EventID=4720) and TimeCreated[timediff(@SystemTime) <= 86400000]]]'
$events = Get-WinEvent -LogName 'Security' -FilterXPath $xpath
$uri = 'logicappurl'
$html = @"
<h1>Accounts created in the last 24h:</h1>
<table>

    <tr>
        <th>Created User</th>
        <th>Date</th>
        <th>Created by</th>
    </tr>
    {users}
<table>
"@
if ($events.Count -gt 0) {
    $replaceText = foreach ($event in $events) {
        @"
    <tr>
        <td>$($event.properties[0].value)</td>
        <td>$($event.TimeCreated)</td>
        <td>$($event.properties[4].value)</td>
    </tr>
"@
    }
    $body = @{
        Channel = 'General'
        Message = $html -replace '\{users\}', $replaceText
    }
    $headers = @{
        'Content-Type' = 'application/json'
    }
    $splat = @{
        Uri     = $uri
        Method  = 'POST'
        Headers = $headers
        Body    = $body | ConvertTo-Json
    }
    Invoke-RestMethod @splat
}

If your organization adheres to security best practices, then an account created by the domain's administrator account should raise suspicion.

Adjust the script to capture different security events

PowerShell gives you a lot of options to retrieve information from the Windows event logs and to respond when you find concerning results. Using XPath in PowerShell only sounds complicated until you realize you can easily build an XPath query in the Event Viewer. Try either customizing these existing suggestions or developing your own to keep tabs on your environment.

Dig Deeper on Microsoft identity and access management