banshee is under active development: collectors will keep changing, and a version you publish will likely need adjustments. For stable use, wait for version 1.0.0.

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

  • active-directory
  • kerberos
  • krbtgt
  • password
  • winrm
  • powershell

by banshee1 installation2 versions

Keen 1.0.0 · 44 lines
// 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);
}