Lab: SQL injection with filter bypass via XML encoding

Lab: SQL injection with filter bypass via XML encoding

October 19, 2025 9 min read

Beginner-friendly, hands-on walkthrough: bypass a WAF using XML encoding (Hackvertor), perform a UNION-based SQLi in an XML parameter, exfiltrate `username||'~'||password`, and log in as administrator. Includes full Repeater/Exploit flow, why each step works, defensive remediation, and real-world examples.




⚠️ Disclaimer

This write-up is for educational and defensive purposes only. Run these techniques only in legal, authorized environments such as PortSwigger Web Security Academy or your own test systems. Do not perform these tests against third-party websites or services without explicit permission.


TL;DR

A WAF is blocking obvious SQL payloads in this lab’s stock check XML endpoint. The trick is to inject a UNION-based payload into an XML field but obfuscate it with XML entity/hex encoding so the WAF doesn’t match obvious keywords. I used the Hackvertor extension to encode the payload (hex or dec entities), then sent the wrapped UNION SELECT username || '~' || password FROM users payload inside the <storeId> element. The app returned concatenated username~password rows which I used to log in as administrator.

This post shows every step I took in Burp Repeater, why the payloads work, how to bypass filters, how to build a safe exploit, and how teams should fix the issue. There’s also a link back to our pillar post on SQL Injection for deeper background: The Ultimate Guide to SQL Injection (SQLi).


1 - Lab objective & setup

Goal: Exploit an SQL injection in the stock check feature (XML POST) to retrieve the administrator's credentials, then log in as administrator.

Target: a POST /product/stock endpoint that accepts XML like:

POST /product/stock HTTP/1.1
Host: <LAB_HOST>
Content-Type: application/xml

<request>
  <productId>2</productId>
  <storeId>1</storeId>
</request>
SQL

You should work through this in Burp Suite with your browser proxied. I used Repeater for the iterative testing and Hackvertor (Burp extension) to encode payloads.


2 - Recon: why the target is interesting

I started by clicking the product page and using the “Check stock” button which triggers a POST /product/stock XML request. In the Proxy → HTTP history I captured the request and noted two XML elements: <productId> and <storeId>. The app returned stock levels directly in the HTTP response (so results are in-band), which makes UNION attacks feasible - if the server will accept our injected SQL and return the UNION output.

Important observation: when I tried an obvious UNION SELECT in the storeId value the WAF flagged the request and blocked it. That told me:

  • The injection point exists (data is used directly in SQL), and
  • A filter (WAF) blocks obvious SQL keywords.

So the problem reduces to: how do I sneak the UNION payload past the filter?

Captured stock check POST with productId and storeId.


3 - Initial probes: does the server evaluate expressions?

Before bypassing the WAF I needed to check that storeId is indeed evaluated by the server (and not just a tokenized value). I tested simple arithmetic expressions:

<storeId>1+1</storeId>
SQL

The server returned the stock for store 2. That proved the application evaluated and used the input inside SQL - a great sign for SQLi via storeId.

Next I tried a small union to test column counts:

<storeId>1 UNION SELECT NULL</storeId>
SQL

This was blocked and returned an "Attack detected" message from the WAF. So the target is vulnerable, but the WAF blocks obvious signatures.

Repeater: WAF blocked an obvious UNION payload.


4 - Bypass idea: obfuscate the payload using XML/hex entities

The WAF is matching keywords (UNION, SELECT, --, etc.). The canonical bypass is to encode those keywords so the WAF inspection rules don't match them while the backend still evaluates the decoded content. Since the injection point is XML, XML entity/hex encoding is a natural fit.

