漏洞编号:CVE-2023-23752
攻击者可以通过构造特制请求进而访问RestAPI接口,通过变量覆盖漏洞绕过鉴权,从而获取Joomla相关配置信息。
https://downloads.joomla.org/cms/joomla4/4-2-7/Joomla_4-2-7-Stable-Full_Package.zip?format=zip
http://servername/api/index.php/路由路径?public=true
/api/index.php/v1/config/application?public=true
:此API用于获取网站最重要的配置信息,其中包含数据库的账号与密码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
/api/index.php
/api/index.php
下个断点开始debug/api/includes/app.php
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();
}
}
}
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()
的变量覆盖特性$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
,将不会进行身份认证变量覆盖漏洞的修复方式比较简单,如果是覆盖已有变量造成危害,则在存在变量覆盖的函数前判断变量是否已经存在
如果像这种,是因为变量覆盖新增了一个用于鉴权的变量,则在变量覆盖函数之后,将对应的变量释放掉即可
可以参考官方的修复方式:
https://github.com/joomla/joomla-cms/compare/4.2.7...4.2.8
可以看到,在存在变量覆盖的路由解析方法调用之后,unset
掉了用于鉴权的变量,这样就无法通过变量覆盖绕过鉴权了
当然,也可以更新到漏洞修复后的版本:https://github.com/joomla/joomla-cms/releases/tag/4.2.8
参考链接:
本文作者:Timeline Sec
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/198104.html