Inhalt

Änderungen an sensitiven on-Prem Gruppen mit MDI alarmieren

Microsoft Defender for Identity ist ein mächtiges Werkzeug wenn es darum geht Änderungen an Benutzern und Gruppen im on-prem Active Directory zu identifizieren. Wenn man diese Daten im Microsoft 365 Defender Portal mittels Advanced Hunting analysiert und Custom Detection Rules nutzt kann man sehr einfach ein Change Tracking für sensitive Accounts etablieren.

Wenn du ein on-prem Active Directory beschützt sind Änderungen an hochprivilegierten Gruppen ein großes Risiko. Microsoft selbst definiert einen Teil dieser Gruppen in der Dokumentation zu Active Directory Domain Services in in der Defender for Identity Dokumentation werden zusätzliche Gruppen genannt. Insgesamt sind dies 17 Gruppen bei denen Änderungen überwacht werden sollten.

  • Account Operators
  • Administrators
  • Backup Operators
  • Domain Admins
  • Domain Controllers
  • Enterprise Admins
  • Enterprise Read-only Domain Controllers
  • Group Policy Creator Owners
  • Incoming Forest Trust Builders
  • Microsoft Exchange Servers
  • Network Configuration Operators
  • Power Users
  • Print Operators
  • Read-only Domain Controllers
  • Replicators
  • Schema Admins
  • Server Operators

Wenn zusätzlich noch Exchange vorhanden ist und nicht für split permissions konfiguriert wurde, sollten folgende Gruppen ebenfalls überwacht werden.

  • Exchange Trusted Subsystem
  • Exchange Windows Permission
  • Organization Management

Diese Überwachung gestaltet sich sehr komplex wenn kein zentrales Logging der Änderungen vorhanden ist. Hier kommt Defender for Identity ins Spiel. Alle Änderungen an AD Gruppen werden überwacht und an die Cloud weitergeleitet. Dafür muss die Windows Event Collection korrekt konfiguriert sein und alle Event Ids in diesem Artikel müssen überwacht werden.

Advanced Hunting

Wenn diese Vorraubsetzungen geschaffen sind können wir auf die Jagd gehen. Das Advanced Hunting Schema stellt die benötigten Daten in der Tabelle IdentityDirectoryEvents bereit. Weitestgehend aufbereitet um Sie abfragen zu können.

Welche Events muss ich filtern

Der erste SChritt ist es zu prüfen welche Aktionen überhaupt geloggt werden und wie auf einen bestimmten ActionType gefiltert werden kann. Die nachfolgende Abfrage listet alle Aktionen und Ihre Namen die im definierten Zeitraum aufgetreten sind.

IdentityDirectoryEvents 
| distinct ActionType
| sort by ActionType asc 

Für eine komplette Übersicht der möglichen ActionTypes muss die in-portal schema reference genutzt werden, da selbst Microsoft nicht alle möglichen Werte in der Online Dokumentation auflistet.

Das Ergebnis sollte ähnlich aussehen wie dieses:

/alert-sensitive-ad-groups-mdi/images/ActionTypes.png

In den meisten Fällen wird es eine Aktion mit dem Namen “Group Membership changed” geben. Mit diesem Wert werden wir weiter arbeiten.

Alle Gruppenänderungen finden

Um zu viele Ergebnisse zu vermeiden habe ich die Anzahl auf maximal 100 limitiert.

IdentityDirectoryEvents
| where ActionType == "Group Membership changed"
| limit 100

Die ausgegebenen Daten sind nicht unbedingt das womit man rechnet.

/alert-sensitive-ad-groups-mdi/images/RawQueryResults.png

Es sind zwar alle wichtigen Informationen vorhanden, jedoch nicht unbedingt in einem einfach lesbaren Format sondern als JSON im Feld AdditionalFields.

/alert-sensitive-ad-groups-mdi/images/AdditionalFields.png

Die Daten entwirren

Kusto bietet für die Verarbeitung von JSON Daten ein mächtige Funktion: parse_json

