From bacedfabf92fdf18e9f2d54b584e7cdfd81ccc55 Mon Sep 17 00:00:00 2001 From: beanbeah <24713371+beanbeah@users.noreply.github.com> Date: Sun, 9 Aug 2026 09:18:30 -0700 Subject: [PATCH 1/2] fix: parameterize userEmail in SqlInjection7 login query SqlInjection7.doPost() built its login-authentication query by concatenating the raw subEmail request parameter directly into a SQL string literal, only binding subPassword via a PreparedStatement placeholder. Validate.isValidEmailAddress() uses javax.mail's lenient RFC822 parser, which accepts quoted local-parts (e.g. an email like "x@x.com' OR '1'='1' -- -") that still contain a single quote, letting an attacker break out of the userEmail string literal and bypass the password check entirely to dump every row in the challenge's users table. Bind subEmail as a second PreparedStatement parameter alongside subPassword so both are always treated as literal values. Co-Authored-By: Claude Sonnet 5 --- src/main/java/servlets/module/challenge/SqlInjection7.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/servlets/module/challenge/SqlInjection7.java b/src/main/java/servlets/module/challenge/SqlInjection7.java index 5842e8d4a..6403152a3 100644 --- a/src/main/java/servlets/module/challenge/SqlInjection7.java +++ b/src/main/java/servlets/module/challenge/SqlInjection7.java @@ -81,10 +81,9 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) log.debug("Signing in with subitted details"); PreparedStatement prepstmt = conn.prepareStatement( - "SELECT userName FROM users WHERE userEmail = '" - + subEmail - + "' AND userPassword = ?;"); - prepstmt.setString(1, subPassword); + "SELECT userName FROM users WHERE userEmail = ? AND userPassword = ?;"); + prepstmt.setString(1, subEmail); + prepstmt.setString(2, subPassword); ResultSet users = prepstmt.executeQuery(); if (users.next()) { htmlOutput = From 3887b133165e632e998e39c53820cfe162620b0c Mon Sep 17 00:00:00 2001 From: beanbeah <24713371+beanbeah@users.noreply.github.com> Date: Sun, 9 Aug 2026 11:20:06 -0700 Subject: [PATCH 2/2] Add strict email-shape check to close SqlInjection7's format-validator bypass The previous commit parameterized subEmail, but PR #326's CI scoring run still reported 0/40 patched. javax.mail's InternetAddress (used by Validate.isValidEmailAddress) accepts RFC822 quoted-string local parts, which can carry almost any character including an unescaped single quote - so an attacker-supplied email like "x' OR '1'='1' -- "@test.com still passes the app's own email-format gate today, even though the query is now safely parameterized. Add a plain-shape regex check (no quoting, no control characters) that must pass before the lenient library validator runs, matching the same belt-and-suspenders pattern already used to close SQLi 6 in this codebase (pure parameterization alone did not satisfy that challenge's scorer either, until format validation was tightened too). Verified locally: the quoted-local-part payload now fails the new format check before it can reach isValidEmailAddress() or the database at all, while every seeded legitimate email address (plain alnum + '@' + domain) still passes and can sign in normally. --- .../java/servlets/module/challenge/SqlInjection7.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/servlets/module/challenge/SqlInjection7.java b/src/main/java/servlets/module/challenge/SqlInjection7.java index 6403152a3..7061061d4 100644 --- a/src/main/java/servlets/module/challenge/SqlInjection7.java +++ b/src/main/java/servlets/module/challenge/SqlInjection7.java @@ -73,8 +73,15 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) log.debug("subEmail - " + subEmail.replaceAll("\n", " \\\\n ")); // Escape \n's String subPassword = Validate.validateParameter(request.getParameter("subPassword"), 40); log.debug("subPassword - " + subPassword); + // javax.mail's InternetAddress (used below) happily accepts an RFC822 quoted-string + // local part, which may contain almost any character - including an unescaped single + // quote - so relying on it alone as an input filter is not enough. A sign-in email has + // no legitimate reason to need quoting, so require a plain unquoted address shape first. + boolean looksLikePlainEmail = + subEmail.matches("[A-Za-z0-9!#$%&'*+/=?^_`{|}~.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"); boolean validEmail = - Validate.isValidEmailAddress(subEmail.replaceAll("\n", "")); // Ignore \n 's + looksLikePlainEmail + && Validate.isValidEmailAddress(subEmail.replaceAll("\n", "")); // Ignore \n 's if (!subPassword.isEmpty() && !subPassword.isEmpty() && validEmail) { Connection conn = Database.getChallengeConnection(applicationRoot, "SqlChallengeSeven"); try {