To prevent brute-force attacks on your Windows accounts, you should configure an appropriate Account Lockout Policy, which is supported in both Windows 10 and Windows 11. Changing these settings in the Group Policy Editor (gpedit.msc
) is straightforward:
If you want to query these settings programmatically, you can use one of these methods:
C:\>%windir%\System32\net.exe Accounts
…
Lockout threshold: 5
Lockout duration (minutes): 10
Lockout observation window (minutes): 10
…
The command completed successfully.
This method does not print the Allow Administrator account lockout setting, however.
The settings are stored in the [HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account]
registry key, which is not easily readable. Furthermore, Windows uses a single REG_BINARY
value that needs to be decoded. The Get-AccountLockoutSettings cmdlet uses reg.exe
to read the contents of that key and then decodes the binary data:
function Get-AccountLockoutSettings {
[CmdletBinding()]
param();
function Assert-ExitCode {
[CmdletBinding()]
param(
[Parameter( Mandatory )]
[scriptblock]
$Command
);
& $Command | Write-Verbose;
if( $LASTEXITCODE -ne 0 ) {
throw "Command $Command returned exit code $LASTEXITCODE.";
}
}
$random = [guid]::NewGuid().ToString('N');
Assert-ExitCode -Command {
& "$env:windir\System32\reg.exe" SAVE "HKLM\SAM\SAM\Domains\Account" "$env:TEMP\$random.hiv";
};
Assert-ExitCode -Command {
& "$env:windir\System32\reg.exe" LOAD "HKLM\$random" "$env:TEMP\$random.hiv";
};
"HKEY_LOCAL_MACHINE\$random [1]" | Out-File -FilePath "$env:TEMP\$random.txt";
Assert-ExitCode -Command {
& "$env:windir\System32\regini.exe" "$env:TEMP\$random.txt";
};
[byte[]] $bytes = Get-ItemPropertyValue -LiteralPath "HKLM:\$random" -Name 'F';
function ConvertTo-Minutes {
[CmdletBinding()]
param(
[byte[]]
$Bytes
);
$ticks = [System.BitConverter]::ToInt64( $Bytes, 0 );
[timespan]::FromTicks( -$ticks ).TotalMinutes;
}
@{
"Account lockout duration" = ConvertTo-Minutes -Bytes $bytes[0x30..0x37];
"Reset account lockout counter after" = ConvertTo-Minutes -Bytes $bytes[0x38..0x3F];
"Account lockout threshold" = [System.BitConverter]::ToUInt16( $bytes[0x54..0x55], 0 );
"Allow Administrator account lockout" = $(
switch( $bytes[0x4C] ) {
0 { "Disabled"; }
8 { "Enabled"; }
default { throw "Unexpected value '0x{0:X2}'." -f $_; }
}
);
};
Assert-ExitCode -Command {
& "$env:windir\System32\reg.exe" UNLOAD "HKLM\$random";
};
Remove-Item -LiteralPath "$env:TEMP\$random.hiv", "$env:TEMP\$random.txt" -ErrorAction 'SilentlyContinue';
}
Get-AccountLockoutSettings.ps1You need administrative rights to run this function. The output should look like this:
PS C:\> Get-AccountLockoutSettings | Format-Table -AutoSize
Name Value
---- -----
Account lockout duration 10
Allow Administrator account lockout Enabled
Reset account lockout counter after 10
Account lockout threshold 5