How admins can cheat at changing their password


Here is a little known trick that you can do if you have AD permissions to manage your own account: when you are prompted to change your password when its age has expired, do this:

  • Start AD Users & Computers, and find your account.
  • Open the Accounts tab, check the box next to “User must change password at next logon”, and click Apply.
  • Clear the box next to “User must change password at next logon”, and click Apply.

Done. You no longer have to reset your password, and in the meantime, you did not have to change your password. Good idea? Of course not. If your account is set to expire, this was done for security reasons. Bypassing it and keeping the same password forever compromises security, and worse, you did it on purpose.

The way this trick works is simple. Each account has an attribute called pwdLastchanged. This records the timestamp of the most recent password change. If you set “User must change password at next logon” the value of pwdLastchanged gets set to the specific value of 0 (zero). This value tells AD to prompt the user at next logon to change his password. If you clear the flag again, the attribute must get another value than zero, of course. But which value? The choice the AD designers made was to assign the current time… so the overall effect is that it seems as if the user changed his password just now.

Many companies monitor their admin accounts for the flag “password never expires”, making sure it is not set on these accounts. If an admin changes this flag, it is visible. The trick above is not visible for inspection, at least not easily. But I would not be posting this if I did not have a solution. The trick is to look at replication metadata. There is a famous TechNet article called How the Active Directory Replication Model Works that explains all this, but for now let me give you the really short summary.

Each attribute on an AD object has replication information, the metadata. This data contains useful information such as update version numbers, timestamp of the last change, and the DC that made the last change to the attribute. Taking for example the attribute unicodePwd, its metadata would show when the password was last written. You can see the troubleshooting potential here. In one shot you can see where an attribute was last changed, and when. You can immediately go to that DC to investigate further.

With sufficient permissions you can read this data using tools like repadmin or PowerShell. Typically, a Domain Admin is needed. Interestingly, Full Control on an object is not sufficient, you really need high-level domain permissions. The following working sample script does the job of reading the required information to determine if an admin cheated at setting his/her password:

[CmdletBinding()]
param (
    [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
    [string] $DistinguishedName,
    [Parameter(Mandatory=$false)]
    [string]$dc=(Get-ADDomainController -service PrimaryDC -Discover).hostname
)

PROCESS
{
    Get-ADReplicationAttributeMetadata -server $dc -Object $DistinguishedName -Properties unicodepwd,pwdlastset,samaccountname | ForEach-Object {
        $attribute = $_ 
        switch ($attribute.attributename)
        {
            "unicodepwd" { 
                $unicodepwdtime = $attribute.LastOriginatingChangeTime 
            }
            "pwdlastset" { 
                $pwdlastsettime = $attribute.LastOriginatingChangeTime 
                $pwdlastsetvalue = [datetime]::fromFileTime($attribute.attributevalue) 
                $pwdneverset = $attribute.attributevalue -eq 0
            }
            "samaccountname" { 
                $samaccountname = $attribute.attributeValue 
            }
        }
    }
    $delta = New-TimeSpan -Start $unicodepwdtime -End $pwdlastsetvalue
    $fakedSettingPWD = -not $pwdneverset -and  ($delta.Seconds -gt 2)
    [PSCustomObject] @{
        samAccountname = $samaccountname
        pwTimeStamp = $unicodepwdtime
        pwdLastSetValue = $pwdlastsetvalue
        fakedSettingPWD = $fakedSettingPWD
    }
}

This code sample is enabled for pipeline input, allowing you to verify large numbers of users in one shot. The central function here is Get-ADReplicationAttributeMetadata (only available on 2012 R2 and higher) which extracts the metadata for a given object — a user in this case. We need to point it to a specific DC and tell it which attributes we need to inspect. One of the attributes we need is unicodePwd, which is a write-only attribute representing your password. It returns an object representing the metadata. Next, we analyze this object and compare the value of pwdLastSet to the metadata of unicodePwd. If these differ, and especially if pwdLastSet is more recent, the user had his flag toggled.

This is a typical application of the script. Save it as Get-UserLastChangedPassword.ps1 and analyze the Domain Admins group as follows:

Get-ADGroupMember -Identity "domain admins" -Recursive | .\Get-UserLastChangedPassword.ps1 | Out-GridView

I tried this at various customers, and the results were interesting. At two customers there was nothing suspect going on, but at a third we spotted a group of admins where about 30% of the people were apparently cheating and showing each other how to do it!

This is a sample from my lab, illustrating what it might look like with three users that are problematic:

blog-really-pwd-changed

Get the full script including comments and documentation here.

Comments (2)

  1. Nice article.

    As always, there is a PowerShell one-liner instead of manipulating the UI. In this case, it’s a two liner as it needs to be run twice with a different value

    Set-ADUser -Identity username -Replace @{pwdLastSet=0}
    Set-ADUser -Identity username -Replace @{pwdLastSet=-1}

    This directly updates the pwdLastSet value to the current time. You can’t set the value directly. The interface only accepts two possible values; 0 and -1. A value of 0 forces the password to be changed at next logon. The -1 sets it to the current time.

    FWIW, this shenanigan can also be detected as the version number of the pwdLastSet attribute in the metadata will be two higher than the unicodePwd, lmPwdHistory and ntPwdHistory attributes. By using the version number method, you can detect how many time shenanigan was performed on the user object.

  2. Mary B says:

    although current security advice from GCHQ, NIST etc and indeed MSR is *not* to force password changes on users, because you drive them to write them down

Skip to main content