| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | GraphQL API Vulnerabilities |
| Difficulty | Practitioner |
| Objective | Locate a hidden GraphQL endpoint, bypass its introspection filter using a newline injection, discover the deleteOrganizationUser mutation, and delete carlos |
Finding a Hidden GraphQL Endpoint¶
Browsing the app generated no GraphQL traffic — the endpoint wasn't linked from any page. Probing common paths, navigating to /api returned:
"Query not present"
"Query not present" is the standard GraphQL response to a non-GraphQL request — the confirmation we needed. Testing with a parameter:
/api?query=teto
"Invalid syntax with offending token 'teto' at line 1 column 1"
A GraphQL parse error confirms both the endpoint location and that queries are being processed. A hidden endpoint is not a secure endpoint — it was still accessible to anyone probing common paths, and the mutations it exposed had no access control.
Loading Burp's full introspection query via the GraphQL tab:
{
"errors": [
{
"message": "GraphQL introspection is not allowed, but the query contained __schema or __type"
}
]
}
The server uses a regex or string match blocking __schema in the query. Inserting a URL-encoded newline (%0a) between __schema and the opening brace breaks the regex while remaining valid GraphQL syntax — the parser handles __schema{ and __schema\n{ identically. Any regex-based keyword filter can be defeated by whitespace or newlines that GraphQL ignores; the only reliable way to disable introspection is at the library level:
GET /api?query=query+IntrospectionQuery+%7b%0a++++__schema%0a...
Introspection succeeded. Saving the response to the site map for structured browsing:
The site map revealed two relevant operations: a getUser query and a deleteOrganizationUser mutation. Sending getUser with incrementing IDs to find carlos:
id: 3 → "username": "carlos". Sending the mutation:
mutation($input: DeleteOrganizationUserInput) {
deleteOrganizationUser(input: $input) {
user {
id
username
}
}
}
{"input": {"id": 3}}
Lab solved...