HackTheBox — Hacknet

Linux | Dificulty: Medium | Released: 13 Sep 2025

https://app.hackthebox.com/machines/HackNetarrow-up-right

https://labs.hackthebox.com/achievement/machine/1948178/727arrow-up-right

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)

  1. Recon → identify webapp (Django) on port 80.

  2. 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.

  3. Use leaked credential (mikey) → SSH to obtain initial shell.

  4. Identify world-writable Django file-based cache (/var/tmp/django_cache) owned by sandy:www-data.

  5. Overwrite a .djcache file with a pickle payload (expiry epoch + newline + pickled object) to achieve remote code execution when Django unpickles it — becomes sandy shell.

  6. 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 page

  • GET /like/<POST_ID> — triggers the like

  • GET /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:

  1. Set your profile username to a template payload that prints helpful object internals. Example payloads:

    • {{users.values}} — attempts to show users variable values if available

    • {{request.META}} — show request headers/environment

    • {{settings.__dict__}} — (may or may not be available depending on exposure)

  2. Use the like endpoint 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_bandit account did not like any public posts, Instead, pivot to the deepdive user first and you'll see deepdive's private post are being liked by backdoor_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)

  1. On attacker:

  1. Trigger Django to load the cache (e.g., hit /explore or 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