PowerShell Cleanup-AdminCount-Script

Windows[English]Kleiner Tipp für Administratoren von Windows-Systemen. Mark Heitbrink hat sich hingesetzt, und ein kleines PowerShell-Script gebaut, welches unter Windows das Active Directory auf verwaiste Administratorkonten prüft und das System von solchen Leichen bereinigt. Das PowerShell-Script kann noch einige weitere Feinheiten.


Anzeige

Ich bin die Tage auf Facebook unter gruppenrichtlinien über einen Post von Mark Heitbrink auf das Thema gestoßen.

Cleanup-AdminCount PS script

Mark hat das PowerShell-Script Cleanup-AdminCount auf GitHub bereitgestellt und schreibt dazu:

Ein sprachunabhängiges(!) Powershell-Skript zum Entfernen verwaister AdminCounts auf Benutzerobjekten im AD und zum Aktivieren der ACL-Vererbung. Reparieren von Benutzerkonten, die durch den sdprop/AdminSDHolder-Prozess in Active Directory geschützt und nicht mehr Mitglied von geschützten Gruppen sind.

Das Script, welches unter MIT-Lizenz bereitgestellt wird, funktioniert zunächst nicht mit dem Displaynamen der Gruppe sondern verwendet die Well-Known SID. Details zu den Funktionen sowie zum Aufruf sind der GitHut-Seite zu entnehmen.


Anzeige


Anzeige

Dieser Beitrag wurde unter Tipps, Windows, Windows Server abgelegt und mit , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

