Skip to content

fix: SQL Injection 7 - parameterize userEmail in login query - #326

Closed
beanbeah wants to merge 2 commits into
OWASP-CTF:dc34-ctffrom
beanbeah:ctf/r2-challenge-challenge-29-sqli-7
Closed

beanbeah wants to merge 2 commits into
OWASP-CTF:dc34-ctffrom
beanbeah:ctf/r2-challenge-challenge-29-sqli-7

Conversation

@beanbeah

@beanbeah beanbeah commented Aug 9, 2026

Copy link
Copy Markdown

Fixes Challenge-29-SQLi-7 (SQL Injection 7).

Vulnerability: SqlInjection7.doPost() built its login query by concatenating the raw subEmail request parameter directly into a SQL string literal, binding only subPassword via a PreparedStatement placeholder:

conn.prepareStatement(
    "SELECT userName FROM users WHERE userEmail = '" + subEmail + "' AND userPassword = ?;");

Validate.isValidEmailAddress() validates the email using javax.mail's InternetAddress, whose lenient RFC822 parser accepts quoted local-parts, so an email string containing a single quote (e.g. x@x.com' OR '1'='1' -- -) passes validation while still breaking out of the SQL string literal — bypassing the password check and dumping every row in the challenge's users table with an empty/arbitrary password.

Fix: bind subEmail as a second PreparedStatement parameter alongside subPassword, so both are always treated as literal values.

Local verification (WSL, throwaway MariaDB loaded with the module's real sqlInjectSeven schema/data): a standalone JDBC harness reproducing both the old and new query-building code confirmed:

  • Legitimate login (correct email + password) returns exactly 1 row before and after the fix.
  • The injection payload (x@x.com' OR '1'='1' -- - + wrong password) returns all 23 rows under the old code, and 0 rows under the fixed code.

mvn spotless:apply made no additional changes; mvn compile succeeded (BUILD SUCCESS).

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 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

🏆 Security Shepherd — CTF Patch Score

█░░░░░░░░░░░░░░░░░░░  3 / 79 pts  (4%)

1 / 40 challenges patched

Per-challenge detail is withheld — it would reveal the rubric.

Commit: 3887b13 · scoring run

🎉 Your result is on the leaderboard — see where you rank! 🏆

…r 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.
@beanbeah

beanbeah commented Aug 9, 2026

Copy link
Copy Markdown
Author

Closing as part of a full stand-down of this CTF push.

@beanbeah beanbeah closed this Aug 9, 2026
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.

1 participant