Skip to content

Nested GraphQL query with circular relationship can cause Denial of Service

HackerOne report #638282 by freddd on 2019-07-09, assigned to estrike:

Security issue on dev: https://dev.gitlab.org/gitlab/gitlabhq/issues/2903

Summary

Due to a circular relationship in the GraphQL schema as seen in the "Steps to reproduce" it's possible to create a query that becomes exponentially more complex with almost no effort that can ultimately lead to Denial of Service. Note that I have only tested with a couple of small queries to not cause any service disruption.

URL: https://gitlab.com/api/graphql through https://gitlab.com/-/graphql-explorer or using any http client
Authentication: None needed
IP used: 81.224.134.248, all of the tests were carried out manually.

Steps to reproduce

  1. There is no preconditions other than having curl/postman/insomnia or using the graphql-explorer
  2. Use the following body in the GraphQL call, you can increase the number of nested levels indefinitely getting an exponentially larger response each time.
query allSchemaTypes {  
    __schema {  
        types {  
            fields {  
                type{  
                    fields {  
                        type {  
                            fields {  
                                type {  
                                    fields {  
                                        name  
                                    }  
                                }  
                            }  
                        }  
                    }  
                }  
            }  
        }  
    }  
}
  1. Press send in Postman or through the graphql-explorer

Impact

The impact if this attack were to be executed could be denial of service and reducing the availability of the GraphQL API. Depending on the underlying architecture the attack could be cascading as it can potentially use all resources on the web-server.

Examples

What is the current bug behavior?

A call with the example body resulted in a request with a response of the size 93.5 KB. Nesting one level further resulted in a request with the response size being 139.36 KB. As you can see the size is increasing by each nesting and there is no limit on how many levels one can ask for (as far as I can tell). The largest query I ran before stopping took 18341 ms and had a response size of 2.35 MB.

It's worth mentioning that this applies to all circular relationships in the schema not only to the introspection query that is being used in this example.

A malicious party could write a simple script generating a huge query and execute it in parallel causing the web server(s) to run out of resources handling the requests due to CPU and/or RAM consumption.

What is the expected correct behavior?

I would expect to get an error message after an arbitrary number of levels as usually each API has a max number of levels that is reasonable. I would also expect to have a rate limit on query complexity as usually there are parts of the schema that might be heavier or less likely to be cached than others, etc.

There are multiple strategies on how to deal with this type of attack, you can either rate limit on depth (levels of nesting), on complexity, on query size and so on. This blog post describes it in better detail: https://blog.apollographql.com/securing-your-graphql-api-from-malicious-queries-16130a324a6b

Relevant logs and/or screenshots

I have attached the following:

  • A screenshot on a call in postman
  • A screenshot on the same call in the graphql-explorer
  • The response from postman as a .json file

Output of checks

This bug happens on https://gitlab.com/api/graphql

Results of GitLab environment info

Please let me know if you need more information or want me to clarify something,

/Fredrik

Impact

Denial of service of the GraphQL API and possibly cascading problems depending on the underlying architecture.

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

Edited by GitLab SecurityBot