Quick-Tipp: Inaktive Nutzerkonten aufspüren

15.04.2021
G DATA Blog

Neue Benutzerkonten sind schnell angelegt - aber wie behält man diese am besten im Blick? So manche Karteileiche liegt vielleicht noch jahrelang im virtuellen Keller des Active Directory.

Maike Renner gehört zum Administrationsteam in einem mittelständischen Betrieb. Ihr obliegt normalerweise die Verwaltung von Nutzerkonten. Nachdem jedoch kürzlich mehrere schwerwiegende Sicherheitslücken im Mailserver des Unternehmens bekannt wurden, musste sie kurzfristig als Springer in anderen Teams aushelfen. Zwei Wochen später ist Ruhe eingekehrt – dachte sie. Eines Abends erhält sie einen Anruf des Geschäftsführers, der sich nicht am Terminalserver anmelden kann. Angeblich seien Benutzername und Kennwort nicht bekannt. Ein Blick in die Administratorkonsole des Active Directory sollte hier Klarheit schaffen – doch selbst Maike kann sich nicht einloggen. Da sie nur fünf Minuten vom Büro entfernt wohnt, macht sie sich trotz der Uhrzeit – es war immerhin nach 21:00 Uhr - mit einem unguten Gefühl auf den Weg. Ihre Ahnung sollte bestätigt werden: eine Ransomware hat sämtliche Datenbanken und Fileserver verschlüsselt. Und auch der globale Katalog, das Herzstück des Active Directory, ist betroffen. Nun ist guter Rat teuer.

Ursachenforschung

Vier Tage später ist der Betrieb fast wieder so etwas wie normal. Ein Backup, das noch kurz vor dem Zuschlagen der Malware gelaufen ist, konnte wiederhergestellt werden und es sind nur eine Handvoll unkritischer Dateien zerstört worden.

Der Incident-Response-Dienstleister, den sie an jenem Abend ebenfalls umgehend informiert hatte, fand schnell heraus, dass ein exponierter Terminalserver der Ausgangspunkt des Angriffs gewesen war. Das Passwort eines Ex-Mitarbeiters, der seit zwei Jahren nicht mehr im Unternehmen ist, war kompromittiert worden. Mittels einer Automatisierung hatten die Angreifer einfach so lange das Passwort geraten bis sie Erfolg hatten. Zwar lässt sich das Passwort im Nachhinein nicht mehr rekonstruieren, aber alleine die Tatsache, dass das Nutzerkonto noch nicht deaktiviert war und noch immer für Remote-Einwahlen nutzbar war, stellte ein Problem dar.

Doch alle Konten durchzugehen, die im Active Directory vorhanden sind, ist eine fast unlösbare Aufgabe: hunderte, wenn nicht tausende Nutzerkonten existieren hier – aktive Mitarbeiter, freie Mitarbeiter, Systemhäuser, ausgeschiedene Mitarbeiter, KollegInnen in Elternzeit, Dienstkonten für Datenbankserver – die Liste ist lang. Die alle durchzugehen, würde Wochen dauern. Glücklicherweise lässt sich so etwas leicht automatisieren, und es sind keine zusätzlichen kostenpflichtigen Programme notwendig. Ein einfaches PowerShell-Skript schafft hier Abhilfe für Maike und auch alle anderen Administratoren, die eine Bestandsaufnahme benötigen.

<#
.Synopsis
   The script outputs all AD user accounts that have not logged into AD for x days.
.DESCRIPTION
   The script outputs all AD user accounts that have not logged into AD for x days.
   The number of days can be specified as parameter -LastLogonInDays, otherwise the
   default value 90 is used.
.EXAMPLE
   Get-LastAdLogon
.EXAMPLE
   Get-LastAdLogon -LastLogonInDays 180
#>

Param(
    [Parameter(Position=0, Mandatory = $false)]
    [string]$LastLogonInDays = 90
)

Import-Module ActiveDirectory -ErrorAction Stop

