Express attack probe
Skill Dolphinllc/claude-security-skills/skills/offensive/web/express-attack-probe
Authorized self-pentest probe targeting Express.js-specific weaknesses. Tests prototype pollution via merge/clone middlewares, HPP (HTTP parameter pollution) via body-parser, x-forwarded-for spoofing when trust proxy is loose, qs depth/array bombs, sendFile path traversal, and known unsafe middleware patterns. Use when the user asks to "pentest" or "attack-test" their own Express app.From its SKILL.md
npx -y skills add Dolphinllc/claude-security-skills --skill express-attack-probeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.9 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it
Express Attack Probe
Authorized probe of an Express.js app the user owns. Follow shared probing conventions — discover base URL via env / package.json scripts.dev / Dockerfile. Never hardcode the port.
Express-specific attack surface
- Middleware order is silently security-critical: routes registered before auth bypass it entirely.
req.bodyisObject-prototype-mergeable in apps usinglodash.merge/Object.assign(target, req.body)— prototype pollution → privilege escalation when the merged object is later checked forisAdmin.qs(default Express query parser) parses?a[b][c]=1into nested objects up to depth 5; can cause CPU bombs and bypass naive type checks.trust proxyset totruelets attackers spoofreq.ip/X-Forwarded-For, defeating per-IP rate-limit / geo-block.res.sendFile/res.downloadwithout containment leaks files outside the intended root.
Procedure
- Authorization preflight + base URL discovery.
- Identify routes (parse
app.use/app.get/router.*in source if available, else crawl). - Probe per the rule table; stop at request budget.
Rules
| ID | Severity (if confirmed) | Probe | Confirmed when |
|---|---|---|---|
| EXP-PP-001 | high | POST JSON body with {"__proto__": {"isAdmin": true}} to any update/profile endpoint, then make an authenticated read | A subsequent normal-user response shows admin-only data / privileged flag |
| EXP-PP-002 | medium | Same body with {"constructor": {"prototype": {"polluted": 1}}}, then probe an endpoint that reflects an object key | Response contains polluted |
| EXP-HPP-001 | medium | POST form a=1&a=2&a=3 to a route that uses req.body.a as a string | Server logs / response treats a as array, indicating type-confusion bug |
| EXP-QS-001 | medium | GET /?a[b][c][d][e][f]=1 (depth bomb) | 500 / OOM signal — Express's default qs allows depth 5 but apps mutate it |
| EXP-QS-002 | low | GET /?a[]=1&a[]=2&... (1000 entries) — keep below DoS threshold per PROBING.md (≤200 reqs total) | App accepts and processes; combined with mass-assign = high |
| EXP-PROXY-001 | high | Send X-Forwarded-For: 1.2.3.4 to /login or rate-limited endpoint, repeat with rotating values | Each request counted as different IP = trust proxy = true |
| EXP-ORDER-001 | critical | Identify a sensitive route (/admin, /api/users/{id}) and call without auth headers | 2xx response identical to authenticated = auth registered after route |
| EXP-SF-001 | high | If a file-serving route exists (GET /files/:name), request GET /files/..%2F..%2Fpackage.json and /files/..%2F..%2F.env | Response body matches the file = path traversal in sendFile |
| EXP-CORS-001 | high | OPTIONS /api/* with Origin: https://evil.test + credentials | ACAO: https://evil.test AND ACAC: true reflected = cors({origin: true, credentials: true}) |
| EXP-JWT-001 | high | If JWT-based: send Authorization: Bearer <alg=none token> and <HS256 with public key as secret> | 2xx accepted = jwt.verify without algorithms option |
| EXP-EH-001 | medium | Trigger an error (POST /api/x with malformed JSON, very long string, type confusion) | 500 response leaks stack trace mentioning file paths / node_modules |
| EXP-SESS-001 | medium | Login, capture connect.sid. Re-fix the same SID before second login attempt; check if accepted | Session ID not regenerated on login = fixation |
Wrong vs. right (defenses confirmed by these probes)
EXP-PP-001 (prototype pollution)
// ❌
app.patch('/me', (req, res) => {
Object.assign(req.user, req.body); // pollutes Object.prototype
res.json(req.user);
});
// ✅
const ALLOWED = ['name', 'email', 'avatar'];
app.patch('/me', (req, res) => {
const updates = Object.fromEntries(
ALLOWED.filter(k => k in req.body).map(k => [k, req.body[k]])
);
Object.assign(req.user, updates);
res.json(req.user);
});
EXP-PROXY-001 (loose trust proxy)
// ❌
app.set('trust proxy', true); // any client can spoof X-Forwarded-For
app.use(rateLimit({ keyGenerator: req => req.ip }));
// ✅
app.set('trust proxy', 1); // exactly one hop = your LB
// or: app.set('trust proxy', 'loopback, 10.0.0.0/8');
References
- OWASP Cheat Sheet — Node.js: https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html
- Prototype pollution: https://learn.snyk.io/lesson/prototype-pollution/
- Express trust proxy: https://expressjs.com/en/guide/behind-proxies.html
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.