HackTheBox — Hacknet
Linux | Dificulty: Medium | Released: 13 Sep 2025

https://app.hackthebox.com/machines/HackNet
https://labs.hackthebox.com/achievement/machine/1948178/727
This machine demonstrates chaining multiple Django-specific weaknesses. The route I used: discover a Server-Side Template Injection (SSTI) in how usernames are rendered, abuse that to leak stored user records (including passwords), log in as a user, then move laterally by tampering with Django’s file-based cache (pickle deserialization) to obtain code execution as another user, and finally analyze a GPG-encrypted backup to escalate privileges.
Overview (attack chain)
Recon → identify webapp (Django) on port 80.
Server-side template injection (SSTI) via an unescaped profile field used inside an HTML attribute. This is used to reveal internal objects (QuerySet dump) containing plaintext credentials.
Use leaked credential (
mikey) → SSH to obtain initial shell.Identify world-writable Django file-based cache (
/var/tmp/django_cache) owned bysandy:www-data.Overwrite a
.djcachefile with a pickle payload (expiry epoch + newline + pickled object) to achieve remote code execution when Django unpickles it — becomessandyshell.Enumerate for GPG keys / backup artifacts to escalate to root.
Recon & enumeration
Port/service fingerprinting

The target is serving HTTP on port 80. Add hacknet.htb to /etc/hosts pointing at the target IP.
Web fuzzing for subdomains and directories
both feroxbuster and ffuf didnt yield anything
These scans didn’t return many surprises, but visiting the site showed a Django-powered webapp (login page).

Sign-up and login as new user to access the application
Application Surface
Browsing the app exposed endpoints typical of a social-like Django app:
/profile— user profiles/profile/edit— change username/profile fields/messages— messaging/contacts— contact list/explore— content discovery/search— search pageGET /like/<POST_ID>— triggers the likeGET /likes/<POST_ID>— returns an HTML fragment that shows who liked a post
Confirming behavior
Register a test account and update your username to a safe test string like {{ 7*7 }}. Then cause the webapp to render the fragment (e.g., like a post) and fetch the fragment:
If the server evaluates {{7*7}} into 49 inside the returned HTML, you have SSTI-capable template rendering. Note: Django’s built-in template engine is safer than Jinja; SSTI usually implies an engine that permits expression evaluation (Jinja) or unsafe usage of mark_safe|safe/render_to_string/Template with untrusted data. In any case, the app is rendering unsanitized input through the template layer.
Exploitation: cause the server to expose internal objects
The app in this exercise renders user objects inside an attribute (e.g., title="..."), and the payload {{users.values}} produced a Django QuerySet dump. To exploit:
Set your profile
usernameto a template payload that prints helpful object internals. Example payloads:{{users.values}}— attempts to showusersvariable values if available{{request.META}}— show request headers/environment{{settings.__dict__}}— (may or may not be available depending on exposure)
Use the
likeendpoint to appear your profile the likers list, then fetch/likes/<id>to trigger rendering.

If the server outputs an object-like representation such as:
then you can parse those values.
Notes:
The
backdoor_banditaccount did not like any public posts, Instead, pivot to thedeepdiveuser first and you'll seedeepdive'sprivate post are being liked bybackdoor_bandit.

Login as deepdive
email: deepdive@hacknet.htb
pass: D33pD!v3r

like the post with {{user.values}} as your username (user your test account)

the observed flaw here despite the UI restrictions of user can't see the private post, any user can still like and render the likes of private post of any user via IDOR.
Parsing the leak via automation
This script assumes you already have session cookies (replace sessionid with yours)

As you notice we didn't include the csrftoken on the script, as this application's flaw is the csrftoken isn't tied to the sessionid and there's no csrftoken validation despite its implementation on client-side.
Initial shell (SSH) and post-exploitation
Using harvested credentials:
Once inside as mikey, standard reconnaissance:
sudo -l didnt yield anything

Django cache discovery & analysis
Look for common Django cache locations:
Check permissions and ownership:
Key observation: 777 and owned by sandy:www-data → any local user can create/modify .djcache files that Django might later unpickle.
Crafting a pickle payload
Payload generator:
Execute this on mikey's machine (replace with your pickle payload)
On attacker:
Trigger Django to load the cache (e.g., hit
/exploreor relevant page):
or simply reloading the http://hacknet.htb/explore
If successful, the unpickle will execute and the attacker listener will receive a reverse shell as the process user (often www-data or, depending on how the app is run, the system user — in this machine it becomes sandy).
Privilege escalation. GPG and encrypted backups
Once you have a shell as sandy, search for private keys and backups:
Transfer backup02.sql.gpg and armored_key.asc to your local machiine via python server
GET R00T
Last updated