Die folgende Abfrage wirkt erstmal abschreckend, daher werde ich Sie Zeile für Zeile erläutern.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
IdentityDirectoryEvents
| where Timestamp >= now(-2d)
| where ActionType == "Group Membership changed"
| extend AdditionalFields = parse_json(AdditionalFields)
| extend FromGroup = AdditionalFields.["FROM.GROUP"]
| extend ToGroup = AdditionalFields.["TO.GROUP"]
// Extract target user or device name
| extend TargetObject =  iff( isnull(AdditionalFields.["TARGET_OBJECT.USER"]), AdditionalFields.["TARGET_OBJECT.GROUP"], AdditionalFields.["TARGET_OBJECT.USER"])
// Special case group managed service accounts and devices
| extend TargetObject =  iff( isnull(TargetObject), AdditionalFields.["TARGET_OBJECT.DEVICE"], TargetObject)
| project-away AdditionalFields
| project-reorder Timestamp, ActionType,Application, FromGroup, ToGroup, TargetObject

Als erstes filtert die Abfrage alle Daten aus die einen Timestamp älter als zwei Tage haben und gibt nur Zeilen mit dem ActionType = “Group Membership changed” aus.

1
2
3
IdentityDirectoryEvents
| where Timestamp >= now(-2d)
| where ActionType == "Group Membership changed"

Nun extrahiert die Funktion parse_json die JSON Daten aus dem Feld AdditionalFields und speichert diese in einer dynamischen Spalte mit demselben Namen. Die ursprüngliche Zeile wird einfach überschrieben.

4
| extend AdditionalFields = parse_json(AdditionalFields)

Durch diese Änderung können nun die einzelnen Felder in diesem Feld direkt ausgewertet werden und die beiden nächsten Zeilen speichern die Informationen über welche Gruppe verändert wurde in den neuen Felden FromGroup und ToGroup.

5
6
| extend FromGroup = AdditionalFields.["FROM.GROUP"]
| extend ToGroup = AdditionalFields.["TO.GROUP"]

Um herauszufinden ob ein Benutzerobjekt, ein Computerobjekt oder eine Gruppe als Mitglied hinzugefügt bzw. entfernt wurden nutzen wir AI eine einfache if/then Prüfung.

Das erste iff prüft ob das Feld TARGET_OBJECT.USER leer ist und wenn dies der Fall ist wird TARGET_OBJECT.GROUP als Wert gespeichert. Wenn nicht wird TARGET_OBJECT.USER genutzt.

Sollte kein dieser Felder die entsprechende Information beinhalten wird im zweiten iff das Feld TARGET_OBJECT.DEVICE verwendet.

7
8
| extend TargetObject = iff( isnull(AdditionalFields.["TARGET_OBJECT.USER"]), AdditionalFields.["TARGET_OBJECT.GROUP"], AdditionalFields.["TARGET_OBJECT.USER"])
| extend TargetObject = iff( isnull(TargetObject), AdditionalFields.["TARGET_OBJECT.DEVICE"], TargetObject)
Info
Group managed Service Accounts werden wie Computerobjekte behandelt und somit ebenfalls korrekt angezeigt.

Anschließend wird die Spalte AdditionalFields entfernt, da sie nicht mehr benötigt wird. Zusätzlich verändert project-reorder die Reihenfolge in der die Spalten angezeigt werden.

11
12
| project-away AdditionalFields
| project-reorder Timestamp, ActionType,Application, FromGroup, ToGroup, TargetObject

Das Ergebnis ist um einiges besser lesbar.

/alert-sensitive-ad-groups-mdi/images/CleanedUpResults.png

Nur wichtige Gruppen berücksichtigen

Die letzte Abfrage gibt jede Veränderung an einer Gruppe in den letzten zwei Tagen aus. Daher muss das Ergebnis noch weiter eingeschränkt werden um nur Änderungen an sensitive Gruppen zu sehen.

Dafür nutze ich eine ‘virtual table’ mit dem Namen GroupsToMonitor. Diese Tabelle beinhaltet nur eine Spalte GroupName und für jede Gruppe die überwacht werden soll eine Zeile mit dem Namen. So ist es super einfach die Abfrage für deine Umgebung anzupassen um weitere Gruppen hinzuzufügen oder nicht benötigte zu entfernen.