Two approaches often used:

  • Decimal/hex entity encoding (e.g., &#x55;&#x4E;&#x49;&#x4F;&#x4E; for UNION)
  • XML entity references wrapped in a tag that the backend will decode or an extension that will transform encoded text before SQL execution.

I used the Hackvertor extension because it quickly encodes selected text into dec/hex entities that the server will decode (or that the SQL engine will interpret) while the WAF sees no plain UNION string.


5 - Tools: Hackvertor & Burp Repeater (workflow)

If you don’t have Hackvertor, you can install it from the BApp store. It’s a Burp extension that converts highlighted text into multiple encodings (decimal entities, hex entities, URL-encoding, etc.). For this lab I:

  1. Captured the POST /product/stock request in Proxy.
  2. Right-clicked the storeId value in Repeater and chose Extensions → Hackvertor → Encode → hex_entities (or dec_entities).
  3. Hackvertor replaced the highlighted payload with an encoded entity block; I then sent the modified request to the server.

Using Hackvertor saves manual encoding work and reduces encoding mistakes.

Hackvertor encoding being applied to injection payload.

Hackvertor encoding being applied to injection payload response.


6 - Determining the number of columns & compatibility

I needed to know how many columns the original query returned. Because the response displays results in a single column, the query likely returns one column. Trying to return more than one column (without concatenation) produced no units or an error. So the server expects a single column - therefore the correct technique is to concatenate multiple values into one string.

Example: trying UNION SELECT NULL,NULL would fail. But UNION SELECT username || '~' || password returns a single column constructed by concatenation - perfect for this environment.


7 - Build the exploit payload (concatenation + encoding)

I crafted the SQL expression to concatenate username and password separated by ~:

1 UNION SELECT username || '~' || password FROM users
SQL

Direct injection is blocked, so I encoded just the injection part with Hackvertor. In Repeater I replaced the storeId value with an encoded wrapper:

<storeId><@hex_entities>1 UNION SELECT username || '~' || password FROM users</@hex_entities></storeId>
SQL

How it looks in practice: the server receives the XML, decodes entities, and the SQL engine sees the UNION SELECT username || '~' || password FROM users. The WAF sees only encoded bytes and doesn’t trigger the detection rule.

Note: depending on the DB, concatenation operator differs (|| is standard in many SQL dialects; use CONCAT() or + where appropriate). For this lab the || operator works.


8 - Send the encoded payload and interpret results

I sent the encoded request from Repeater. The server responded with stock results - but embedded among those results were entries like:

carlos~9zmg0e073r62xbdnz6ck
wiener~z8r4cnl7kga0kezuxsw6
administrator~sodvukzmb4rkevb14mjz
Gf

That means the UNION returned rows combining username and password. The app put them into the response body (in-band), so I copied the administrator password and used it to log in at the /login endpoint.

Encoded payload returned concatenated usernames and passwords.

Successful login as administrator and lab solved banner.


9 - Why this works: WAF vs backend decoding

Two separate systems are involved:

  1. The WAF/filters inspect the raw HTTP payload for signatures like UNION SELECT and block matches.
  2. The backend XML parser and DB eventually decode or interpret encoded entities, reconstructing the original UNION SELECT string before the SQL engine executes it.

Encoding prevents the WAF from matching the pattern while preserving the payload for the server. This split - between inspection and execution - is where many bypasses succeed.


10 - Troubleshooting common issues

  • Attack still blocked: Try a different encoding (dec_entities vs hex_entities) or reduce payload size - some WAFs inspect decoded entities too.
  • Payload truncated / XML invalid: Ensure your XML remains well-formed after encoding. Hackvertor keeps the structure intact but be mindful of CDATA or entity boundaries.
  • Concatenation fails (syntax errors): Adapt to DB flavor. If || throws errors, try CONCAT(username, '~', password) or %2b depending on backend.
  • No rows returned: The UNION might have wrong column type - make sure your SELECT returns a compatible single column. Use NULL placeholders to count columns first (e.g., 1 UNION SELECT NULL then try 1 UNION SELECT 'test' to check compatibility).
  • WAF detects encoded payloads: Some WAFs decode entity references before inspection. In that case, try alternate obfuscation (string concatenation via char codes, REPLACE tricks, or application-layer encoding).

11 - Defensive perspective (how to fix this)

This section is for developers and ops - it’s critical.

Top priority fixes

  1. Use prepared statements / parameterized queries - never build SQL by concatenating user input. This is the definitive fix.
  2. Validate and canonicalize XML inputs - only accept expected numeric IDs and types; reject expressions like 1+1 unless explicitly required.
  3. WAF rules aren't a primary defense - they help, but they can be bypassed. Treat them as a layer, not the core fix.

Additional mitigations

  • Whitelist input formats: Only allow storeId to be digits (e.g., regex ^\d+$). Reject or normalize encoded forms.
  • Output encoding / least privilege: Ensure DB accounts used by the public endpoints cannot access the users table. Use separate roles.
  • Logging and monitoring: Log suspicious request patterns (encoded entities, unusual XML DOCTYPEs), and alert on changes in response content structure.
  • Automated tests: Add security tests that attempt benign obfuscation variants in staging (dec_entities, hex_entities) so you catch decoding-based bypasses earlier.

12 - Real-world examples & impact

Example 1 - E-commerce inventory leak
An online retailer’s stock-check endpoint used dynamic SQL and returned results in the page. Attackers encoded UNION payloads and exfiltrated user credentials stored in an auxiliary table. The fix required parameterization and rotation of exposed credentials.

Example 2 - Vendor integration exploited via XML
A third-party XML integration presumed the payload was safe and performed entity expansion. Attackers used entity encoding to send obfuscated SQL payloads that the vendor backend decoded and executed. Blocking DOCTYPE and entity expansion prevented further abuse.

These illustrate why defenses must be layered: input validation, parameterized queries, parser safe defaults, and egress limitations.


13 - Report & remediation checklist (copy-paste)

  • Replace dynamic SQL in POST /product/stock with parameterized queries.
  • Whitelist productId and storeId formats (integers only).
  • Disable XML entity expansion or configure secure parser options.
  • Limit DB account privileges so web tier cannot access sensitive tables.
  • Add WAF exceptions for decoded entity attacks and log attempted encodings.
  • Rotate credentials if any sensitive data was extracted during tests.
  • Add regression tests for entity-encoded payloads in staging.

14 - Final thoughts

This lab is a great example of how a WAF may make you feel secure while a small gap - decoding downstream of the WAF - lets an attacker through. The cure is not a better signature; it’s correct coding: parameterize, validate, and restrict. From a learning perspective, understanding how encoding and execution differ across the stack is crucial to both offensive and defensive security.


References

  • PortSwigger Web Security Academy - SQL injection labs (UNION attacks, WAF bypass, XML encoding).
  • OWASP - XML External Entity (XXE) Prevention Cheat Sheet.
  • Burp Suite - Hackvertor extension (BApp Store) documentation.

Join the Security Intel.

Get weekly VAPT techniques, ethical hacking tools, and zero-day analysis delivered to your inbox.

Weekly Updates No Spam
Herish Chaniyara

Herish Chaniyara

Web Application Penetration Tester (VAPT) & Security Researcher. A Gold Microsoft Student Ambassador and PortSwigger Hall of Fame (#59) member dedicated to securing the web.

Read Next

View all posts

For any queries or professional discussions: herish.chaniyara@gmail.com