16 Antworten zu PowerShell Cleanup-AdminCount-Script

  1. Martin Handl sagt:

    Das ist so ein lediges Standardthema bei "PingCastle"- oder "Purple Knight"-Audits.
    Für einen Kunden hatte ich ein kleines PowerShell-Skript geschrieben um erstmal nur zu gucken: https://iqunit.com/admincount-powershell/

  2. Froschkönig sagt:

    Ich verstehe nicht, nach welchem Kriterium das Script Accounts zur Löschung vorschlägt, oder löscht? Nach zu großem LastLogonTime Wert scheinbar nicht. Nur nach dem Kriterium, dass Accounts in den bekannten vordefinierten Admingruppen sind? Weil sie in mehr als 1 solcher Gruppe sind? Das kann IMHO nicht Sinn der Sache sein. Das würde ja bedeuten, dass man nur noch mit "administrator" arbeiten würde, was ich für sehr gefährlich halte, auch in Bezug auf zwingend erforderlicher Personalisierung von Accounts, um Aktivitäten im AD überhaupt bestimmten Personen zuordnen zu können. Bei uns würde das Script übrigens nicht mal laufen, weil der Hauptadmin nicht "administrator" heißt und außerdem im Normalfall deaktiviert ist. Ich glaube, das Script würde zumindestens bei uns mehr Schaden anrichten, als dass es nützt.

    • Mark Heitbrink sagt:

      servus, lies den MS Artikel. du hast das Thema adminSDholder nicht verinnerlicht.

      jedes manuell delegierte Konto, hat keinen(!) Admincount= 1

      Admincount= 1 heißt, das Konto war mal in einer protected group, protected im sinne des adminSDholder… deswegen zb auch kein reset der Passwörter durch einen Konto Operator.

      du möchtest nicht, das der Helpdeskt Kennworte der Domänen Admins resettet (privilege escalation), deswegen schützt der adminSDholder diese sensiblen Konten

    • Mark Heitbrink sagt:

      entschuldige, ich lese gerade deine Skeptik noch Mal und möchte dir empfehlen einen Active Directory Workshop zu besuchen, am Besten keinen von der Stange, sondern individuell für dich/euch.

      bei dir vermischt sich Wissen mit Hören/Sagen und Glauben.
      Der adminSDholder mit seinem sdprop Prozess liegt genau in der Zone, wo du glaubst, aber nicht weißt

  3. Rolf Wilhelm sagt:

    Via "WellKnown SID" die Analyse zu machen erscheint mir zu kurz gedacht. Im Prinzip wird das Attribut von einer (ehemaligen) Gruppe vererbt, die das Attribut ebenfalls gesetzt hat. Wenn also ein User-Account "AdminCount=1" gesetzt hat, aber nicht (mehr) Mitglied einer Gruppe ist, die das ebenfalls gesetzt hat, dann kann es gelöscht und die Vererbung der Security wieder hergestellt werden.

    $Users = Get-ADUser -filter "AdminCount -eq 1" -Properties *
    foreach ($user in $Users) {
    $groups = ""
    foreach($group in ($user.MemberOf)) {
    $membergroup = Get-ADGroup $group -Properties AdminCount
    if ($membergroup.AdminCount -eq 1) { $groups += ";" + $membergroup.SamAccountName }
    }
    if ($groups.Length -ge 1) { $groups = $groups.Substring(1) }
    if ($groups.Length -ge 1 -or $user.IsCriticalSystemObject) {
    $status="good"
    if ($user.IsCriticalSystemObject) { $status="Critical" }
    } else {
    if ($Whatif -and $user.AdminCount -eq 1) {
    Write-Output "fix!`t$($user.distinguishedname)`t$($user.Displayname)`t$groups"
    } else {
    if ($User.AdminCount -eq 1) {
    Fix-UserInheritance $user
    $user = Get-ADUser $user.SamAccountName -Properties *
    $status = "fixed"
    } else {
    $status = "good"
    }
    if ($user.nTSecurityDescriptor.AreAccessRulesProtected -eq $True -or $user.adminCount -eq 1) { $status = "not fixed!" }
    }
    }

    function Fix-UserInheritance($user) {
    if (-not $user.isCriticalSystemObject)
    {
    If ($user.nTSecurityDescriptor.AreAccessRulesProtected -eq $True) {
    $ou = [ADSI]("LDAP://" + $user)
    $sec = $ou.psbase.objectSecurity
    if ($sec.get_AreAccessRulesProtected())
    {
    $isProtected = $False ## false allows inheritance, true disables inheritance
    $preserveInheritance = $true ## preserver inhreited rules
    $sec.SetAccessRuleProtection($isProtected, $preserveInheritance)
    $ou.psbase.commitchanges()
    }
    }

    # clear AdminCount=1
    $user.AdminCount = $null
    Set-ADUser -Instance $user
    sleep 5
    }

    }

    • Mark Heitbrink sagt:

      warum die Wellknown SID?
      wegen Domänen-Admins vs Domain Admins. die Gemeinsamkeit ist: S…DomSID-512

      letztlich musst du die Gruppen kontrollieren, die unter Ad/System/AdminSDholder eingetragen sind

    • Mark Heitbrink sagt:

      Ich habe es noch mal durchgeschaut. Das "admincount=1" wird NICHT von der Gruppe an das Mitglied übergeben. Ich habe mehr Gruppe mit dem Flag, als in der Liste des MS Artikels genannt, aber keines der Mitglieder hat das Flag, wenn es nicht in einer der geschützten Gruppen ist.

      Get-ADObject -Filter {Admincount -eq "1"}

      der sdprop Prozess arbeitet eine vorgegebene Liste ab

    • Mark Heitbrink sagt:

      Der Abgleich mit dem Gruppen Attribut ist flasch.

      Situation: Ich habe einen User "ca-admin", der ist Mitglied der "ca-admins", diese haben admincount = 1, der User auch, weil er mal in der Gruppe der Domänen Admins war.

      Dein script wird den AdminCount nicht resetten, weil er noch in der Gruppe "ca-admins" ist, die aber nicht schützenswert im Sinne des sdprop/adminSDHolders ist.
      Der adminSDHolder wird die Rechte auf dem User Objekt weiterhin schützen, obwohl der User kein Admin mehr im Sinne des adminSDHolders ist.

      Das könnte ein gewünschter Nebeneffekt sein, weil du damit eigene Admingruppen definieren kannst, aber dann sehe ich die Einordnung in eine eigene OU mit passenden Rechte, wer die Gruppe und das Objekt editieren darf als simpler, da einleuchtender im Sinne der Delegation.
      Wohingegen ich allerdings die Idee mag, im Sinne des Ausnutzens einer Logik, die nicht geplant war :-)

  4. Tomas Jakobs sagt:

    Verbesserungsvorschläge:

    1) try…catch verwenden, das Skript bricht sonst beim kleinsten Fehler ab

    2) Statt $AllTogether=$($AllGroups;$AllAdminSD) besser Compare-Object für mehr Klarheit und Kontrolle nutzen.

    3) Wenn Du schon mit Well-Known SIDs arbeitest und diese in einzelne Variablen packst, warum machst Du keine Liste und gehst alles in einer foreach ($entry in $WellKnownGroups) Schleife durch?

    4) Ich würde manuell vor Ausführen noch die PS Engine explizit abfragen und wählen. Gibt genügend Admins da draussen, die noch mit <5.1 arbeiten. Ist doof wenn mehrere installiert sind und das Skript sich die älteste zur Ausführung nimmt.

    5) ach ja und ein Logging oder Dry-Run wäre schön, bevor was im AD gelöscht wird

    Ansonsten gut gedacht und ich begrüsse jeden Admin, der anfängt zu programmieren und in Richtung GitOps geht (auch wenn ich jetzt Codeberg statt Github empfehlen würde).

    • Mark Heitbrink sagt:

      compare finde ich von der Logik nicht so simple, wie die Abfrage, ob ein Objekt 2 Mal in einer Liste auftaucht.

      foreach ist mein Kopf, ich schreibe immer noch batch Dateien in der Powershell. dann verstehe ich es auch :-)

      Dryrun ist vorhanden, das Script reported es , ohne den -clean Schalter.

      try/catch und Version nehme ich mit. Danke!

      • Mark Heitbrink sagt:

        achso .. und weil ich foreach mache, steigt das Script bei Fehler nicht aus, sondern macht beim nächsten Objekt weiter. zudem habe ich damit pro objekt eine klare Ausgabe

    • Stingray sagt:

      @Tomas Jakobs,

      hört sich interessant an, könntest Du die Anpassungen als Script zur Verfügung stellen? Danke.

  5. js sagt:

    Danke für das Skript!
    Ich war zu dem Thema auf der Suche nach Infos, wo die Liste der von SDprop abgeklapperten Gruppen eigentlich genau hinterlegt ist.
    (Ich war getrieben durch die unreife Idee, vielleicht eine eigene spezielle Gruppe in die Prüfliste mit aufzunehmen.)
    Ich habe leider keine klare Antwort gefunden, also muss ich wohl davon ausgehen, dass es hardcoded ist.
    Ich habe aber begleitend gefunden, dass das Schaltwerk "dwAdminSDExMask" im "dSHeuristics" Attribut die involvierten Gruppen reduzieren kann.
    Wahrscheinlich ist dessen Verwendung aber viel zu exotisch, als dass das extra im Skript berücksichtigt werden muss.

Schreibe einen Kommentar zu Martin Handl Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Hinweis: Bitte beachtet die Regeln zum Kommentieren im Blog (Erstkommentare und Verlinktes landet in der Moderation, gebe ich alle paar Stunden frei, SEO-Posts/SPAM lösche ich rigoros. Kommentare abseits des Themas bitte unter Diskussion. Kommentare, die gegen die Regeln verstoßen, werden rigoros gelöscht.

Du findest den Blog gut, hast aber Werbung geblockt? Du kannst diesen Blog auch durch eine Spende unterstützen.