Hammer
Use your exploitation skills to bypass authentication mechanisms on a website and get RCE. - by 1337rce
Last updated
Use your exploitation skills to bypass authentication mechanisms on a website and get RCE. - by 1337rce
Last updated
The following post by 0xb0b is licensed under CC BY 4.0
Always question your assumptions and never assume anything that you have not tested.
We start with a Nmap scan and find two open ports. On port 22
we have SSH and on port 1337
we have an Apache web server.
Since our entry point is probably the web server, we scan for possible directories and pages using Feroxbuster while enumerating the target manually.
We find some pages and directories. Among them PhpMyAdmin. So we are dealing with a PHP web server. Apart from these, however, nothing else, except that the CSS folder looks a bit strange.
Visiting the index page by manual enumeration takes us directly to a login page.
In the source, we find the named convention of the directories. These start with hmr_
.
So we edit the used wordlist by prepending hmr_
and scan again.
We now find a directory hmr_logs
, which has directory listing activated. This directory contains an error.logs
file.
With the information we have gathered so far, we should now concentrate on the login.
This only displays a generic message for the email and password entered, from which we cannot conclude that an incorrect email or password has been entered. A pure brute force to enumerate the email is therefore not possible here.
But the login page has a link to a forgot password feature /reset_password.php
. This gives an error message if the chosen mail is wrong, theoretically a valid mail could be enumerated in this way.
Recalling the enumeration using the cusomized wordlist we are able to spot an email in the error.logs.
There is an authentication failure for the user tester@hammer.thm
.
When trying to reset the password for this user, ...
... the paged refreshes and we have to enter a 4-digit code to change the password. Furthermore, there is a time limit of 180
seconds to enter this code.
For the further procedure and analyzing, we intercept the submitting of the 4-digit code using burp suite.
With every request that is now made, the Rate-Limit-Pending value in the response header is reduced. Initially this starts at 8
.
After the value drops to 0
, the rate limit is reached and the token cannot be reset. At this point I lost a lot of time because I thought that with every reset the token would also be reset. Under this assumption, I thought I could only get a token with a bit of luck and chance.
Therefore, I wrote a script that makes 100
requests at the same time with different PHPSESSID
s in the hope of getting a valid reset with a fixed reset token. In fact, after several attempts I had a valid request token, but 100
identical response, for each session the fixed token was valid.
Only then did I realize that the token endures in that time frame over every session created, and does not reset itself with a new session. The assumption could be made by seeing that a token endures 180
seconds.
To verify that the reset token endures, we request a new reset without a cookie to get a new session.
Then we put the PHPSESSID
from the response into our request, and see that we have 8 attempts again, until the 180
seconds have passed.
With the information we have, we are able to automates the process of brute-forcing a password recovery. It first requests a password reset and retrieves the PHPSESSID
cookie, then iteratively submits recovery codes in a brute-force manner, periodically refreshing the PHPSESSID
every seventh request. The script detects a successful code submission by checking for a change in the response text's word count.
After we have run the script, we receive the valid recovery code, the PHPSESSID
and the response body.
All we have to do now is set the PHPSESSID in the browser and reload the page.
After we have reloaded the page, we can reset the password for the user tester@hammer.thm
.
We choose a new password.
We then log in with the new credentials ...
... and are forwarded to the dashboard. We see that we have the role user
, can enter commands and are greeted with the first flag. After a short time, we are logged out.
First we look at what lets us log out, in the source we see a script that checks the cookies after an interval and if the condition is not met, we are logged out. If persistentSession
is not set to True, we will be logged out. Using the OWASP ZAP tool, we can set this value permanently, but we can also continue our investigation using Burp Suite without being logged out.
Furthermore, there is a script that listens for a click event on the #submitCommand
button and retrieves a command input by the user. It then sends an AJAX POST request to execute_command.php
, including the command and a JWT token in the request headers for authorization. Upon receiving a response, it displays the result or an error message in the #commandOutput
element. This script is responsible for the command transmission.
We intercept the request to transfer the command using Burp Suite. We see the token in the header and in the cookie. Furthermore, we are not allowed to execute the ID command. We use FFuF with a word list to check which commands can be used.
It seems that we can only execute the ls command. Besides the pages and directories we already know there is a .key
file present. We remember that our user role was displayed in the dashboard. It is possible that other roles can execute more.
We analyze the JWT token using jwt.io
and can make out the structure, in the header a kid
is set, that points to a key file located at /var/www/mykey.key
. Furthermore the token contains the role user. Maybe with another role like admin we would be able to execute arbitrary commands.
We recall the listing of our ls
command, here we had a key file. The key file contains a hash value. Possibly the secret for signing a JWT token. So we can probably craft our own token, since we have access to the secret and can guess the location of the token for the kid.
Let's create an admin token with a structure like this:
The first token we create is for the role user we already know, to confirm that our self-created token works. However, this is not shown below.
We use a python script to create a token with admin role, we enter content line 4
and path of the secret line 10
. We also set the expiry date a little higher for us.
Running the script, we get a token, signed with the secret, located in the web root folder.
It is possible that the brute force takes longer than the 180 seconds that the token lasts. Therefore, the script may not necessarily find the valid token during its execution. Another attempt must then be made.
Using jwt.io
, we are able to confirm its new content.
Next, we replace the token value in the Authorization header and token cookie value. After that, we are able to execute arbitrary commands as admin. Using ID we see, we are www-data
.
As www-data
we are able to retrieve the second flag at /home/ubuntu.flag.txt
.
In this challenge we faced a vulnerable web application on an Apache server. An Nmap scan identified SSH on port 22
and a web server on port 1337
. After directory scanning and manual enumeration, we discovered a PhpMyAdmin page and a hmr_logs
directory containing an error.logs
file. The logs revealed a valid email (tester@hammer.thm
), which we used to exploit the password reset feature.
The password reset mechanism was vulnerable to brute-force attacks, as it allowed multiple attempts to guess the 4-digit reset code within a time limit, bypassing its rate limit by retrieving a new session every 7ths request. By automating the brute-force process and circumventing rate limits, we successfully reset the user's password. After logging in, we got the first flag and analyzed and manipulated the JWT token to escalate our privileges to admin
, enabling arbitrary command execution as www-data
and retrieving the second flag at /home/ubuntu.flag.txt
.
As a little bonus, we take a look around on the system after receiving the RCE. We set up a listener and get a reverse shell using busybox.
Next, we upgrade our shell and run linpeas.sh
.
We are able to find some database credentials ...
... and take a small peek.
Furthermore, we are able to retrieve the secret used by the application to sign the JWT token.
Unfortunately, a successful execution of the following exploit did not work.