Tracking and auditing changes to passwords in an Active Directory (AD) domain are crucial to maintaining a secure environment and heading off bad actors early. Thankfully, AD offers the information necessary to track these changes, despite being difficult to parse and understand at times. LAPS is a great example of this.
In this article, you’re going to learn how to enable Active Directory auditing of passwords, how to filter events in the Event Viewer and use PowerShell to more easily audit the results with a script.
This tutorial is sponsored by Specops and their useful tool Password Auditor.
Prerequisites
If you’d like to follow along with this tutorial, please be sure you have the following:
- An Active Directory environment with at least one domain-joined workstation. The tutorial is at a domain functional level of Windows Server 2016, however, Advanced Audit Policies were introduced in Windows Server 2008 via a logon script and via a GPO in Windows Server 2008 R2.
- Remote Server Administration Tools (RSAT) for Active Directory installed on your domain-joined workstation
- PowerShell 7.x
Enable Group Policy for Active Directory Auditing
The necessary auditing information you need to audit AD password changes is stored on domain controllers (DC), but the domain controller in the Primary Domain Controller (PDC) emulator role will ultimately process the request. But, by default, the necessary auditing isn’t enabled on DCs. Let’s change that.
To find the DC holding the PDCe role, use the PowerShell command,
(Get-ADDomain).PDCEmulator
.
To enable password change auditing, create a new group policy object (GPO). This GPO will be created and linked to the entire domain.
You could change the Default Domain Policy but Microsoft recommends against this. If you ever need to make a change across DCs in your environment, always create a separate GPO.
Though you are creating and linking a GPO to the entire domain, the relevant audit events are only available on DCs. But, it is beneficial to have those same logs on domain-joined clients because it may be useful in the event that a local non-AD account password is changed. If you’re only auditing Active Directory accounts, you can instead link the GPO to the Domain Controllers organizational unit (OU).
On your domain-joined workstation, create a GPO that forces DCs to begin auditing password changes:
- Open the Group Policy Management snap-in by going to Start → Run and typing gpmc.msc.
2. Click on Create a GPO in this domain, and Link it here… and give the policy a name. This tutorial’s example will use the name Active Directory Password Auditing.
3. Once the policy has been created, right-click it, and choose Edit to open the Group Policy Management Editor.
4. Navigate to Computer Configuration → Policies → Windows Settings → Security Settings → Local Policies → Audit Policy → Audit account management.
5. Next, double-click on the Audit Account Management policy setting and check the checkbox Define these policy settings while ensuring both the Success and Failure checkboxes are checked. By doing so, successful and unsuccessful password attempts will be logged.
6. Click OK to close and exit the editor.
7. Open up a Remote Desktop (RDP) client and connect to the domain controller running the PDC emulator (PDCe) AD role.
All DCs process password changes but all DCs replicate password changes to the DC holding the PDC emulator (PDCe) role so technically, you only need to look at this DC’s events.
8. On the PDCe DC, open a command prompt or PowerShell console and run gpupdate to force a group policy update.
If you have PowerShell Remoting enabled on your DCs, you can also invoke a GPO update that way also.
Once the configuration changes have been made and group policy updated, you now have auditing events turned on and logging for account management. Read on to discover how to interpret these events.
Deciphering Account Management Event Logging
The category of audit events password changes fall under is called Account Management events. These events record information such as password change events and user account lockouts. Account Management audit events are logged as Windows events in the Security event log of a machine that has the auditing enabled.
On your domain-joined machine:
- Open up Windows Event Viewer by running eventvwr.msc or using the Start menu.
2. Right-click on Event Viewer (Local) and select Connect to Another Computer….
3. Provide the name of the DC running the PDCe role in the Another computer: box and click OK to connect Event Viewer to the DC’s event source.
4. Expand the Windows Logs item and click on Security. This will bring you to the Security log as shown below.
Inside of the security log, you’ll find various events with a source of Microsoft Windows security auditing and User Account Management task categories as shown in the filtered view below.
Each Windows event has a unique ID that represents the type of event. Though there are several event IDs that the Microsoft Windows security auditing source contains, the primary event IDs that you should be interested in for password changes (and user lockouts) are:
- 4723 – An attempt was made to change an account’s password.
- 4724 – An attempt was made to reset an account password.
- 4740 – A user account was locked out.
- 4767 – A user account was unlocked.
You’ll see a lot of events in the Security log so you’ll need to create an apply some filters to narrow down only password changes.
Filtering on Password Change Events
Within the Event Viewer, you can create a filter. A filter is a way to limit the number of events that show up and is mandatory when combing through the Windows Security event log.
To create this filter in the Event Viewer:
- Right-click on the Security log and click on Filter Current Log… as shown below.
2. In the Filter Current Log dialog box, create a filter to only find password change events using the following criteria and click on OK.
- Event Sources: Microsoft Windows security auditing.
- Event IDs: 4723,4724,4740,4767
- Task Category: User Account Management.
When you’re complete, your Filter Current Log screen should look like below.
By filtering the Event Viewer, on the domain controller, to just the important event IDs a targeted list of events that have occurred specific to password and password changes are shown below.
Voila! You now have all of the password change (and user lockout) events that have occurred in your domain since you’ve linked the GPO created earlier!
Find Audit Events with PowerShell
Even though you can use the Windows Event Viewer connected to the DC’s Security event log sometimes you need a faster approach. Perhaps you have some automation built in the background or would like to automatically monitor this event log. In this case, you should use PowerShell.
Using PowerShell allows you to perform the exact same function all within a single script. With a script, you won’t have to connect to DCs, create filters manually and manually parse through events.
To find important auditing events with PowerShell, use the Get-WinEvent
cmdlet. This cmdlet queries a local or remote event log and returns all events. It also has support for filters too just as the Event Viewer does. If you’ve already built a filter with Event Viewer, you can even, in fact, share that same filter with PowerShell!
Extracting the Event Viewer Filter
Since PowerShell can use the same filter as the Event Viewer, let’s save some time and extract that filter and use it with PowerShell’s Get-WinEvent
cmdlet.
While still ensuring you’re still connected to the DC with Event Viewer:
- Go back to the Filter Current Log screen. You should still have all of the filter criteria set.
- Click on the XML tab at the top. This tab contains the raw XML that Event Viewer is passing to the event log to display only certain events. PowerShell also can use this XML.
- Select and copy all of the text been <Select Path=”Security”> and </Select> as shown below. This text is an XPath value that the
Get-WinEvent
cmdlet can use.
4. Open up a PowerShell console and paste in the following code snippet. You can see the FilterXPath
parameter value is the exact same text extracted from the Event Viewer filter above.
Get-WinEvent -ComputereName <YOUR DC> -LogName 'Security' -FilterXPath "*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and Task = 13824 and (EventID=4723 or EventID=4724 or EventID=4740 or EventID=4767)]]"
When you run Get-WinEvent
, you should see all of the same events you saw in the Event Viewer earlier as shown in the following screenshot.
You’ve now found all password change and user lockout events with PowerShell!
Buiding a Password Change Auditing Tool
You’ve seen how to use PowerShell’s Get-WinEvent
cmdlet to audit AD events. If you plan to do this often, it’s always a good idea to use a reusable tool to use in other scripts or automation routines.
In this section, let’s build a PowerShell function called Get-ADPasswordEvent
. This function will explore many techniques that can help you retrieve just the information that you want without all the additional properties that you get with Get-WinEvent
.
To not bore you with all of the details, you can see a complete function already built for you below. This function performs a few different tasks:
- Creates the XPath filter
- Runs
Get-WinEvent
using the filter retrieving the specific AD account management password events - Returns custom output relevant to the task at hand.
You can find more granular details in the code comments.
Function Get-ADPasswordEvent {
[CmdletBinding()]
Param(
[Parameter(Position=0)]
[System.Collections.Generic.List[Int]]$EventID = @(4723,4724,4740,4767),
[Parameter(Position=1)]
[Int]$Hours,
[Parameter(Position=2)]
[ValidateSet("Success","Failure")]
[System.Collections.Generic.List[String]]$EventType = @("Success","Failure"),
[Parameter(Position=3)]
[string]$ComputerName
)
Process {
# The event type filter values are from the Audit Success and Audit Failure in the Keywords section of the Event Viewer filter.
If ($EventType -Contains "Success" -And $EventType -Contains "Failure") {
$EventTypeFilter = " and (band(Keywords,13510798882111488))"
} ElseIF ($EventType -Contains "Success") {
$EventTypeFilter = " and (band(Keywords,9007199254740992))"
} Else {
$EventTypeFilter = " and (band(Keywords,4503599627370496))"
}
# We construct a filter to pass to -FilterXPath. Hours should be returned in milliseconds and we use Join-String to properly format the EventID combinations.
$Filter = "*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and Task = 13824{0}{1}{2}]]" -F
(($Hours) ? " and TimeCreated[timediff(@SystemTime) <= $((New-TimeSpan -Hours $Hours).TotalMilliseconds)]" : $Null),
(($EventId) ? ($EventId | Join-String -OutputPrefix " and (" -FormatString 'EventID={0}' -Separator ' or ' -OutputSuffix ")") : $Null),
$EventTypeFilter
Write-Verbose $Filter
$Events = Get-WinEvent -ComputerName $ComputerName -LogName 'Security' -FilterXPath $Filter
# For the Password Change and Reset ID's we format the Details to be more readable and show what is going on regarding the target (account being changed) and the subject (who did the changing).
$Events | ForEach-Object {
$Event = $_
Switch ($Event.ID) {
4723 {
$Description = "Account Password Change Attempt"
$Details = [PSCustomObject]@{
"TargetAccount" = ("{0}\{1}" -F ($Event.Properties)[1].Value, ($Event.Properties)[0].Value)
"TargetSID" = ($Event.Properties)[2].Value
"SubjectAccount" = ("{0}\{1}" -F ($Event.Properties)[5].Value, ($Event.Properties)[4].Value)
"SubjectSID" = ($Event.Properties)[3].Value
}
Break
}
4724 {
$Description = "Account Password Reset Attempt"
$Details = [PSCustomObject]@{
"TargetAccount" = ("{0}\{1}" -F ($Event.Properties)[1].Value, ($Event.Properties)[0].Value)
"TargetSID" = ($Event.Properties)[2].Value
"SubjectAccount" = ("{0}\{1}" -F ($Event.Properties)[5].Value, ($Event.Properties)[4].Value)
"SubjectSID" = ($Event.Properties)[3].Value
}
Break
}
4740 {
$Description = "Account Locked Out"
$Details = $Event.Message
Break
}
4767 {
$Description = "Account Unlocked"
$Details = $Event.Message
Break
}
Default {
$Description = $Null
$Details = $Event.Message
Break
}
}
# Finally let's output our custom object that shows the event information in a more readable format.
[PSCustomObject]@{
"TimeCreated" = (Get-Date $_.TimeCreated)
"ID" = $_.ID
"Description" = $Description
"EventType" = (($_.KeywordsDisplayNames -EQ 'Audit Success') ? "Success" : "Failure")
"Details" = $Details
}
}
}
}
Once created, drop this function into a PowerShell console and run it with no parameters.
PS> Get-ADPasswordEvent
Shown below is running Get-ADPasswordEvent
. You can see that the function returns many success and failure events for password reset attempts.
You can pass the output of the
Get-ADPasswordReset
function toFormat-Table
to make the output a little easier to read.
Implementing Better Password Auditing Solutions
Whether you’re using the Event Viewer or PowerShell, you have to jump through a few different hoops to find AD auditing events. The native tools and abilities of Active Directory could certainly be expanded upon, and for that, third-party tools such as Specops Password Auditor can help.
Specops’ Password Auditor tool not only can find these events, but it can also create in-depth reports that include changes, expired passwords, blank passwords, and more. Specops Password Auditor can fill in the missing gaps for Active Directory password auditing.