Holiday
Gaining Access
Nmap scan:
$ 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-altDetailed 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: ErrorAdded 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.
Visiting /admin redirects me to the /login page:

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.
This produced users,sqlite_sequence,notes,bookings,sessions,sessions. Now, I can enumerate the columns in 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 JS file only had this line:
However, I never received a callback, and that's when I realised it was not reflected XSS, but rather stored:

The payload is reflected directly on screen. I copied and pasted a TON of payloads, then took a look at how the filtering worked.
I noticed that it kept <img> tags in tact:
It spaced out the javascript, but it also kept ` characters in tact.
I also noticed this part here:

If the javascript link was within a ` character, it was left alone. The original payload was:
So it removed spaces, and the entire XSS portion there, thus my final payload cannot contain spaces. The filter also removed all ' characters.
This payload made it past the string filter but I don't think it works since it doesn't use script tags or anything:
I eventually gave up finding this payload, and I checked the solution. The actual payload was:
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.
The final payload would thus be:
This finally works and I got callback:

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:
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:

Privilege Escalation
Sudo Npm -> Root
This user had sudo privileges:
This can be exploited using the script on GTFOBins:

Rooted! Super hard start.
Code Vulnerabilities
I took a look at the vulnerabilities for both the SQL Injection, XSS and RCE within the /home/algernon/app/setup directory.
XSS
Here's the XSS code:
Logs in each time, and then calls evaluate to load the elements, thus allowing me to execute Javascript on this.
The XSS whitelist was within router.js:
Seems that it only allows <img src>, and it replaces certain characters with empty strings except for ` characters.
SQL Injection
Now I can see why ")) was a valid entry point, and why password was not vulnerable.
RCE
The /admin/export directory passed the table parameter to exec, pretty self-explanatory. The regex filter was quite weak as well.
Last updated