This post documents the complete walkthrough of Sink, a retired vulnerable VM created by MrR3boot, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post

Background

Sink is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

masscan -e tun0 -p1-65535,U:1-65535 10.10.10.225 --rate=500
Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2021-02-10 08:30:28 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 22/tcp on 10.10.10.225
Discovered open port 3000/tcp on 10.10.10.225
Discovered open port 5000/tcp on 10.10.10.225

Nothing unusual stood out. Let’s do one better with nmap scanning the discovered ports to establish their services.

nmap -n -v -Pn -p22,3000,5000 -A --reason 10.10.10.225 -oN nmap.txt
...
PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
3000/tcp open  ppp?    syn-ack ttl 63
| fingerprint-strings:
|   GenericLines, Help:
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest:
|     HTTP/1.0 200 OK
|     Content-Type: text/html; charset=UTF-8
|     Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
|     Set-Cookie: i_like_gitea=029412539056da57; Path=/; HttpOnly
|     Set-Cookie: _csrf=wDZf6EYuO6ahzcpdgUTA7lI_21s6MTYxMzEwNzk1Mjg5MzMxNjkzNg; Path=/; Expires=Sat, 13 Feb 2021 05:32:32 GMT; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Fri, 12 Feb 2021 05:32:32 GMT
|     <!DOCTYPE html>
|     <html lang="en-US" class="theme-">
|     <head data-suburl="">
|     <meta charset="utf-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title> Gitea: Git with a cup of tea </title>
|     <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|     <meta name="theme-color" content="#6cc644">
|     <meta name="author" content="Gitea - Git with a cup of tea" />
|     <meta name="description" content="Gitea (Git with a cup of tea) is a painless
|   HTTPOptions:
|     HTTP/1.0 404 Not Found
|     Content-Type: text/html; charset=UTF-8
|     Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
|     Set-Cookie: i_like_gitea=2ee2f644cf40c4ca; Path=/; HttpOnly
|     Set-Cookie: _csrf=UA4Fmja9ShJ3SYAytyqCSEHq1lk6MTYxMzEwNzk1Nzk0OTcyMTc5NQ; Path=/; Expires=Sat, 13 Feb 2021 05:32:37 GMT; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Fri, 12 Feb 2021 05:32:37 GMT
|     <!DOCTYPE html>
|     <html lang="en-US" class="theme-">
|     <head data-suburl="">
|     <meta charset="utf-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title>Page Not Found - Gitea: Git with a cup of tea </title>
|     <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|     <meta name="theme-color" content="#6cc644">
|     <meta name="author" content="Gitea - Git with a cup of tea" />
|_    <meta name="description" content="Gitea (Git with a c
5000/tcp open  http    syn-ack ttl 62 Gunicorn 20.0.0
| http-methods:
|_  Supported Methods: OPTIONS POST HEAD GET
|_http-server-header: gunicorn/20.0.0
|_http-title: Sink Devops

Looks like we have two http services: 3000/tcp and 5000/tcp. This is what they look like.

3000/tcp

I uncover three usernames while cursorily exploring the site.

5000/tcp

This site offers a way to sign up for an account.

I got redirected to /home immediately after signing up.

I’d better include map 10.10.10.125 to sink.htb in /etc/hosts.

Sink Devops

This site has a Comment section in the home page to leave a comment and to delete that comment if you so desire. In addition, there’s also a Notes section to save, view and delete notes.

Be it posting a comment or saving a note, there’s something interesting with the response if you look closely.

The site is served from Gunicorn 20.0.0 via HAProxy (likely in a reverse proxy capacity), i.e. the backend server behind HAProxy is Gunicorn.

HAProxy HTTP request smuggling (CVE-2019-18277) with Gunicorn 20.0.0

According to this article,

To actually exploit HTTP smuggling using the issue described in this writeup, the backend server(s) behind HAProxy would also have to be vulnerable in the sense they too would need to suffer from a bug, but one which parses and accepts a poorly formed Transfer-Encoding header (almost certainly violating RFC7230). The backend must also support HTTP keep-alive.

According to synk, Gunicorn 20.0.0 is also vulnerable to HTTP request smuggling.

Affected versions of this package are vulnerable to HTTP Request Smuggling. It fails to properly process the Transfer-Encoding and Content-Length headers when both are present in a package request. This allows for conflicting information to be sent regarding the length of the package, which when processed by back-end servers under certain configurations would allow for malicious users to bypass security controls, gain unauthorized access to sensitive data, and directly compromise other application users.

Perfect. Taking a leaf from the Turbo Intruder script in the article, we can test for HTTP request smuggling with Burp Suite’s Repeater. The only thing to take note is that the backend server must support HTTP keep-alive.

This is the HTTP request that I’m going to send.

Hit Ctrl-Shift-U to do a URL-decode on the highlighted %0b to turn it into a vertical tab character. The following shows Burp Suite Repeater displaying the non-printable characters.

The idea is to smuggle the second POST request (line 20 onwards) with the hope that the HTTP connection is reused by someone, say, the Administrator. Note that the Content-Length of the second request is 300-bytes more than 4-bytes (msg=). If the HTTP request smuggling is successful, we should see request headers posted as comments like so.

This must be Administrator’s session!

Administrator’s Session

Once we replace our session cookie with the Administrator’s, refresh http://sink.htb:5000/home. And we should see ourselves logged in as Administrator.

Administrator’s Notes

Administrator has three notes.

Note 1

Note 2

Note 3

Gitea 1.12.6

Recall that Gitea had three users? The credential (root:[email protected]>Z3})zzfQ3) works for root.

Voila! We have an archived repository. I’m sure something juicy resides in there!

Marcus’ SSH Private Key

In commit b01a6b7ed3, we have a SSH private key!

If I had to guess, I would say that’s marcus’ private key.

Bingo. The file user.txt is at marcus’ home directory.

Privilege Escalation

During enumeration of marcus’ account, I notice the presence of another user account david.

From marcus to david

We need to look for david’s password. How do I know that? Look at /etc/ssh/sshd_config.

If david can’t SSH in, then surely we need to use su. Simple as that.

aws secretsmanager

What better way to keep a secret than using AWS Secrets Manager? To list the secrets stored in the Secrets Manager we can use AWS CLI like so. But in order to do that we need to run aws configure first. The required information pertaining to marcus such as endpoint, key, secrets, region, etc can be found in the Gitea Log_Management repository. You just need to know where to look. Hint: it’s in one of them commits.

Armed with this information, we can run aws configure like so.

Note that all this information is stored in the .aws directory. Once that’s done, we can list-secrets like so.

And then get-secret-value like so.

Looks like david’s password is EALB=bcC=`a7f2#k.

aws kms

During enumeration of david’s account, I noticed the presence of server.enc, which appears to be an encrypted file.

AWS Key Management Service (AWS KMS) is an encryption and key management web service. Perhaps we can use it to decrypt the file? Check this out in one of the commits in Key_Management repository.

It appears david is able to encrypt and decrypt. But first, let’s do a list-keys first.

We can reuse the marcus’ information in the .aws directory.

There are so many keys, which one is the encryption/decryption key? To that end, I’d to use a little Linux-fu.

There’s only one way to find out.

I think we are done here.

:dancer: