Add vulnerable-by-design lab covering all 12 security categories#4
Add vulnerable-by-design lab covering all 12 security categories#4Bonckheere1 wants to merge 1 commit intomasterfrom
Conversation
Adds a new /vulnerable lab with intentionally exploitable endpoints for
security testing and education. Covers every requested vulnerability class:
1. Broken Access Control / BOLA / IDOR
- IDOR: GET /vulnerable/user/:userId (no ownership check)
- Cross-tenant document leak: GET /vulnerable/document/:docId
- Mass assignment / privilege escalation: PUT /vulnerable/account/:userId
- Forced browsing / missing isAdmin: GET /vulnerable/admin-panel
2. Business Logic & Validation
- Client-supplied price (negative amount → refund): POST /vulnerable/checkout
- Unlimited coupon reuse (no single-use enforcement): POST /vulnerable/coupon
- Negative transfer amount: POST /vulnerable/transfer
3. Code & Command Injection
- OS command injection via child_process.exec(): GET /vulnerable/ping
- Server-side eval() of user input: POST /vulnerable/eval
- Shell injection via execSync: GET /vulnerable/file-info
4. NoSQL / Database / LDAP Injection
- MongoDB operator injection auth bypass: POST /vulnerable/login-nosql
- MongoDB $where JS injection: GET /vulnerable/db-search
- LDAP filter injection: GET /vulnerable/ldap
5. LLM & Prompt Injection
- Direct prompt injection (system prompt leak): POST /vulnerable/ai/chat
- Indirect/template injection: POST /vulnerable/ai/review
- Jailbreak via user-controlled system role: POST /vulnerable/ai/summarize
6. Server-Side Request Forgery (SSRF)
- Arbitrary URL fetch (AWS IMDS, localhost): GET /vulnerable/fetch
- Webhook to internal service: POST /vulnerable/webhook
7. Authentication & Session Management
- JWT alg:none bypass: GET /vulnerable/token/verify
- JWT with weak hardcoded secret: GET /vulnerable/token/generate
- Predictable reset token in URL: GET /vulnerable/reset-password
- Brute-force login (no rate limiting, session fixation): POST /vulnerable/brute-login
8. Client-Side Attacks
- Reflected XSS (Swig autoescape off): GET /vulnerable/search
- Stored XSS via comments: POST /vulnerable/comment
- DOM-based XSS (location.hash → innerHTML): GET /vulnerable/dom-xss
- Open redirect (no allowlist): GET /vulnerable/redirect
9. Insecure Deserialization & SSTI
- Prototype pollution via Object.assign: POST /vulnerable/preferences
- Swig SSTI (user-controlled template): POST /vulnerable/template
10. Files & Misconfigurations
- Path traversal / LFI: GET /vulnerable/files/download
- Unrestricted file upload: POST /vulnerable/files/upload
- Stack trace leakage: GET /vulnerable/error
11. Secrets & Cryptography
- Broken crypto (MD5/SHA-1/base64): GET /vulnerable/crypto/hash
- AES-ECB mode: GET /vulnerable/crypto/ecb
- PII written to logs: POST /vulnerable/payment
- Hardcoded credentials in source (API keys, DB password, AWS keys)
12. Hardening
- Wildcard CORS with credentials: GET /vulnerable/cors-data
- GraphQL introspection without auth: POST /vulnerable/graphql
- Missing security headers: GET /vulnerable/no-headers
Also adds:
- 4 Swig view templates (vulnerable.html, vulnerable-search.html,
vulnerable-comments.html, vulnerable-dom-xss.html)
- "Vulnerable Lab" link in the sidebar navigation
- uploads/ directory for file demo routes
https://claude.ai/code/session_01MgZJx5PDBbX7puZJDXTW4c
| const expression = req.body.expression || req.query.expression || ""; | ||
| try { | ||
| /* jshint ignore:start */ | ||
| const result = eval(expression); // VULNERABLE |
There was a problem hiding this comment.
Remote Code Execution possible via eval()-type functions - critical severity
Using functions such as eval, but also less obvious functions such as setTimeout, setInterval or 'new Function' can lead to users being able to run their own code on your servers.
Show fix
Remediation: If possible, avoid using these functions altogether. If not, use a list of allowed inputs that can feed into these functions.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
| this.getDocument = (req, res) => { | ||
| const docId = req.params.docId; // No tenant/owner check | ||
|
|
||
| db.collection("documents").findOne({ id: docId }, (err, doc) => { |
There was a problem hiding this comment.
NoSQL injection attack possible - critical severity
Query injection attacks are possible if users can pass objects instead of strings to query functions such as findOne.
By injecting query operators attackers can control the behavior of the query, allowing them to bypass access controls and extract unauthorized data. Consider the attack payload ?user_id[$ne]=5: if the user_id query parameter is passed to the query function without validation or casting its type, an attacker can pass {$ne: 5} instead of an integer to the query. {$ne: 5} uses the 'not equal to' operator to access data of other users.
While this vulnerability is known as NoSQL injection, relational databases (mysql, postgres) are also vulnerable to this attack if the query library offers a NoSQL-like API and supports string-typed query operators. Examples include prisma and sequelize versions prior to 4.12.0.
| db.collection("documents").findOne({ id: docId }, (err, doc) => { | |
| db.collection("documents").findOne({ id: { $eq: docId } }, (err, doc) => { |
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
|
|
||
| // VULNERABLE: objects from req.body passed directly as query operators | ||
| db.collection("users").findOne( | ||
| { userName: username, password: password }, |
There was a problem hiding this comment.
NoSQL injection attack possible - critical severity
Query injection attacks are possible if users can pass objects instead of strings to query functions such as findOne.
By injecting query operators attackers can control the behavior of the query, allowing them to bypass access controls and extract unauthorized data. Consider the attack payload ?user_id[$ne]=5: if the user_id query parameter is passed to the query function without validation or casting its type, an attacker can pass {$ne: 5} instead of an integer to the query. {$ne: 5} uses the 'not equal to' operator to access data of other users.
While this vulnerability is known as NoSQL injection, relational databases (mysql, postgres) are also vulnerable to this attack if the query library offers a NoSQL-like API and supports string-typed query operators. Examples include prisma and sequelize versions prior to 4.12.0.
| { userName: username, password: password }, | |
| { userName: { $eq: username }, password: { $eq: password } }, |
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
| const value = req.query.value || ""; | ||
|
|
||
| // VULNERABLE: user-controlled JS string executed inside MongoDB | ||
| const query = { $where: `this.${field} == '${value}'` }; |
There was a problem hiding this comment.
NoSQL injection attack possible - critical severity
Query injection attacks are possible if users can pass objects instead of strings to query functions such as findOne.
By injecting query operators attackers can control the behavior of the query, allowing them to bypass access controls and extract unauthorized data. Consider the attack payload ?user_id[$ne]=5: if the user_id query parameter is passed to the query function without validation or casting its type, an attacker can pass {$ne: 5} instead of an integer to the query. {$ne: 5} uses the 'not equal to' operator to access data of other users.
While this vulnerability is known as NoSQL injection, relational databases (mysql, postgres) are also vulnerable to this attack if the query library offers a NoSQL-like API and supports string-typed query operators. Examples include prisma and sequelize versions prior to 4.12.0.
Show fix
Remediation: User input should be validated (e.g. with class-validator, zod or joi) or sanitized (e.g. with mongo-sanitize). Alternatively cast request parameters to their expected type or use the $eq operator to block object injection. You can also Autofix all instances of NoSQL injection by installing Zen for Node.js.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
| this.bruteLogin = (req, res) => { | ||
| const { username, password } = req.body; | ||
| // No failed-attempt counter, no account lockout, no delay | ||
| db.collection("users").findOne({ userName: username, password: password }, (err, user) => { |
There was a problem hiding this comment.
NoSQL injection attack possible - critical severity
Query injection attacks are possible if users can pass objects instead of strings to query functions such as findOne.
By injecting query operators attackers can control the behavior of the query, allowing them to bypass access controls and extract unauthorized data. Consider the attack payload ?user_id[$ne]=5: if the user_id query parameter is passed to the query function without validation or casting its type, an attacker can pass {$ne: 5} instead of an integer to the query. {$ne: 5} uses the 'not equal to' operator to access data of other users.
While this vulnerability is known as NoSQL injection, relational databases (mysql, postgres) are also vulnerable to this attack if the query library offers a NoSQL-like API and supports string-typed query operators. Examples include prisma and sequelize versions prior to 4.12.0.
| db.collection("users").findOne({ userName: username, password: password }, (err, user) => { | |
| db.collection("users").findOne({ userName: { $eq: username }, password: { $eq: password } }, (err, user) => { |
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
| if (!host) return res.status(400).send("host parameter required"); | ||
|
|
||
| // VULNERABLE: user input embedded directly in shell command | ||
| exec(`ping -c 3 ${host}`, (err, stdout, stderr) => { |
There was a problem hiding this comment.
Potential for OS command injection via child_process call - critical severity
It is generally not recommended to call out to the operating system to execute commands. When the application is executing file-system-based commands, user input should never be used in constructing commands or command arguments.
Show fix
Remediation: If you can, avoid the exec() call. If that is not possible, consider hard coding both the command and arguments to be used. Alternatively, install Aikido Runtime for NodeJS to prevent command injection completely.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
| const filename = req.query.filename || ""; | ||
| try { | ||
| // VULNERABLE: filename injected into shell command | ||
| const output = execSync(`file ${filename} 2>&1`).toString(); |
There was a problem hiding this comment.
Potential for OS command injection via child_process call - critical severity
It is generally not recommended to call out to the operating system to execute commands. When the application is executing file-system-based commands, user input should never be used in constructing commands or command arguments.
Show fix
Remediation: If you can, avoid the exec() call. If that is not possible, consider hard coding both the command and arguments to be used. Alternatively, install Aikido Runtime for NodeJS to prevent command injection completely.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
| this.openRedirect = (req, res) => { | ||
| const redirectTo = req.query.next || "/dashboard"; | ||
| // VULNERABLE: no allowlist / hostname validation | ||
| return res.redirect(redirectTo); |
There was a problem hiding this comment.
Open redirect can be used in social engineering attacks - critical severity
An open redirect allows an attacker to use your app to perform social engineering attacks by redirecting users to other top-level domains (e.g. evilsite.com). It can usually also be used to combine with other exploits that could result in stolen credentials and user account takeover.
Show fix
Remediation: You should only ignore this finding if you've ensured that the domain you are redirecting your user to is a domain from a allowlist.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
Adds a new /vulnerable lab with intentionally exploitable endpoints for security testing and education. Covers every requested vulnerability class:
Broken Access Control / BOLA / IDOR
Business Logic & Validation
Code & Command Injection
NoSQL / Database / LDAP Injection
LLM & Prompt Injection
Server-Side Request Forgery (SSRF)
Authentication & Session Management
Client-Side Attacks
Insecure Deserialization & SSTI
Files & Misconfigurations
Secrets & Cryptography
Hardening
Also adds:
https://claude.ai/code/session_01MgZJx5PDBbX7puZJDXTW4c
Summary by Aikido
🚀 New Features
More info