Last wednesday I was in the Hack In Paris event for the 3rd time. As always there were some great conferences and challenges, and a new competition called "Hacker Jeopardy" which was very fun! During the Wargame I focused my time on Web challenges based on the `graphql` technology which was new to me, you will find below my writeups for the `Meet Your Doctor` challenges.
Goal : `Please help me find the Meet your doctor Admin password`
Gobuster revealed a `/graphql` endpoint at the root of the website, browsing [http://meetyourdoctor1.challs.malice.fr/graphql](http://meetyourdoctor1.challs.malice.fr/graphql) gave us an access to an interactive GraphQL interpreter. On this page we can browse the documentation and `schema`.
There, we can get the structure of the `Doctor` type.
A simple query `{Doctors{id email password}}` was enough to extract all doctors' email and password. The flag was the password of the Admin doctor : `Now-_Let$|GetSeri0us`
Goal: `I need to find the Patient 0 social security number! It is a matter of life and death`
In this challenge we didn't have access to an online "lab" to test and query the GraphQL. I made a tool to interact with the `/graphql` endpoint, it is open-source and available for free on [Github: https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap).
Using `Introspection` we can still get the GraphQL schema, here is the URL-encoded version (Full version available at "[GraphQL Injection: enumerate-database-schema-via-introspection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/GraphQL%20Injection#enumerate-database-schema-via-introspection)").
In this case `Doctors` have an `options` parameter which accepts JSON. It allows us to specify items linked to the doctors' structure and project their data into the GraphQL response. I saw the following picture in a blog post, it helped me a lot to understand the structure of the query.
GraphQLmap> {doctors(options: "{\"patients.ssn\" :1}"){firstName lastName id patients{ssn}}}
{
"firstName": "Admin",
"id": "5d089b25f48c29003191e3b5",
"lastName": "Admin",
"patients": [
{
"ssn": "b16cff8b-4a5a-41c8-8545-d9880fd7aae5"
}
]
}
{% endhighlight %}
There we go, the second flag was `b16cff8b-4a5a-41c8-8545-d9880fd7aae5`.
An asciinema version is available at [https://asciinema.org/a/sbqGXnWXl5Y6YeQr0wzsCzQli](https://asciinema.org/a/sbqGXnWXl5Y6YeQr0wzsCzQli)
## Meet your doctor 3 - 300 pts
URL: `http://meetyourdoctor3.challs.malice.fr`
Goal: `I need to find the Patient 0 social security number! It is a matter of life and death`
Things are getting complicated, we can dump the schema as the `Introspection` is set to false. Based on the previous challenge we can guess the structure is the same. Unfortunately we can't reuse the same query to extract the `ssn`, we get the following error.
Field "doctors" argument "search" of type "JSON!" is required, but it was not provided.
{% endhighlight %}
This means there is a `search` argument in the doctor structure, something like `doctors[Doctor]: options (JSON!), search(JSON!)`. Pete Correy explains how to exploit the search argument in his blog post [GraphQL NoSQL Injection Through JSON Types](http://www.petecorey.com/blog/2017/06/12/graphql-nosql-injection-through-json-types/).
Let's try a simple NoSQL injection with fields we know such as `lastName`.
The injection worked, now we can re-use the payload from the challenge #2 and extract the `social security number` of the patient 0, which is the patient of the `Admin` doctor. The query was as follows, where we had to replace `CHARACTER_FROM_THE_FLAG` to test characters from the flag.
At that time we were checking if the content of `r.json()['data']['doctors']` was not empty, in order to abstract the data extraction we now take a check input from the user in order to compare the output.