Get-ADUser -Filter { Enabled -eq $true } -Properties SamAccountName, DisplayName, LastLogonDate |
    Where-Object { ($_.LastLogonDate -lt (Get-Date).AddDays(-$LastLogonInDays) -and ($_.LastLogonDate -ne $null)) } |
    Sort-Object -Property SamAccountName |
    Select-Object -Property SamAccountName, DisplayName, LastLogonDate

Serviervorschlag: Der Output dieses Skriptes listet all die Konten auf, die über einen gewissen Zeitraum keine Anmeldeaktivitäten zu verzeichnen hatten.

Für besondere Fälle....

Jetzt weiß Maike zumindest schon einmal, welche Nutzerkonten sie im Auge behalten und gegebenenfalls deaktivieren sollte. Wenn es jedoch darum geht, verdächtige Aktivitäten schneller zu entdecken, lassen sich auch andere Informationen aus dem Active Directory nutzen. Generell stehen hier viele Daten zur Verfügung, die eine große Hilfe sein können - man muss sie nur nutzen.

Ein recht sicheres Anzeichen dafür, dass eventuell etwas nicht mit rechten Dingen zugeht, sind fehlgeschlagene Login-Versuche. Treten diese gehäuft über einen sehr kurzen Zeitraum auf (z.B. Dutzende Anmeldeversuche innerhalb weniger Sekunden), sollten die Alarmglocken angehen. Andere Aktivitäten sind dagegen eher wenig verdächtig - denn fehlgeschlagene Loginversuche gibt es häufig, wenn Nutzer gerade ihr Passwort geändert haben oder auch wenn sie gerade aus einem längeren Urlaub zurückkehren.

Active Directory protokolliert fehlgeschlagene Loginversuche und speichert diese in einem internen Zähler. Dieser wird wieder zurückgesetzt, sobald die Anmeldung erfolgreich war. Hat ein Angreifer also einen Zugang bereits kompromittiert, taucht er hier nicht auf. Einen Haken hat das Ganze jedoch: In größeren Netzwerkumgebungen ist oft mehr als nur ein Domain Controller (DC) im Einsatz. Wer sich aus der Ferne anmeldet, landet nicht zwangläufig immer bei demselben Domain Controller - je nachdem, welcher DC gerade mehr oder weniger Auslastung hat. Die Anzahl fehlgeschlagener Anmeldeversuche ist eine der Informationen, die nicht immer zwischen ein DCs synchronisiert wird. Wir müssen also die Information von jedem einzelnen DC abfragen. Genau das tut das nachfolgende Skript.

<#
.Synopsis
   The script outputs all AD user accounts that have not logged into AD for x days, as well as failed login attempts.
.DESCRIPTION
   The script outputs all AD user accounts that have not logged into AD for x days.
   The number of days can be specified as parameter -LastLogonInDays, otherwise the
   default value 90 is used. Each Domain Controller on the same domain will be queried for failed login attempts. 
   The output constitutes the total number of failed logins over all DCs on the domain. 
   The condition in line 38 will prevent accounts that have never logged on previously from showing up in the results. 
   (oftentimes those accounts will display a nonsensical LastLogonDate that is in the 1600s) 
.EXAMPLE
   Get-LastAdLogonExtended
.EXAMPLE
   Get-LastAdLogonExtended -LastLogonInDays 180
#>

Param(
    [Parameter(Position=0, Mandatory = $false)]
    [string]$LastLogonInDays = 90
)

Function Get-ADUserLogonData([Hashtable]$UserData)
{
    $badPwdCount = 0 
    $badPwdTime = New-Object System.DateTime
    $allDCs = Get-ADDomainController -Filter * | Select-Object -Property Name

    foreach($dc in $allDCs)
    { 
        $user = Get-ADUser -Identity $UserData.SamAccountName -Server $dc.Name -Properties badPwdCount, badPasswordTime 
        $badPwdCount += $user.badPwdCount
        $userBadPwdTime = [System.DateTime]::FromFileTime($user.badPasswordTime)

        if ($userBadPwdTime -gt $badPwdTime)
        {
            $badPwdTime = $userBadPwdTime
        }
    }

    if ($badPwdTime -lt (Get-Date -Date '2000-01-01'))
    {
        $badPwdTime = $null
    }

    $UserData.BadPwdCount = $badPwdCount
    $UserData.BadPwdTime = $badPwdTime
}

Import-Module ActiveDirectory -ErrorAction Stop

$users = Get-ADUser -Filter { Enabled -eq $true } -Properties SamAccountName, DisplayName, LastLogonDate |
            Where-Object { ($_.LastLogonDate -lt (Get-Date).AddDays(-$LastLogonInDays) -and ($_.LastLogonDate -ne $null)) }

foreach ($user in $users)
{
    $userData = @{SamAccountName=$user.SamAccountName;DisplayName=$user.DisplayName;LastLogonDate=$user.LastLogonDate;BadPwdCount=0;BadPwdTime=$null}
    Get-ADUserLogonData -UserData $userData
    [PSCustomObject]$userData
}

Auswertung

Das oben stehende Skript ist als Anregung zu verstehen. Der Output, den es generiert, muss sinnvoll ausgewertet werden. Zwei Typen von Konten verdienen hier besonderes Augenmerk: Die Konten von Mitarbeitern, die das Unternehmen bereits verlassen haben und solche, die unregelmäßig von externen Dienstleistern genutzt werden. Die Accounts von Ex-Mitarbeitern sollten zumindest deaktiviert werden - so kann ein Angreifer diese nicht mehr nutzen, um sich im Netzwerk anzumelden. Solche Konten zu löschen, mag der erste Impuls sein, aber damit würden einige Informationen permanent verloren gehen - vor allem die Zugehörigkeit bestimmter Daten bzw. Dateien. Zudem gelten für bestimmte Informationen möglicherweise auch gesetzliche Speicherfristen.

Der zweite Kontentyp verdient einen noch kritischeren Blick: Accounts, welche von Dienstleistern wie etwa Systemhäusern genutzt werden. Diese sind zwar nicht dauerhaft in Benutzung, verfügen aber oftmals über weitreichende Berechtigungen, bis hin zur Rolle eines Domain-Administrators. Schafft ein Angreifer es, einen solchen Benutzeraccount zu übernehmen, stehen ihm fast alle Türen innerhalb des Netzwerkes offen. Daher sollten auch solche Konten deaktiviert werden, solange sie nicht benötigt werden. Ist das aus praktischen Gründen nicht möglich, sollte der Zugang auf vorher definierte Wartungsfenster beschränkt werden, etwa eine bestimmte Uhrzeit.

Zusammenfassung

Natürlich lässt sich im Rahmen eines Artikels wie diesem kein umfassendes Konzept zur Verfolgung und Verwaltung von Nutzerkonten erstellen. Dennoch soll dieser Text als Anregung dienen, künftig diesen Aspekt mit zu berücksichtigen. Die wichtigsten Tipps sind hier noch einmal zusammengefasst:

  • Regelmäßig das Active Directory nach inaktiven Nutzerkonten durchsuchen
  • Nicht genutzte Konten entweder löschen oder deaktivieren
  • Zugänge für externe Dienstleister besonders im Blick behalten; gegebenenfalls nur nach Bedarf freischalten. Endet die Zusammenarbeit mit einem Dienstleister: unbedingt das Konto deaktivieren!
  • Verlässt ein Mitarbeiter das Unternehmen: Nutzerkonto sofort stillegen; hier ist gegebenenfalls eine Absprache mit der Personalabteilung erforderlich.

Tim Berghoff
Security Evangelist

Wichtige IT-Security-News per E-Mail

  • Aktuelle IT-Gefahren
  • Schutz-Tipps für Privatkunden
  • 15 % Willkommensgutschein