Skip to main content

Server shutdown via GraphQL during real-life pentest

Illustration of Server shutdown via GraphQL during real-life pentest
KAMIL JAROSIŃSKI

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.

Remember!

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:

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:

GraphQL 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:

Other Insights

Illustration of Persistent threats via blind XSS and subsequent data exfiltration - tips and ticks from a security perspective.

Persistent threats via blind XSS and subsequent data exfiltration - tips and ticks from a security perspective.

SEBASTIAN JEŻ, KALINA ZIELONKA

A sneaky security threat that combines Blind XSS with data exfiltration techniques poses a significant risk, allowing adversaries to insert persistent HTML/JavaScript code that executes within the domain context of an application. This vulnerability can be exploited to steal any data from the application or perform actions on behalf of another user.

READ article
Illustration of Better safe than sorry - The Imperative of Double-Checking Application Architecture Before Launch.

Better safe than sorry - The Imperative of Double-Checking Application Architecture Before Launch.

MICHAŁ ŻACZEK

Every application's journey from conception to release involves critical steps within the Software Security Development Life Cycle (SSDLC). Paramount among these is the Design Phase, where the application's architecture is conceptualized. This step is fundamental in determining the coding approach and necessitates careful consideration, especially from a security standpoint. Key aspects like data processing and storage need thorough examination.

READ article
A professional cybersecurity consultant ready to assist with your inquiry.

Any questions?

Happy to get a call or email
and help!