Automatisch C2 traffic auf Endgeräten erkennen
Im Laufe des Wochenendes bin ich in meinem Twitter-Feed über das Projekt Feodo Tracker gestolpert. Das Projekt verfolgt aktive C2 Server, die mit den Schadprogrammen Dridex, Emotet/Heodo, TrickBot und QakBot in Verbindung stehen. Dabei kann man die Liste der Server aktiv durchsuchen oder die Daten als CSV herunterladen.
Die bereitgestellte Block-Liste ist in verschiedenen Varianten verfügbar. Als CSV, die alle IP-Adressen von aktiven C2-Servern enthält, sowie alle IP-Adressen, die in den letzten 30 Tagen aktiv waren, als eine Blockliste für z.B. die Firewall, die nur aktive C2-Server-IP-Adressen enthält, und eine vollständige Liste aller IP-Adressen, die jemals mit C2-Aktivitäten in Verbindung gebracht wurden. Diese letzte Liste sollte nicht verwendet werden, da sich die IP-Adressen dieser Server häufig ändern und von legitimen Websites wiederverwendet werden.
In diesem Artikel werde ich die empfohlene IP-Blockierliste verwenden, um zwei Möglichkeiten zu zeigen, wie man Verbindungen von mit MDE geschützten Endpunkten zu diesen IP-Adressen erkennen und/oder blockieren kann.
Advanced Hunting
Advanced hunting queries werden in KQL geschrieben und erlauben es sehr einfach externe Daten einzubinden. Dafür wird der externaldata
operator genutzt. Das macht das Erstellen der Custom Detection Rule um einiges einfacher.
Die folgende KQL Abfrage liest die Datei https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.txt
ein und stellt sie als virtuelle Tabelle BotnetC2IPBlocklist
mit nur einer Spalte RemoteIP
zu Verfügung.
Anschließend wird die Tabelle DeviceNetworkEvents abfragt und alle Verbindungen auf ausgehenden Datenverkehr zu einer IP Adresse in der BotnetC2IPBlocklist
Tabelle geprüft.
let BotnetC2IPBlocklist = externaldata(RemoteIP:string)
[
h@"https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.txt"
]
with(format="csv")
| where RemoteIP !startswith "#";
DeviceNetworkEvents
| where RemoteIP in ( BotnetC2IPBlocklist )
Wenn du diese Abfrage in deiner Umgebung ausführst findet du hoffentlich ebenfalls keine Treffer.
Update
Nachdem ich den Blogeintrag veröffentlicht habe, bin ich mit dem Team von abuse.ch in Kontakt getreten. Diese waren so nett die 30 Tage CSV Datei auch im JSON Fomrat bereitzustellen.
Das erlaubt eine etwas differenzierte Abfrage zu schreiben die nicht nur die IP Adresse, sondern auch den Port vergleicht. Das sollte False Positives stark reduzieren.
externaldata(RemoteIP: string, RemotePort: int, Status: string)
[
h@"https://feodotracker.abuse.ch/downloads/ipblocklist.json"
]
with(format="MultiJSON", ingestionMapping='[{"Column":"RemoteIP","Properties":{"Path":"$.ip_address"}}, {"Column":"RemotePort","Properties":{"Path":"$.port"}}, {"Column":"Status","Properties":{"Path":"$.status"}}]')
| where Status == "online"
| join kind=inner DeviceNetworkEvents on RemoteIP, RemotePort
Custom detection rule
Was wir gerade gemacht haben war eine einmalige Abfrage, die man natürlich manuell auch häufiger ausführen kann. Mit einer Custom Detection Rule kann man dies aber automatisieren.
Um eine Custom Detection Rule zu erstellen muss die Advanced Hunting Query mindestens folgende Informationen beinhalten. Zum Glück ist dies eigentlich immer der Fall wenn man die MDE Tabellen nutzt und nicht manuell die Ausgabe verändert.
- Timestamp, um das Ereignis zeitlich zuordnen zu können.
- DeviceId, um das richtige Gerät zu verknüpfen
- ReportId, um weitere Daten aus dem Report abzufragen und dem Analysten zu Verfügung stellen zu können.
Da all diese Informationen vorliegen reicht es “Create detection rule” anzuklicken um die Einrichtung zu starten.
Alert details
Dies ist der interne Name dieser Regel. In meinem Beispiel Connection to C2 server based on Feodo Tracker
. Diesen Namen wird der Analyst nicht zu sehen bekommen.
Über die Frequenz kann gesteuert werden wie häufig die Abfrage ausgeführt wird. In diesem Beispiel habe ich das Minimum von einer Stunde ausgewählt um möglichst schnell einen Alarm zu erhalten.
Der Alert Title ist der Name der später im Portal angezeigt wird, wenn der Alarm auftritt. Daher sollte etwas beschreibendes gewählt werden z.B. Connection to active C2 server detected
Das Severity Level sollte in Abstimmung mit den internen Prozessen gewählt werden.
Als Kategorie habe ich “Command and control” und als MITRE Technik “T1102 Web Service” gewählt.
Die Beschreibung und die empfohlenen Aktionen sollten möglichst ausführlich sein, damit Sie auch für den Analysten hilfreich sind.
Impacted entities
Im nächsten Schritt muss man auswählen welche Felder für die Verknüpfung gewählt werden sollen. Hier nutze ich DeviceId
um das betroffene Gerät zu verbinden und den Account UPN der Besitzer des ausführenden Prozess war um den betroffenen Benutzer zu finden.
Actions
Hier kann definiert werden welche Aktionen auf dem betroffenen Gerät ausgeführt werden sollen. Dies erlaubt ein weiteres Level an Automatisierung.
- Isolate device trennt im “Full mode” das Gerät fast komplett vom Netzwerk. Nur noch die Verbindung zu Microsoft Defender for Endpoint service wird zugelassen. Im “Selective mode” werden zusätzlich noch Outlook, Microsoft Teams und Skype zugelassen. So kann der Benutzer noch erreicht werden.
- Run antivirus macht genau das, einen Vollscan mit Defender AV ausführen.
- Initiate investigation startet eine automatische Überprüfung auf dem Gerät und führt alle Informationen in einem Incident zusammen
- Restrict app execution verhindert, dass der Angreifer weitere Software nachlädt und startet. Es wird nur noch Software ausgeführt die durch ein von Microsoft ausgestelltes Zertifikat signiert wurde
Anschließend nochmal alle Einstellungen prüfen und die Custom Detection erstellen.
Die eben erstellte Custom Detection findet sich unter Hunting -> Custom detection rules.
Test der Custom Detection
In diesem Fall ist es sehr einfach die Custom Detection zu prüfen. Es reicht ein Test-NetConnection -Port 443 -ComputerName IP
auf der PowerShell und man tauscht noch IP mit einer IP-Adresse aus der Blockliste. Anschließend muss man warten bis die Custom Detection automatisch gestartet wird oder man beschleunigt den Test indem man sie manuell startet.
Der Alarm sollte ausgelöst werden und ein Incident erstellt werden.
Auch die konfigurierten Aktionen sollten vollautomatisch gestartet werden.
Custom IoC
Die eben vorgestellte Variante der Custom Detection ist ein mächtiges Werkzeug um alle möglichen Szenarien zu alarmieren. Man muss es nur in eine Kusto Abfrage übersetzen können. Aber da der Zeitraum zwischen Aktion und Reaktion bis zu einer Stunde sein kann, ist dies in vielen Fällen zu lang und daher nicht ideal. Hier kommen Custom Indicators ins Spiel.
Custom Network Indicators erlauben es Netzwerkverbindungen direkt auf dem Endgerät zu blockieren. Somit sind Sie der ideale Kandidat f+r eine Zero Trust Architektur in der es keine zentrale Firewall oder Proxy gibt und die Geräte aufgrund von Remotearbeit oder Reisetätigkeit in privaten W-LAN Netzen hängen.
Es muss auch die erweiterte Funktion custom network indicators im Microsoft Defender for Endpoint Portal aktiviert werden.
Es gibt mehrere Optionen die Custom IoCs zu aktualisieren. Einer davon ist mittels CSV Import.
CSV Import
Die CSV-Beispieldatei, die man vom Portal herunterladen kann, enthält einige Hinweise dazu, wie die Daten formatiert seien müssen, um sie über das Portal zu importieren. Eine sehr wichtige Spalte ist die ExpirationTime
. Dies macht es sehr einfach, nur aktive C2-Server-IP-Adressen zu importieren, während alte Adressen automatisch raus altern. Dies minimiert Fehlalarme durch alte IoC.
IndicatorType,IndicatorValue,ExpirationTime,Action,Severity,Title,Description,RecommendedActions,Scope/DeviceGroups,Category,MitreTechniques,GenerateAlert
IpAddress,78.105.213.151,2018-09-16T12:11:06.2446367Z,Allowed,Informational,"Ip Address custom TI example","malware downloader","Recommended actions should be here","","","",false
Auf der Grundlage dieses Datenmodells habe ich dieses PowerShell-Skript erstellt, das die aktuelle Blockliste abruft und eine importfähige csv-Datei erstellt.
# Download latest CSV blocklist
$Blocklist = Invoke-WebRequest -UseBasicParsing -Uri "https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.txt"
# Split in multiline string an remove comments
$Blocklist = $Blocklist.Content -split "`r`n" | Select-String -NotMatch -Pattern "^#"
# Get current date + 2 hours
$ExpirationTime = Get-Date -Format "o" -Date (Get-Date).ToUniversalTime().AddHours(2)
# Generate IoC CSV
$IoC = foreach ($BlockIP in $Blocklist) {
[PSCustomObject]@{
'IndicatorType' = "IpAddress"
'IndicatorValue' = $BlockIP
'ExpirationTime' = $ExpirationTime
'Action' = 'Block'
'Severity' = 'High'
'Title' = 'Connection to active C2 server detected'
'Description' = 'Connection to C2 server based on Feodo Tracker blocklist'
'RecommendedActions' = 'Check machine for any signs of infections'
'Scope/DeviceGroups' = ''
'Category' = ''
'MitreTechniques' = ''
'GenerateAlert' = 'true'
}
}
$IoC | Export-Csv -Path .\feodo-ioc-blocklist.csv -Delimiter "," -NoClobber -NoTypeInformation
Sobald die CSV Datei erstellt wurde kann sie im MDE Portal importiert werden.
Anschließend kann nun auf einem der Endgeräte geprüft werden ob das Verhalten wie erwartet ist. Je nach genutzter Applikation kommt es zu unterschiedlichen Meldungen.
Blockiert durch Network Protection
Blockiert durch SmartScreen
Vollautomatisiert mit Azure Automation
Wenn der PoC dein Interesse geweckt hat, zeige ich dir, wie ich eine vollständig automatisierte Lösung mit Azure Automation implementiert habe.
Dies ist keine Klick-für-Klick-Erklärung, sondern eher ein Überblick, um etwas Ähnliches selbst zu implementieren.
Grundlegende Kenntnisse in Azure und Azure Automation sind erforderlich.
Zuerst musst du eine Entra ID (Azure AD) application registration erstellen, welche später für die Authentifizierung genutzt wird.
Diese Application benötigt die folgenden API Rechte und diese müssen für den gesamten Tenant genehmigt werden. Dazu muss der genutzte Account in einer der folgenden Rollen aktives Mitglied sein: Global Administrator, Privileged Role Administrator, Cloud Application Administrator oder Application Administrator.
Lege noch ein Client Secret an und notiere das Secret Value, dieses brauchst du gleich.
Deploye die benötigten Infrastrukturkomponenten mittels ARM Template.
Die Application Id und den das Application Secret müssen auf jeden Fall geändert werden. Den Automation Account Namen kannst du ebenfalls ändern. Tenant Id, Location und BaseTime dürfen nicht verändert werden.
Wenn du nicht das bereitgestellte ARM template nutzen willst, kannst du die Komponenten auch einzeln anlegen:
- Automation Account
- Runbook mit dem PowerShell Skript weiter unten bzw. hier
- Scheduler der jede Stunde triggert
- Verbindung zwischen Scheduler und Runbook
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]
$ApplicationId,
[Parameter(Mandatory = $true)]
[string]
$TenantId
)
function Invoke-IndicatorImport {
param (
$Body,
$Headers
)
#region Import indicators
try {
$Response = Invoke-RestMethod -Method Post -Headers $Headers -Body $Body -UseBasicParsing -Uri "https://api.securitycenter.microsoft.com/api/indicators/import"
foreach ($ReturnValue in $Response.value) {
if ($ReturnValue.isFailed) {
Write-Warning "Could not import indicator`t $($ReturnValue.indicator) because:`t $($ReturnValue.failureReason)"
} else {
Write-Output "Successfully imported indicator`t $($ReturnValue.indicator)"
}
}
} catch {
Write-Warning "$($($_.Exception).Message)"
}
#endregion
}
# Retrieve application secret
$AppSecret = Get-AutomationVariable -Name 'AppSecret'
#region Connect to Defender for Endpoint service API
$body = @{
"resource" = "https://api.securitycenter.windows.com"
"client_id" = $ApplicationId
"client_secret" = $AppSecret
"grant_type" = "client_credentials"
}
$Response = Invoke-RestMethod -Method Post -Body $body -UseBasicParsing -Uri "https://login.windows.net/$TenantId/oauth2/token"
$AccessToken = $Response.access_token
$Headers = @{
'Content-Type' = 'application/json'
'Accept' = 'application/json'
'Authorization' = "Bearer " + $AccessToken
}
#endregion
# Download latest CSV blocklist and filter out offline servers
try {
$Blocklist = Invoke-RestMethod -Method Get -UseBasicParsing -Uri "https://feodotracker.abuse.ch/downloads/ipblocklist.json"
$Blocklist = $Blocklist | Where-Object status -EQ "Online"
} catch {
throw "Could not download list of indicators - $($($_.Exception).Message)"
}
# Get current date + 2 hours
$ExpirationTime = Get-Date -Format "o" -Date (Get-Date).ToUniversalTime().AddHours(2)
#region Generate IoC JSON
# https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/import-ti-indicators
$i = 0
$Indicators = New-Object System.Collections.ArrayList
foreach ($BlockIP in $Blocklist) {
$Indicators.Add( @{
indicatorValue = "$($BlockIP.ip_address)"
indicatorType = "IpAddress"
action = "Block"
generateAlert = "False"
severity = "High"
title = "Connection to active C2 server detected"
description = "Connection to C2 server based on Feodo Tracker blocklist.`n`nAdditional information:`nMalware type: $($BlockIP.malware)`nFirst seen: $($BlockIP.first_seen)`nLast online date: $($BlockIP.last_online)`nTCP port: $($BlockIP.port)"
recommendedActions = "Check machine for any signs of infections"
expirationTime = $ExpirationTime
}) | Out-Null
$i++
if ($i -eq 500) {
# Body is limited to 500 indicators per request
$BodyJSON = @{ "Indicators" = $Indicators } | ConvertTo-Json
Invoke-IndicatorImport -Headers $Headers -Body $BodyJSON
# Clear indicators array
$Indicators = New-Object System.Collections.ArrayList
# Reset counter
$i = 0
}
}
if ( $Indicators.Count -gt 0) {
# Submit last batch of indicators
$BodyJSON = @{ "Indicators" = $Indicators } | ConvertTo-Json
Invoke-IndicatorImport -Headers $Headers -Body $BodyJSON
}
#endregion
Nun starte das Runbook um zu prüfen ob alles wie erwartet funktioniert. Das Skript ist gesprächig und sollte etwaige Fehler melden. Beim starten muss die Application Id und die Tenant Id angegeben werden.
Der letzte Schritt ist die Anlage eines Schedulers der das Runbook jede Stunde ausführt und die IoCs aktualisiert. Da die erstellen IoCs nur eine Lebenszeit von 2 Stunden haben sollte das Skript mindestens alle 2 Stunden und in keinem Fall häufiger als alle 5 Minuten laufen. Da die Server von abuse.ch bei jedem Lauf angefragt werden sollten diese nicht unnötig belastet werden. Siehe dazu auch die Terms of Service und lass doch eventuell auch gleich eine Spende da.
Das war’s. Du hast deine Endgeräte erfolgreich gegen eine weiter bekannte Gefahr beschützt und das ohne viel Aufwand.
Zusammenfassung
Custom detection Rules und Custom Indicators sind eine große Hilfe um detaillierte Alarme auszulösen und erlauben es, in Kombination mit Funktionen wir Network Block, Endgeräte auch außerhalb der Firmenfirewall zu schützen.
Durch die offene API ist es möglich viele Aufgaben zu automatisieren und externe Datenquellen effektiv zu nutzen. Die hier gezeigte Integration kann dazu als Blaupause dienen. Auch ein Blick in Richtung Azure Logic App connector lohnt sich, da hier Automatisierung auch ohne viel Skripten möglich ist.
Ein wichtiger Hinweis an dieser Stelle ist aber, das nicht alle hier gezeigten Funktionen auf jedem Betriebssystem funktionieren. Aber Microsoft hat vor Kurzem die “modern unified solution for Windows Server 2012 R2 and 2016 Preview” veröffentlicht, welche viele wichtige Funktionen wie Network Protection auf diese Betriebssysteme portiert.