Skip to content

Add vulnerable-by-design lab covering all 12 security categories#4

Open
Bonckheere1 wants to merge 1 commit intomasterfrom
claude/vulnerable-access-control-code-LuAgr
Open

Add vulnerable-by-design lab covering all 12 security categories#4
Bonckheere1 wants to merge 1 commit intomasterfrom
claude/vulnerable-access-control-code-LuAgr

Conversation

@Bonckheere1
Copy link
Owner

@Bonckheere1 Bonckheere1 commented Mar 6, 2026

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

Summary by Aikido

⚠️ Security Issues: 16 Quality Issues: 0 Resolved Issues: 0

🚀 New Features

  • Registered and mounted /vulnerable routes in main router
  • Added vulnerable.js handlers implementing numerous intentional vulnerabilities
  • Included Swig view templates, sidebar lab link, and uploads directory

More info

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) => {
Copy link

@aikido-pr-checks aikido-pr-checks bot Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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 },
Copy link

@aikido-pr-checks aikido-pr-checks bot Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
{ 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}'` };

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) => {
Copy link

@aikido-pr-checks aikido-pr-checks bot Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants