Active Directory
banshee/active-directory-collection/active-directory/winrm/
krbtgt
The age of the password of the krbtgt accounts of a domain, by WinRM, PowerShell and LDAP
par banshee1 installation2 versions
v2Dernière
Publiée le 28 octobre 2026. One naming convention across collections; documentation completed.
// The age of the password of the krbtgt accounts of a domain, by WinRM: the key that signs every Kerberos
// ticket of the domain. An old one keeps any ticket forged with a stolen copy valid as long as it lives.
// The accounts are krbtgt, and krbtgt_<number> for each read-only domain controller.
// Runs one PowerShell script that only reads the directory over LDAP, on the domain controller itself: the
// attributes sAMAccountName and pwdLastSet (the time of the last change of the password, in hundreds of
// nanoseconds since 1 January 1601 UTC) of the users whose sAMAccountName starts with krbtgt. The age is
// counted in days on the domain controller.
// The script is the one of active-directory/ssh/krbtgt, without the powershell -Command "..." that cmd.exe
// needs there.
need('protocol:winrm');
per('account', 'Accounts');
output('age', 'number', array('label' => 'Password age', 'unit' => 'd', 'per' => 'account'));
watch.above('old', 'krbtgt password old', 'age', array('warning' => 180, 'critical' => 365));
watch.missing('missing', 'krbtgt account missing', 'age', array('critical' => array('krbtgt')));
$read = winrm.run("$ErrorActionPreference = 'Stop'; "
. "[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding; "
. "$computer = Get-CimInstance Win32_ComputerSystem; "
. "$server = $computer.DNSHostName + '.' + $computer.Domain; "
. "$rootDSE = [ADSI]('LDAP://' + $server + '/RootDSE'); "
. "$domain = [ADSI]('LDAP://' + $server + '/' + $rootDSE.Properties['defaultNamingContext'][0]); "
. "$searcher = New-Object DirectoryServices.DirectorySearcher($domain,"
. " '(&(objectCategory=person)(objectClass=user)(sAMAccountName=krbtgt*))', @('sAMAccountName', 'pwdLastSet')); "
. "$now = [DateTime]::UtcNow; "
. "$accounts = @($searcher.FindAll() | ForEach-Object {"
. " @{name = [string]$_.Properties['samaccountname'][0];"
. " days = [long]($now - [DateTime]::FromFileTimeUtc([long]$_.Properties['pwdlastset'][0])).TotalDays} }); "
. "ConvertTo-Json -Compress -Depth 4 -InputObject @{accounts = $accounts}");
$answer = text.trim($read['output']);
if (list.count(text.match($answer, '^\{')) == 0) {
fail('misconfigured', 'PowerShell did not return the krbtgt accounts', $answer . $read['errors']);
}
$krbtgt = json.read($answer);
$accounts = 0;
foreach ($krbtgt['accounts'] as $account) {
$accounts = $accounts + 1;
value('age', $account['days'], $account['name']);
}
if ($accounts == 0) {
fail('misconfigured', 'No krbtgt account found in the directory', $answer);
}