top of page

GraphQL CSRF Demo

Updated: Jul 5, 2024

GraphQL CSRF logo.

In this blog:

➡️ Find my blog on exposing private GraphQL endpoints here.

Exploring POST requests through Burpsuite

Open up Burpsuite and start the Chromium browser.

Load up the lab which will allow us to perform CSRF exploits over GraphQL.

Opening Burpsuite.
Open up Burp and the built-in Chromium browser

Sign into the account using the given credentials (Wiener:Peter). We can now change the email address for this user. 

Signing into the account.
Signing into the account

Checking Burpsuite shows us the GraphQL requests being made to the database, to retrieve a blogpost from the website.

Burpsuite.
Checking Burpsuite requests made to the database

Let’s check another GraphQL request.

We see here a mutation request, allowing us to log into our account.

Mutation request.
Mutation request to log in

Playing with variables


Another mutation request allowing us to change our user email address.

This is the request which we will be using as part of our attack.

Mutation request.
Mutation request to change email

Right click the request and send to Repeater.

Send to Repeater.
Sending the request to Repeater

Inside the Repeater tab, if we go over into the GraphQL tab, we can see that the request simply uses variables to change the email address based on user input and inserts it into the ‘email’ variable.

GraphQL tab.
GraphQL request for email change

It doesn’t matter whether we have “$input” or the actual value in the request body, as GraphQL will accept either method. Lets make it easier for ourselves, and modify the query to have no variables and take our updated email address as the value.

If we use this modified query and hit send, we will receive a 200 OK message. The email has been changed just like before, but with a variable-less query.

Modified query
Modified query

'Content-Type'


Going back to the raw request, we can see the content-Type is ‘application-json’. Application/json separates key value pairs using colons and commas.


We can change the content-Type and insert our own queries which we can then run to make the server do different things.

Changing the content-type.
Changing the content-type.

Right-click the left pane and click ‘change request method’.

The content-Type changes to application/x-www-form-urlencoded.

Changing request method to 'application/x-www-form-urlencoded'.
Changing request method to 'application/x-www-form-urlencoded'.

When running this request, we get a ‘Query not present’ message. This means that there is a GraphQL endpoint accepting queries but this isn’t a supported one. We must change the request to fit the ‘urlencoded’ format.

Discovering a functional GraphQL endpoint.
Discovering a functional GraphQL endpoint.

Copy and paste the modified GraphQL query from before, highlight it and click ‘control+U’ to url-encode the selection. When we run the request, we get a 200 OK. It works.

URL encoding the query.
URL encoding the query.

Crafting the payload


The server is accepting different content types and there is no CSRF token present. We know that it may be vulnerable to cross-site request forgery attacks. Let’s craft a malicious HTML script and send the request to the server to change anyone’s email address.


Start typing the HTML script. We need a ‘form action’ tag which holds the URL of the request page that we sent to the Repeater in Burpsuite, with the method ‘POST’. Inside this form tag we also need three input tags. I have named the first input tag ‘query’, and its value will be the mutation query from before. To encode it in HTML so that the server can understand it, we will use the decoder function in Burpsuite.

Start of the HTML payload.
Start of the HTML payload.

Go to the decoder tab and paste in the query. Encode it as html.

Encoding our script as HTML.
Encoding our script as HTML.

Copy and paste the encoded query into the value for the query input tag in our html script. Add ‘type=”hidden’ at the end.

Adding the value for our query input tag.
Adding the value for our query input tag.

inside the request variables, copy the variables and paste it into the decoder.

Copy the variables into the decoder.
Copy the variables into the decoder.

Encode this selection as html like before.

Encoding the variables as HTML.
Encoding the variables as HTML.

Copy and paste this into the value section of our third ‘variables’ input tag.

We also need to add an input tag named ‘OperationName’ which will be for our mutation query. The value for this will be ‘changeEmail’.

At the end of the value section of each input tag, add the ‘type=”hidden’ message. 

Building our HTML payload.
Building our HTML payload.

Finally, add a script tag with ‘document.forms[0].submit():’ which basically just runs the script as soon as we send it to the victim’s server.

Adding the immediate run condition.
Adding the immediate run condition.

Deliver the exploit


Once you have finished the script, click ‘store’ at the bottom.

Now click ‘Deliver exploit to victim’.

If nothing happens, it may be because the email address you passed as the value for the variable is the same as before. You will need to change this to something else.

 

Simply go to the decoder again and change the email to anything else, encode it as html and paste It into the variables tag value in place of the old value.

Changing the email and re-sending the payload.
Changing the email and re-sending the payload.

Store and deliver exploit to victim again.


Lab complete!

Thank you for reading!



Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
  • GitHub
  • Twitter
  • LinkedIn
bottom of page