Inspiriert von diesem Blog-Eintrag und diesem Blog-Eintrag, habe ich einmal versucht herauszufinden, wo im Betriebssystem überall Azure-Zugangsdaten, also JWTs oder Refresh-Tokens, gespeichert werden, wenn die Anwender bestimmte Tools, die mit Azure interagieren, verwenden.
Ein JWT (JSON Web Token) ist eine Möglichkeit, wie ein Client Anmeldeinformationen speichern und an den Server übertragen kann. Die Informationen werden in einem JSON-Objekt gespeichert und dann mithilfe einer digitalen Signatur geschützt. Diese Signatur verhindert, dass die Informationen auf dem Client oder während der Übertragung verändert werden. Der Server kann die Signatur prüfen und die angefragten Daten bereitstellen, bzw. die gewünschten Aktionen ausführen. Genaue Details zu JWT oder Refresh-Tokens können hier gefunden werden.
Das spannende an dem Thema ist, dass im Gegensatz zu den Single-Sign-On Informationen (Kerberos & NT-Hash) in einem on-premise Active Directory, die nur mit Administrator-Rechte ausgelesen werden können, hier im Benutzerkontext auf die Daten zugegriffen werden kann.
Die Azure PowerShell von Microsoft ist eines von vielen Werkzeugen mit denen Administratoren auf Azure und vor allem auf die Ressourcen darin zugreifen und verwalten können. Bevor die Azure PowerShell verwendet werden kann, muss der Benutzer eine Anmeldung an Azure durchführen. Der Befehl dazu lautet Connect-AzAccount
.
Wenn der Befehl ausgeführt wird, kann in Procmon
beobachtet werden, dass zwei Dateien von PowerShell geschrieben werden.
Die AzureRMContext.json
enthält JSON-Daten, die unter Umständen ein AccessToken enthalten können. Unter welchen Umständen ein Azure Token vorhanden ist, konnte ich nicht nachvollziehen.
Weiterhin wird die Datei msal.cache
geschrieben. Beim Betrachten der Datei fällt zunächst auf, dass sie nicht lesbar ist. Wer allerdings genauer hinsieht und die Datei in einem Hex-Editor öffnet, der erkennt eine bekannte Signatur am Beginn der Datei. Der HEX-Wert 01000000d08c
ist charakteristisch für einen DPAPI-Blob. Das bedeutet, dass die Datei mit der DPAPI verschlüsselt wurde. Das Gute an einem DPAPI-Blob ist, dass er mit einem PowerShell Skript entschlüsselt werden kann, zumindest solange es der Eigene ist.
Add-Type -AssemblyName System.Security;
$Path = <Pfad zur msal.cache Datei>
$data = Get-Content -Path $Path -Encoding Byte
$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser
$plaintext = [System.Security.Cryptography.ProtectedData]::Unprotect($data, $null, $scope )
[System.Text.UTF8Encoding]::UTF8.GetString($plaintext)
Im Anschluss kann der Inhalt der Datei gelesen werden. Die Datei enthält ein JSON-Objekt, mit diversen Access- und RefreshTokens.
Die Azure CLI ist ein in Python geschriebenes Framework, das die plattformübergreifende Verwaltung und Steuerung von Azure erlaubt. Für die Anmeldung in der Azure CLI wird der Befehl az login
verwendet. Nach bzw. während der Anmeldung werden unter Windows mehrere Dateien geschrieben.
In der Datei accessTokens.json
werden diverse Access- und RefreshTokens gespeichert, die für die Azure CLI notwendig sind.
Die Frage, wo Office seine Tokens/Zugangsdaten speichert, wurde in einem der genannten Artikel am Anfang schon beleuchtet. Hier nochmal kurz die Zusammenfassung: Office Anwendungen speichern die Zugangsdaten in %LOCALAPPDATA%\Microsoft\TokenBroker\Cache
. Hier liegen eine ganze Reihe an Dateien mit der Endung .tbres
. Jede Datei enthält ein JSON-Objekt, die wiederum aus weiteren JSON-Objekten bestehen, unter anderem eines mit dem Namen ResponseBytes
, dass den Wert IsProtected
enthält. Wenn man die Zeichenkette hinter Value
anschaut, erkennt man wieder einen alten bekannten: AQAAAN
.
Das entspricht der Base64-enkodierten Version vom oben genannten DPAPI-Header. Um den Wert im Klartext ausgeben zu können, müssen wir das Skript von eben etwas modifizieren.
Add-Type -assembly System.Security
$data = "AQAAANCMnd8B [...]"
$decodedData = [Convert]::FromBase64String($data)
$decryptedData = [System.Security.Cryptography.ProtectedData]::Unprotect($decodedData, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
$enc = [system.text.encoding]::Default
$enc.GetString($decryptedData)
Die Daten, die dabei rauskommen, sehen zwar etwas weniger strukturiert aus. JWT-Tokens mit dem bekannten Start eyJ0
können darin trotzdem erkannt werden.
Der nächste Ort, an dem Azure-Tokens gefunden werden können, sind alle möglichen Prozesse, die mit Azure interagieren. Am einfachsten kommt ihr an die Tokens, wenn ihr ein Speicherabbild von Prozessen wie teams.exe
und co. erstellt. Dafür könnt ihr beispielsweise den Task-Manager verwenden. Im Anschluss können die Abbilder mit Tools wie strings aus den Sysinternals analysiert werden. Hier könnt ihr einfach nach eyJ0
suchen und schauen, ob die Tokens noch gültig sind.
In Kombination mit findstr
kann das Ganze noch etwas beschleunigt werden.
strings.exe teams.dmp | findstr /i eyJ0eX
So das waren die Orte, die ich finden konnte und die ohne administrative Rechte zugänglich sind. Das Betriebssystem selbst speichert natürlich auch Informationen ab, die für Single-Sign-On in AZure genutzt werden können. Das ist aber Stoff für einen weiteren Eintrag. Für die ungeduldigen unter euch, kann ich folgende beiden Blogs empfehlen: hier und hier.
Damit ihr das ganze nicht händisch machen müssten, habe ich ein kleines Tools dafür programmiert. Das findet ihr hier.
Falls ihr noch Ideen habt, wo JWT-Tokens gefunden werden können, dürft ihr mir gerne eine Nachricht senden. Wie immer gilt, falls ihr Fragen, Anmerkungen oder Verbesserungsvorschläge zu dem Artikel habt, schreibt eine Mail an info@hackmich.net.