This website uses cookies

To provide the highest level of service we use cookies on this site.
Your continued use of the site means that you agree to their use in accordance with our terms and conditions.

Pentest Chronicles

Server shutdown via GraphQL during real-life pentest

Kamil Jarosiński

February 19, 2024

GraphQL is a query language and environment created by Facebook in 2012 and released publicly in 2015. However, it has only gained significant popularity among developers and organizations in the last few years. Why is it so popular? GraphQL serves as an alternative to traditional API protocols, like REST, offering a more flexible and efficient way for client-server communication.


The emergence of new technology opens up new perspectives and solves some problems, but unfortunately, it also introduces threats. This is the case with GraphQL. If used without proper knowledge, it could potentially allow for a DoS (Denial of Service) attack.

Rememeber!
This article describes just one of the many attack scenarios against GraphQL. It's crucial to remember that various threats are associated with this technology. Attacks can take different forms, and security considerations must cover multiple aspects.

Introspection Mechanism

The introspection mechanism allows GraphQL to self-analyze and dynamically read metadata about its schema. This enables exploration of available types, fields, relationships, and other schema information without needing to define these details in code beforehand. The introspection mechanism is a critical element of GraphQL’s flexibility.

If this mechanism is not disabled on a production instance, attackers could determine the data structure. In some ways, it's comparable to obtaining information about the database structure from the information_schema table during an SQL Injection attack in databases like MS SQL, PostgreSQL, or MySQL.

Atak DOS via GraphQL – real-life example

During one of the penetration tests, I was verifying an application that implemented a chatbot feature. Analyzing HTTP communication, I discovered that the bot connects with GraphQL:

POST /api/chat/graphql HTTP/1.1
Host: […]
[…]

{
"operationName":"GetRefInfo",
"variables":{
"ref":"emplocity"
},
"query":"
query GetRefInfo($ref: String!) {
public {
ref(value: $ref) {
account {
slug
locale {
[…]
}"
}

The response returned the content of the request:

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
[…]
Content-Length: 368
Content-Type: application/json
Connection: close
{
"data": {
"public": {
"__typename": "Public",
"ref": null
}
},
"errors": [
{
"error_type": "AccountNotExist",
"locations": [
{
"column": 5,
"line": 3
}
],
"message": "AccountNotExist",
"path": [
"public",
"ref"
],
"sentry": "None"
}
]
}

After confirming GraphQL's presence, I attempted to download the data schema:

POST /api/chat/graphql HTTP/1.1
Host: […]
Content-Length: 1728
Content-Type: application/json
{
"query":"
query IntrospectionQuery {
__schema {

queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation
onFragment
onField
}
}
}
[…]
}"
}

The response revealed the structure, which I analyzed to detect objects that could be called in a looped manner. I looked for a connection where object A could access object B, and from object B, access could be gained back to object A. Below is a fragment of the introspection with highlighted objects possessing the described property:

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
[…]
Content-Length: 258699
Content-Type: application/json
{
"data": {
"__schema": {
[…]
"mutationType": {
"name": "Mutation"
},
"queryType": {
"name": "Query"
},
"subscriptionType": null,
"types": [
{
"description": null,
"enumValues": null,
"fields": [
[…]
{
"args": [],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "me",
"type": {
"kind": "OBJECT",
"name": "Chatter",
"ofType": null
}
},
[…]
],
"inputFields": null
, "interfaces": [],
"kind": "OBJECT",
"name": "Query",
"possibleTypes": null
},
[…]
{
"description": null
, "enumValues": null,
"fields": [
[…]
{
"args": […],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "messages",
"type": {
"kind": "OBJECT",
"name": "MessageConnection",
"ofType": null
}
},
[…]
],
"inputFields": null,
"interfaces": [
{
"kind": "INTERFACE",
"name": "AuthorizedNode",
"ofType": null
}
],
"kind": "OBJECT",
"name": "Chatter",
"possibleTypes": null
},
[…]
{
"description": null,
"enumValues": null,
"fields": [
[…]
{
"args": [],
"deprecationReason": null,
"description": "Contains the nodes in this connection.",
"isDeprecated": false,
"name": "edges",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "MessageEdge",
"ofType": null
}
}
}
}
],
"inputFields": null,
"interfaces": [],
"kind": "OBJECT",
"name": "MessageConnection",
"possibleTypes": null
},
[…]
{
"description": "A Relay edge containing a `Message` and its cursor.",
"enumValues": null,
"fields": [
{
"args": [],
"deprecationReason": null,
"description": "The item at the end of the edge",
"isDeprecated": false,
"name": "node",
"type": {
"kind": "OBJECT",
"name": "Message",
"ofType": null
}
},
[…]
],
"inputFields": null,
"interfaces": [],
"kind": "OBJECT",
"name": "MessageEdge",
"possibleTypes": null
},
[…]
{
"description": null,
"enumValues": null,
"fields": [
[…]
{
"args": [],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "conversation",
"type": {
"kind": "OBJECT",
"name": "Conversation",
"ofType": null
}
},
[…]
],
"inputFields": null,
"interfaces": [
{
"kind": "INTERFACE",
"name": "AuthorizedNode",
"ofType": null
}
],
"kind": "OBJECT",
"name": "Message",
"possibleTypes": null
},
[…]
{
"description": null,
"enumValues": null,
"fields": [
[…]
{
"args": [],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "chatter",
"type": {
"kind": "OBJECT",
"name": "Chatter",
"ofType": null
}
}
],
"inputFields": null,
"interfaces": [
{
"kind": "INTERFACE",
"name": "AuthorizedNode",
"ofType": null
}
],
"kind": "OBJECT",
"name": "Conversation",
"possibleTypes": null
},
[…]
]
}
}
}
From the analyzed schema, it became clear we could create a loop by transitioning through the query named “me,” which is an object “Chatter” where you can find a node “messages” of type “MessageConnection.” Then, I discovered a node “edges” of type “MessageEdge.” The next node in the indicated type is “node” of type “Message.” Further, I found a node “conversation” representing the “Conversation” object. In the final step, a node named “chatter” of type “Chatter” was found.