Notiz
Da diese Abfrage Änderungen an verschachtelten Gruppen nicht überwacht, müssen diese unbedingt manuell aufgenommen werden.
let GroupsToMonitor = datatable(GroupName:string)
[
"Account Operators",
"Administrators",
"Backup Operators",
"Domain Admins",
"Domain Controllers",
"Enterprise Admins",
"Enterprise Read-only Domain Controllers",
"Exchange Trusted Subsystem",
"Exchange Windows Permission",
"Group Policy Creator Owners",
"Incoming Forest Trust Builders",
"Microsoft Exchange Servers",
"Network Configuration Operators",
"Organization Management",
"Power Users",
"Print Operators",
"Read-only Domain Controllers",
"Replicators",
"Schema Admins",
"Server Operators",
];
IdentityDirectoryEvents
| where Timestamp >= now(-2d)
| where ActionType == "Group Membership changed"
| extend AdditionalFields = parse_json(AdditionalFields)
| extend FromGroup = AdditionalFields.["FROM.GROUP"]
| extend ToGroup = AdditionalFields.["TO.GROUP"]
// Extract target user or device name
| extend TargetObject =  iff( isnull(AdditionalFields.["TARGET_OBJECT.USER"]), AdditionalFields.["TARGET_OBJECT.GROUP"], AdditionalFields.["TARGET_OBJECT.USER"])
// Special case group managed service accounts and devices
| extend TargetObject =  iff( isnull(TargetObject), AdditionalFields.["TARGET_OBJECT.DEVICE"], TargetObject)
| where FromGroup in (GroupsToMonitor) or ToGroup in (GroupsToMonitor)
| order by Timestamp
| project-away AdditionalFields
| project-reorder Timestamp, ActionType,Application, FromGroup, ToGroup, TargetObject

In den meisten Fällen sollte das Ergebnis leer sein. Wenn nicht ist jetzt ein guter Zeitpunkt zu prüfen ob diese Änderung legitim war.

/alert-sensitive-ad-groups-mdi/images/NoResultFound.png

Eine Custom Detection erstellen

Um bei zukünftigen Änderungen einen Alarm zu erhalten kannst du eine Custom Detection erstellen. Dazu nach der Ausführung der Abfrage auf den Button “create detection rule” klicken.

/alert-sensitive-ad-groups-mdi/images/CreateDetectionRule.png

Gib dem Alarm einen sprechenden Namen und füge Informationen hinzu was gemacht werden soll wenn dieser auftritt. Wähle eine Severity die für deine Umgebung passend ist.

/alert-sensitive-ad-groups-mdi/images/AlertDetails.png

Als betroffenen Entität muss TargetAccountUpn ausgewählt werden und der Bereich Action kann übersprungen werden.

/alert-sensitive-ad-groups-mdi/images/ImpactedEntities.png

Erstelle den Alarm und wechsel zu Custom detection rules.

/alert-sensitive-ad-groups-mdi/images/Success.png

/alert-sensitive-ad-groups-mdi/images/DetectionRule.png

Um sofort ein Ergebnis zu erhalten kann die Detection manuell gestartet werden.

/alert-sensitive-ad-groups-mdi/images/RunManually.png

Alarm

Sollte der Alarm ausgelöst werden ist es sehr einfach zu erkennen welche Benutzer verändert wurde.

/alert-sensitive-ad-groups-mdi/images/ImpactedUser.png

In den Alert Details werden auch die Query Results inklusive der betroffenen Gruppe anzeigt. Da FromGroup und ToGroup unterschiedliche Felder sind, ist es einfach zu erkennen ob jemand zusätzliche Rechte erhält oder diese verliert.

/alert-sensitive-ad-groups-mdi/images/AlertDetails_2.png

Notiz

Mit kleinen Anpassungen könnte somit auch basierend auf diesen Feldern unterschiedliche Alarme ausgelöst werden

  • Ein Alarm mit “High Severity” wenn jemand in eine Gruppe aufgenommen wird
  • Der zweite mit einer “Information Severity” wenn ein Benutzer entfernt wurde

Fazit

Defender for Identity und Custom Detections sind ein wichtiges Werkzeug um deine Umgebung zu schützen. Die zusätzlichen Informationen die abgefragt und genutzt werden können sind ein Schatz um Änderungen an der on-Prem Umgebung festzustellen.

Ich kann nur jedem empfehlen sich mit Advanced Hunting auseinander zu setzen und zu spielen. Da alle Daten schreibgeschützt sind kann man dabei auch nichts kaputt machen und Kusto als Sprache ist intelligent genug um z.B. auch meine nicht optimierten Abfragen sehr schnell auszuführen.

Nutze wenn möglich | limit 10 um initial nur wenig Daten abzufragen und so schneller Ergebnisse zu prüfen.

Wenn dir dieser Blogeintrag gefallen hat solltest du dir meinen Blogpost Automatisch C2 traffic auf Endgeräten erkennen anschauen.