Target:
HackNet (HTB)Date: 2025-09-17
I found a Server-Side Template Injection (SSTI) in the SocialNetwork app that leaked other users’ credentials. I used one of those accounts to SSH in, discovered a Django cache file write vector to get a reverse shell as
sandy, recovered .gnupg keys and encrypted backups, cracked the GPG private key with john, decrypted backups, found credentials to escalate to root, and captured the root flag. 🎯nmap- Browser (web UI)
ssh,nc(listener)tar,scpgpg,gpg2john,john- Linux utilities:
grep,su, etc.
Screenshot of the
nmap output used to start enumeration:
Registered a user and inspected the web app (Login / Signup / Edit Profile):

Edit profile page where username field is editable:

Test payload
{{7*7}} reflected but initially filtered:
Checked where the username is reflected on the site:

Likes UI — reflection point for usernames:

Tried again — {{7*7}} reflected & executed but got blocked (blacklist evidence):

Switched to {{users.values}} — SSTI executed and returned user objects (leak):

Collected ~24 accounts by repeating this on multiple posts/pages. One user (id 18) was missing from like lists:

Profile/18 is private (no public likes):

Sent a contract request from our account to the private user, then used SSTI to extract the private user's credentials via a mutual connection/post:

SSTI leaked the user's email & password:

Logged into the web app with recovered creds:

Accepted the contract request from the compromised user:

Can't edit that user's username because 2FA blocks it (screenshot):

From our main account we found a post liked by the target — used the SSTI there to leak the target's credentials:

SSTI steps and successful leak:

Leak confirmation (credentials shown):
![]()
Tried SSH with the leaked credentials (failed), then tried mikey and succeeded:

SSH success as mikey (we have a shell):

Found web project at
/var/www/HackNet/SocialNetwork/ and saw a `sandy` file in home — target user is likely sandy:
Observed Django cache behavior: visiting /explore?page=1 creates cache files (one looked empty initially):
![]()
After refresh the cache files appear:

Django cache files visible on disk / app path:

Created a cache-named file containing a reverse shell payload (placed in the web directory) to be executed when the cache is rendered:

Ran the script to write the file on target:

Confirmed file exists in target dir:

Refreshed the web page; reverse shell hit our listener — got a shell as sandy:

As
sandy found /home/sandy/.gnupg/ with private keys:
Found encrypted backup files (*.gpg etc.):

Packed .gnupg and backups, exfiltrated to attacker for local cracking:

Downloaded archive locally and tried to extract / decrypt: got prompt that passphrase is required:

GPG decryption requires passphrase (we'll crack the private key offline):

Created a directory with the private key files and converted the private key to a john hash with
gpg2john:
Cracked the hash with john (rockyou + rules) and obtained the passphrase:
![]()
Recovered passphrase visible in john output:

Used passphrase to decrypt backups locally:
![]()
Searched decrypted backup contents for secrets (grep):

Found a password inside the backups that allowed privilege escalation:
![]()
Used the discovered password to
su to root and captured the root flag:
- SSTI exposure — sanitize/escape user input, disable template evaluation of user-controlled fields, use template sandboxing.
- Sensitive data in backups — never store plaintext credentials in backups; use proper key management and rotate keys.
- Django cache file safety — store cache files in non-web-executable directories and apply strict file permissions.
- 2FA & account protection — enforce 2FA for all high-privilege actions and monitor for suspicious activity.
If you want, I can:
- Shrink this to a one-page summary with the key images only.
- Output a README.md (GitHub-ready) with collapsible sections.
- Export a downloadable PDF.