Simplified, it looks like the schema below:

Chatter → MessageConnection → MessageEdge → Message → Conversation → back to Chatter.

Due to these steps, I created a loop of calls leading to a DoS attack:



The effect achieved was a response time of over 55 seconds and a response size of about 84MB. Generating such a response from the server, besides straining RAM and CPU, also affects the network, as the entire message must be sent back to the HTTP client. Sending such a request through an automation tool caused the service and the entire server to be unavailable. Of course, there was nothing preventing the payload from being increased by adding more loops, but it was unnecessary for these tests.

Defense methods:

When starting with new technology, remember that knowledge and understanding are key. Don't blindly rush towards the latest programming language or popular framework. Lack of proper knowledge can bring more problems than benefits. Begin your journey with technology by thoroughly understanding its basics.

One important step in preventing attacks is disabling the introspection mechanism. While this doesn’t solve the problem entirely, it makes it harder for potential attackers to exploit vulnerabilities. Without the ability to check available objects, an attack becomes much harder to execute. Disabling introspection can serve as an additional layer of protection.

Further steps include introducing limitations. Limit how many nesting levels can be in a request, reject the request if the limit is exceeded and consider implementing rate limiting and maximum request size. Additionally, applying response padding functions allows controlling the size of responses and breaking them into more manageable segments. Control and limitations are a key part of the strategy for protection against attacks.

Materials:
  • https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html#dos-prevention
  • https://graphql.org/learn/introspection/
  • https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/graphql




  • #CyberSecurity #Pentesting #GraphQL #ServerSecurity #PentestChronicles


    Next Pentest Chronicles

    When Usernames Become Passwords: A Real-World Case Study of Weak Password Practices

    Michał WNękowicz

    9 June 2023

    In today's world, ensuring the security of our accounts is more crucial than ever. Just as keys protect the doors to our homes, passwords serve as the first line of defense for our data and assets. It's easy to assume that technical individuals, such as developers and IT professionals, always use strong, unique passwords to keep ...

    SOCMINT – or rather OSINT of social media

    Tomasz Turba

    October 15 2022

    SOCMINT is the process of gathering and analyzing the information collected from various social networks, channels and communication groups in order to track down an object, gather as much partial data as possible, and potentially to understand its operation. All this in order to analyze the collected information and to achieve that goal by making …

    PyScript – or rather Python in your browser + what can be done with it?

    michał bentkowski

    10 september 2022

    PyScript – or rather Python in your browser + what can be done with it? A few days ago, the Anaconda project announced the PyScript framework, which allows Python code to be executed directly in the browser. Additionally, it also covers its integration with HTML and JS code. An execution of the Python code in …

    Any questions?

    Happy to get a call or email
    and help!

    Terms and conditions
    © 2023 Securitum. All rights reserved.