Capture - TryHackMe
Can you bypass the login form?
This machine, named Capture!, is part of TryHackMe and rated as an easy challenge. It offers a great opportunity to practice fundamental web enumeration and brute force techniques while dealing with common protections like captchas. Throughout this challenge, we’ll explore how to identify valid usernames, automate captcha solving, and brute force passwords to gain access. Let’s dive in and walk through the process of capturing the flag step-by-step!
You can find the full Python script used in this write-up at the following link : capture_thm.py
Initial enumeration
We begin with a full port scan in order to see what services are exposed :
1
nmap capture.thm -sV -sC -Pn -p- -oN nmapres
Results :
The scan reveals two open ports :
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)- `80/tcp open http Werkzeug httpd 2.2.2 (Python 3.8.10) http-title: Site doesn’t have a title (text/html; charset=utf-8).
Next, we can perform a directory enumeration using gobuster:
The scan reveals that we can access to the login and home page.
Then I try to send a HEAD request to the target domain using :
1
curl -I capture.thm
This allowed me to inspect the HTTP headers without downloading the full page content.
The server responded with :
This tells us a few things :
-
302 Found → The page redirects to another location (temporary redirect).
-
Werkzeug/2.2.2 Python/3.8.10 → The backend is running on Python, most likely using the Flask framework (Werkzeug is Flask’s underlying WSGI server).
-
Location: /login → Any unauthenticated request is sent to the login page.
At this point, it’s clear we’re dealing with a Python/Flask web application, and authentication might be the first barrier to bypass.
After identifying the /login endpoint and noticing that the application explicitly tells us when a username does not exist
Error: The user 'test' does not exist
I decided to use Hydra to enumerate valid usernames.
The idea here is simple :
- If the server returns that exact error message → the username is invalid.
- If that error is missing → we probably found a valid username.
- Hydra’s
http-post-formmodule requires three parts separated by colons::
- Path to the login form →
/login - POST data with placeholders →
username=^USER^&password=^PASS^ - Failure condition → a string from the server response that means “login failed”
Here’s the command I used :
1
hydra -L /usr/share/wordlists/SecLists/Usernames/xato-net-10-million-usernames.txt -p fakepassword capture.thm http-post-form "/login:username=^USER^&password=^PASS^:Error\: The user" -t 10
After many requests, the application activated a captcha system :
At this point, all Hydra requests failed because it can’t solve captchas. The earlier list of usernames likely included false positives caused by the captcha page replacing the normal login error.
Weaponization
To continue the attack, a different approach was needed one that could:
- Detect when the captcha is present
- Parse the mathematical challenge (e.g.,
418 * 84) - Solve it and submit the result with the login attempt
Using Python with the requests and re modules, I wrote a script that:
- Submits each username with a dummy password
- If a captcha appears, extracts both numbers using a regex :
1
test_captcha = re.search(r"(\d+)\s*([+\-*/])\s*(\d+)", text)
This finds any number (operator) number pattern, ignoring spaces.
-
Computes the product, resends the form including the captcha answer
-
Checks if the “user does not exist” message is absent — indicating a possible valid username
This script avoids the limitations of Hydra and can adapt to the captcha challenge, allowing the enumeration to continue despite the protection.
Exploitation
During the enumeration, we get output similar to this :
As we can see, the script correctly identifies <REDACTED> as a possible valid username because the login response for <REDACTED> does not contain the “user does not exist” error, unlike all the others.
Thanks to this approach, we bypass the captcha and avoid lockouts, enabling effective username enumeration.
When trying a wrong password for a valid user, the application responds with a different error message :
1
Error: Invalid password for user <REDACTED>
This distinct response allows us to differentiate between a non-existent user and a wrong password for an existing user.
To leverage this, we need to adjust our script so that once a probable username is found, it switches from enumerating usernames to brute forcing passwords specifically for that user.
The script should still handle the captcha challenge during each login attempt, as the protection remains active throughout the process.
By doing so, we will be able to systematically test passwords against the valid username until the correct password is discovered.
After updating the script we can see the script outputs feedback on each password attempt :
Here, each [-] Invalid password message indicates the password was incorrect. Finally, after trying <PASSWORD>, the script detects a successful login :
1
2
3
############################################################
### Password FOUND for user '<USERNAME>': '<PASSWORD>' ###
############################################################
Flag
Once the valid credentials were found, we tested this combination on the login page on the login page :
Successfully logging in granted us access to the final page where we retrieved the flag, completing the challenge.
Congratulations! I hope you found this write-up insightful. This CTF challenge was an excellent exercise that emphasized the importance of understanding web application defenses, especially how captcha protections can be bypassed with a bit of clever scripting. By carefully analyzing server responses and automating requests with captcha solving, we were able to enumerate valid usernames and subsequently brute force passwords to gain access.
This challenge was a great reminder that even basic protections like captchas can sometimes be circumvented if you think outside the box and automate effectively. Beyond just manual testing, scripting becomes essential in penetration testing to handle repetitive tasks and dynamic defenses. Ultimately, this methodical approach led us to successfully retrieve the flag.
Thanks for reading, and happy hacking!