Brute forcing 4-digit code
Last updated
Last updated
Using the email tester@hammer.thm
we have found from the enumeration phase, we can explore the reset password feature.
(1) Enter email in the Reset Password page
POST /reset_password.php
(email=tester%40hammer.thm)
NOTE: this request must be performed before attempting to POST the recovery code (refer to step 3)
Response
(2) A GET /reset_password.php
request will be sent.
If the cookie is valid (not expired), we will be presented with a form to enter the 4-digit recovery code.
(3) Send recovery code
POST /reset_password.php
with recovery code and s parameter defined in the request body.
A script exist that automatically logs the user out after a set period of time. The value countdownv
is used as the time variable, which appears to be controlled by the s parameter in our request body:
Response
If we send an expired cookie, we will get the following response:
If the cookie is not expired, we will be able to send our recovery code. The image below shows the display for an invalid recovery code.
(4) Once the window period is over (countdownv <=0
), the cookie will be expired, and the user will be automatically logged out.
Automatic logout: GET /logout.php
This will cause the application to expire our cookie.
Response
Set-Cookie to PHPSESSID=deleted
.
(5) From the Location
header defined in the previous response, a GET /index.php
request with no cookies will be sent, to redirect back to the main login page.
GET /index.php
Response
New PHPSESSID
in the Set-Cookie
Retrieved PHPSESSID: mgj0vc1q4908nel9i695d3mvj8
This value will be sent as a cookie in the subsequent requests
(6) The entire process repeats from step (1) above, with the newly retrieved cookie.
We can control the time period window in the reset password page (/reset_password.php
) with the s parameter. However, this simply prevents the application from immediately expiring our cookie (GET /logout.php
), but does not work by itself when brute forcing the code.
It appears that the application implements a rate limiting feature on the recovery code functionality. For a given PHPSESSID
, we are only allowed 8 attempts within the window period, before it blocks any further requests. To workaround this, we have to retrieve a new PHPSESSID
after every 8 brute force attempts. This can be done by (#1) sending a GET request to /index.php
, with no cookies, before (#2) sending a POST request to /reset_password.php
with the new cookie retrieved.
The flaw is in the way the application handles this process. When we perform step (#1), the application simply associates our new cookie with the email, but fails to generate a new 4-digit code. Thus, this allows us to indefinitely reset our PHPSESSID
session (without the 4-digit code changing), and eventually retrieve the correct code with 100% success rate.
This defeats the purpose of the rate limiting feature.
Python script to brute force the 4-digit recovery code with 100% success rate:
After running the script, we found the valid code!
Now, we have to manually insert the PHPSESSID
value into our web browser cookies. Upon navigating to the reset password page, we can enter the found code, before being able to set a new password.
After logging in with our new password, we are presented with the dashboard page.
With this, we have found the answer to our first question "What is the flag value after logging in to the dashboard?": THM{AuthBypass3D}