Security question exposed to anonymous callers via /rest/user/security-question
The /rest/user/security-question endpoint is mounted without any authentication, letting unauthenticated attackers enumerate the security question chosen by any registered email — the metadata they need to attack the reset flow.
At line 427:
app.get('/rest/user/security-question', utils.asyncHandler(securityQuestion()))
Unlike many other routes in this file the handler is registered without security.isAuthorized() or any other gate. Combined with the Juice-Shop reset-password flow (also wired at line 426 — app.post('/rest/user/reset-password', utils.asyncHandler(resetPassword()))), which uses securityAnswer as the sole knowledge factor, this endpoint lets an attacker:
- Submit a victim's email and learn the security question (e.g. ‘Name of your favorite pet?’).
- Pick targets whose questions are easy to OSINT (pet name, school, hometown, mother's maiden name) and pivot to the reset endpoint.
The walker brief explicitly calls this pattern out: ‘Endpoints that return the security question text to anonymous callers (lets attackers shop accounts to find easy questions).’ The presence of a SecurityAnswerModel import alongside UserModel and its use in the reset flow confirms answers are stored as a reset factor rather than as a step-up after token verification, so disclosing the question is directly weaponizable.
1. Anonymously query the security question for any victim
curl 'https://target/rest/user/security-question?email=victim@example.com'
→ { "question": { "question": "Name of your favorite pet?" } }
2. Guess/research the answer, then call /rest/user/reset-password
curl -X POST https://target/rest/user/reset-password \ -H 'Content-Type: application/json' \ -d '{"email":"victim@example.com","answer":"Fluffy","new":"pwned","repeat":"pwned"}'
Unauthenticated account takeover precursor. Any attacker with a target email can discover the secret question, then exploit the security-answer-based reset flow with a researched/guessed answer. Affects every registered user, no authentication required.
Line 427 registers app.get('/rest/user/security-question', utils.asyncHandler(securityQuestion())) with no security.isAuthorized() or other gate, while sibling rest routes (e.g. /rest/user/authentication-details, /rest/basket/:id) are explicitly guarded. Combined with the unauthenticated reset flow at line 426 (/rest/user/reset-password) that uses the security answer as the sole knowledge factor, an attacker can query the question by email and pivot to reset. The scope explicitly instructs treating Juice Shop findings as real production bugs, so the intentionally-vulnerable nature is irrelevant.
The route at line 427 app.get('/rest/user/security-question', utils.asyncHandler(securityQuestion())) is registered without security.isAuthorized() or any other gate (compare to siblings like /rest/user/authentication-details and /rest/2fa/status which carry security.isAuthorized()), so any anonymous HTTP client can query the endpoint over the network with no preparation or user interaction (AV:N/AC:L/PR:N/UI:N). The direct impact of this specific sink is information disclosure of a single per-user metadata field (the security question text keyed by email) — the attacker doesn't choose which bits leak beyond picking the victim email, so confidentiality is Low. The handler itself does not write data nor degrade availability (I:N/A:N), and the impact stays within the Juice-Shop app's security authority (S:U); the downstream reset-password takeover is a separate sink scored elsewhere.
- CWE-640
- OWASP ASVS V6.2
- https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html