HIP19 Writeup - Meet Your Doctor 1,2,3
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.
Meet your doctor 1 - 100 pts
URL: http://meetyourdoctor1.challs.malice.fr
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 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.
It seems we don’t need to authenticate with the mutation signIn
to query Doctor
data.
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
Meet your doctor 2 - 200 pts
URL: http://meetyourdoctor2.challs.malice.fr
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.
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”).
It is strongly recommended to use Firefox
to view the server response as it parses JSON an displays it nicely.
Using GraphQLmap, we got the following structure.
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.
We can query {doctors(options: "{\"patients.ssn\" :1}"){firstName lastName id patients{ssn}}}
, don’t forget to escape the "
inside the options
.
There we go, the second flag was b16cff8b-4a5a-41c8-8545-d9880fd7aae5
.
An asciinema version is available at 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’t 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.
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.
Let’s try a simple NoSQL injection with fields we know such as lastName
.
Since the challenge I added a way to test the regex quickly in GraphQLmap using the GRAPHQL_CHARSET
keyword inside a GraphQL query:
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.
Obviously we scripted the data extraction in Python, the script below will get the last flag : 4f537c0a-7da6-4acc-81e1-8c33c02ef3b
.
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.
I hope you enjoyed the challenges as I did !
Feel free to share the blog post ! :)