Module 8: SQL Injection Fundamentals
Introduction
Introduction
Most modern web applications sit on top of a database-backed architecture. User requests reach the application, the application builds database queries, and the database returns data used to generate the response.
In practice, this looks like a three-tier flow:
User interacts with the client
Client sends HTTP(S) requests to an application server
Application server issues queries to a database (DBMS)
When user-supplied input is included in those queries, the database becomes an attack surface.
Example: Where SQL Injection Enters the Flow
A typical request cycle:
User submits data through a form or URL parameter
Application uses that data to build an SQL query
Query is sent to the database
Database response is returned to the user
If the application does not strictly control how input is handled, an attacker can supply input that changes the structure of the SQL query itself, not just its values. This is the core of SQL Injection (SQLi).
SQL Injection (SQLi)
SQL injection is an attack against relational databases (such as MySQL) where attacker-controlled input alters the final SQL query executed by the database.
This module focuses on MySQL-based SQL injection. Injection techniques against non-relational databases (for example, MongoDB) fall under NoSQL injection and are out of scope here.
Example: Escaping Intended Input Boundaries
Most SQL injection attacks begin by breaking out of expected input boundaries.
In the simplest case, this is done by injecting:
A single quote
'A double quote
"
These characters are commonly used to delimit string values in SQL. If input is not handled safely, injecting one of them can terminate the intended value and allow new SQL syntax to be appended.
Once injection is possible, the attacker focuses on:
Modifying the original query’s logic
Or appending additional SQL statements
This can be done using techniques such as:
UNION queries
Stacked queries
Conditional logic
The attacker then observes how the application responds to infer or extract data.
Use Cases and Impact
SQL injection has wide impact when database permissions are lax or application logic is fragile.
Common outcomes include:
Data disclosure Usernames, passwords, personal data, and financial information can be retrieved directly from the database.
Authentication bypass Login checks can be subverted, granting access without valid credentials.
Privilege escalation Restricted functionality, such as administrative panels, may become accessible.
Server-side compromise In some configurations, attackers may read or write files on the backend system, leading to persistent access and full application takeover.
These outcomes explain why SQL injection remains one of the most damaging web vulnerabilities.
Prevention (Context for Later Sections)
SQL injection vulnerabilities arise from:
Unsafe query construction
Missing input validation
Excessive database privileges
Later sections focus on secure coding practices, including:
Input validation and sanitization
Proper query construction
Restrictive database permissions
These defenses are discussed after exploitation techniques so the attack paths are fully understood first.
Databases
Intro to Databases
Before SQL injection makes sense operationally, you need a working mental model of what databases are, how applications talk to them, and where attacker-controlled input enters that flow.
This section establishes that baseline so later SQLi examples are reconstructible rather than abstract.
Databases in Web Applications
Web applications rely on back-end databases to persist almost everything they operate on, including:
Application assets (images, files, metadata)
Content (posts, comments, updates)
User data (usernames, passwords, session-related data)
Each incoming request that needs data triggers database interaction. The application builds a query, sends it to the database, and uses the response to construct what the user sees.
This query construction step is where injection becomes possible.
Database Management Systems (DBMS)
As applications grew beyond small, file-based storage, Database Management Systems (DBMS) replaced direct file handling.
A DBMS is responsible for:
Creating and defining databases
Storing and retrieving data efficiently
Enforcing rules around access and consistency
Over time, different DBMS types emerged for different workloads:
File-based databases
Relational DBMS (RDBMS)
NoSQL databases
Graph databases
Key/value stores
In this module, the focus is on relational DBMS, since that is where classic SQL injection applies.
Example: How Applications Interact with a DBMS
Applications do not interact with databases directly in one fixed way. Common interaction methods include:
Command-line database clients
Graphical database management tools
Application-level APIs and drivers
In real deployments, applications use database drivers or libraries that translate application logic into database queries.
This abstraction is important: the application developer writes code, not raw database commands, yet unsafe input handling can still alter the final query.
DBMS Core Capabilities (Why They’re Used)
A DBMS exists to solve problems that appear immediately at scale:
Concurrency Multiple users can read and write data at the same time without corruption.
Consistency The database enforces rules so data remains valid even under heavy access.
Security Authentication and permission systems restrict who can view or modify data.
Reliability Databases can be backed up and restored after failure or compromise.
Structured Query Language SQL provides a standardized way to insert, retrieve, update, and delete data.
SQL injection targets the last point: how SQL is constructed and executed.
Architecture: Where the Database Sits
In most modern applications, databases are part of a multi-tier architecture.
A common layout:
Tier I – Client Web browser or client application handling user interaction.
Tier II – Application Server Middleware that processes requests, applies logic, and builds database queries.
Tier III – DBMS Database engine that executes queries and returns results.
The application server:
Receives user input
Translates it into database operations
Sends those operations to the DBMS
Returns results to the client
This separation is critical: user input almost never reaches the database directly, but SQL injection happens when the application server mishandles how that input is incorporated into queries.
Example: Deployment Variations
In small deployments:
Application server and DBMS may run on the same host
In larger environments:
The DBMS is hosted separately
Performance, scalability, and isolation improve
The attack surface expands due to networked components
Regardless of deployment style, SQL injection is possible anywhere user input influences query construction.
This architectural understanding is the foundation for recognizing where SQL injection occurs, why it works, and how later examples exploit it.
Types of Databases
Databases are broadly categorized into Relational and Non-Relational systems. This distinction determines how data is stored, queried, and whether SQL injection is even applicable.
Relational Databases
Relational databases store data in tables with a fixed structure. Each table represents a specific entity, and tables are linked together using keys.
users
1
alice
Alice
Smith
2
bob
Bob
Johnson
3
carol
Carol
Lee
posts
10
1
2021-01-01
Welcome to this web application
11
2
2021-01-02
This is the first post
12
1
2021-01-03
Reminder: maintenance tonight
In this structure:
users.iduniquely identifies each userposts.user_idreferences the author of each postOne user can be associated with multiple posts
User data is stored once and referenced elsewhere
The collection of tables and their relationships is called the schema. Queries commonly join tables using these relationships, which is where SQL injection becomes possible if input is handled unsafely.
Relational databases are optimized for:
Large, structured datasets
Predictable relationships
Efficient querying using SQL
Common relational database platforms include MySQL, PostgreSQL, SQL Server, Oracle, and Microsoft Access. This module focuses on MySQL, though the concepts apply broadly.
Non-Relational Databases
Non-relational databases do not use tables, rows, columns, or fixed schemas. Each record is typically stored as a self-contained object.
In this model:
Keys identify records
Values may be strings, objects, or nested structures
Relationships are handled by application logic, not enforced by the database
Structure can vary between records
This storage pattern resembles dictionary or map structures found in languages like Python or PHP.
A common non-relational database implementation is MongoDB.
Injection Implications
Relational databases → vulnerable to SQL injection when queries are constructed unsafely
Non-relational databases → vulnerable to NoSQL injection, which uses different techniques and payloads
SQL injection and NoSQL injection are distinct attack classes and are treated separately.
This distinction is critical for recognizing when SQL injection is relevant and why subsequent sections focus exclusively on relational databases.
MySQL
Intro to MySQL
MySQL is a relational database system. Web applications send SQL statements to MySQL to store and retrieve structured data.
Connecting to MySQL
mysql– command-line client for MySQL/MariaDB-u root– database username (rootis a superuser)-p– prompt for password instead of passing it inline
mysql>– interactive SQL promptCommands typed here are sent directly to the database
Remote connection:
-h– database host-P 3306– TCP port (default MySQL port)
Databases
A single MySQL server can host multiple databases.
Lists all databases accessible to the current user
Create a new database:
Select it:
All table operations now apply to
users
Tables
Tables define how data is stored inside a database.
logins– table nameid INT– numeric identifierVARCHAR(100)– string up to 100 charactersDATETIME– date and time value
List tables:
Inspect table structure:
Field– column nameType– expected data type for that column
This output shows exactly where user input ends up when inserted.
Column Constraints
Constraints control what data is allowed into each column.
Auto-incrementing identifier:
NOT NULL– value is requiredAUTO_INCREMENT– value increases automatically per row
Unique usernames:
UNIQUE– prevents duplicate values
Default timestamp:
DEFAULT NOW()– assigns current date/time automatically
Primary key:
Uniquely identifies each row
Used internally for indexing and lookups
Final Table Definition
This table shape is typical for authentication data:
One row per user
Predictable lookup by
idorusernameCommon target for SQL injection when input is used unsafely
SQL Statements
INSERT Statement
Example 1: Insert a full row when all columns require values
What is visible
A new row is added.
All columns are populated in column order.
This only works when every column is provided and order matches the table schema.
Key decoding
INSERT INTO <table>selects the target table.VALUES (...)must align positionally with all columns.
Example 2: Insert selectively into specific columns
What is visible
idanddate_of_joiningare auto-filled.Only specified columns receive values.
Key decoding
(username, password)explicitly defines which columns are written.Columns with defaults can be skipped.
Columns marked
NOT NULLwithout defaults cannot be skipped.
Example 3: Insert multiple rows in one statement
What is visible
Two new rows are inserted in a single query.
Row count reflects multiple insertions.
Key decoding
Each parenthesized set represents one row.
Commas separate rows.
SELECT Statement
Example 1: View all columns and rows
Visible result
Key decoding
*is a wildcard meaning all columns.FROMspecifies the source table.
Example 2: View only specific columns
Visible result
What changed
Only selected columns appear.
Row count remains unchanged.
DROP Statement
Example: Remove a table completely
Visible effect
Table no longer exists.
Key decoding
DROP TABLEpermanently deletes the table structure and data.No confirmation or undo.
ALTER Statement
Example 1: Add a new column
Visible effect
Table gains a new column with default
NULLvalues.
Example 2: Rename a column
What changed
Column name updates.
Data remains intact.
Example 3: Modify column datatype
What changed
Column datatype changes.
Stored values must be compatible.
Example 4: Drop a column
Visible effect
Column and its data are permanently removed.
UPDATE Statement
Example: Update specific rows using a condition
Visible result
What is visible
Only rows matching
id > 1change.Rows not matching the condition remain unchanged.
Key decoding
SETdefines new values.WHERErestricts which rows are modified.Omitting
WHEREupdates all rows.
Query Results
Sorting Results (ORDER BY)
Example 1: Sort rows by a single column (default ascending)
Visible result
What is visible
All rows remain.
Row order changes based on
password.Ascending order is implied when no direction is specified.
Inline decoding
ORDER BY <column>sorts result rows after selection.Default sort direction is
ASC.
Example 2: Sort in descending order
Visible result
Inline decoding
DESCreverses the sort order.ASCcan be written explicitly but is optional.
Example 3: Sort by multiple columns
Visible result
What is visible
Primary sort uses
password DESC.Rows with equal passwords are secondarily sorted by
id ASC.
Limiting Results (LIMIT)
Example 1: Return only the first N rows
Visible result
Inline decoding
LIMIT <count>caps the number of returned rows.Without
ORDER BY, row order depends on storage or execution plan.
Example 2: Use offset with LIMIT
Visible result
Inline decoding
First number is the offset.
Offset starts at
0.LIMIT 1, 2skips the first row, then returns two rows.
Filtering Results (WHERE)
Example 1: Filter numeric values
Visible result
What is visible
Rows not matching the condition are excluded.
Table structure remains unchanged.
Example 2: Filter string values
Visible result
Inline decoding
String and date values require quotes.
Numeric values do not.
Pattern Matching (LIKE)
Example 1: Match values starting with a prefix
Visible result
Inline decoding
%matches zero or more characters.Pattern matching is applied after row filtering.
Example 2: Match fixed-length strings
Visible result
Inline decoding
_matches exactly one character.Three underscores require a three-character string.
SQL Operators
AND Operator
Example: both conditions must be true
Visible result
Visible result
Inline decoding
ANDevaluates both expressions.Result is true (
1) only if both sides are true.MySQL treats
1as true and0as false.
OR Operator
Example: at least one condition must be true
Visible result
Visible result
Inline decoding
ORreturns true if any expression is true.Returns false only when all expressions are false.
NOT Operator
Example: invert a boolean expression
Visible result
Visible result
Inline decoding
NOTflips the result of the expression.True becomes false, false becomes true.
Symbol Operators
Example: symbolic equivalents
Inline decoding
&&is equivalent toAND||is equivalent toOR!and!=represent logical negation and not-equal comparison
Operators in Queries
Example: exclude a specific value
Visible result
Example: combine conditions
Visible result
What changed
Rows must satisfy both conditions simultaneously.
Any row failing one condition is excluded.
Operator Precedence
Precedence order (highest to lowest)
/,*,%+,-=,>,<,<=,>=,!=,LIKENOT,!AND,&&OR,||
Example: precedence in action
Step 1: arithmetic evaluated first
becomes:
Step 2: comparisons evaluated
username != 'tom'id > 1
Step 3: logical AND applied
Visible result
DESCRIBE Statement (Schema Inspection)
Example: inspect column names and datatypes
Visible result
Inline decoding
DESCRIBE <table>returns metadata, not row data.Confirms column names, datatypes, nullability, defaults, and keys.
Commonly used before writing
INSERT,UPDATE,WHERE, orALTERstatements to avoid schema assumptions.
SQL Injections
Intro to SQL Injections
Use of SQL in Web Applications
Example: executing SQL inside a PHP application
What is visible
SQL is constructed as a string inside application code.
The database executes whatever SQL string is provided.
Example: consuming query results
What is visible
Each returned row is iterated and printed.
The application fully trusts the query result.
Example: SQL query built using user input
What is visible
User input is concatenated directly into the SQL string.
No validation, escaping, or sanitization is applied.
Inline decoding
$searchInputbecomes part of the SQL syntax, not just data.Any characters the user provides are interpreted by the SQL engine.
What Is an Injection?
Example: intended behavior
User input:
Generated SQL:
What is visible
Input is treated as plain text.
Query logic remains unchanged.
Example: breaking input boundaries
User input:
Generated SQL:
What is visible
The single quote (
') closes the original string.SQL after the quote is interpreted as executable code.
The final trailing quote causes a syntax error.
Inline decoding
'escapes the string context.;attempts to terminate the first statement.Injected SQL alters intended execution flow.
SQL Injection
Definition shown through behavior
Injection occurs when:
User input is inserted into SQL without sanitization.
The database interprets user input as SQL code.
The executed query differs from the developer’s intent.
Example: unsafe query construction
What is visible
The SQL engine cannot distinguish user data from SQL syntax.
Any special characters are processed as part of SQL parsing.
Syntax Errors During Injection
Example: malformed injected query
Result
What is visible
An extra unmatched quote breaks SQL parsing.
Execution stops with a syntax error.
Inline decoding
Successful injection requires syntactically valid SQL.
Attackers must neutralize or comment out remaining query text.
Why Injection Is Still Possible Without Source Code
Observed constraints
Attackers usually do not see the original query.
Input is often embedded mid-query, not at the end.
Observed solutions
Use SQL comments to ignore trailing query parts.
Balance quotes to preserve valid SQL syntax.
(These mechanisms are demonstrated in later sections.)
Types of SQL Injections
Classification by output visibility
In-band SQL Injection
Output is returned directly in the response.
Types
Union-based
Error-based
Blind SQL Injection
No direct output is shown.
Data is inferred through application behavior.
Types
Boolean-based
Time-based
Out-of-band SQL Injection
Output is exfiltrated through external channels.
Examples include DNS or HTTP callbacks.
Subverting Query Logic
Authentication Bypass (Baseline Behavior)
Example: intended authentication query
What is visible
ANDrequires both conditions to be true.A matching row means authentication succeeds.
No matching row means authentication fails.
Example: incorrect credentials
What is visible
username='admin'is true.password='admin'is false.TRUE AND FALSEevaluates to false.No rows returned. Login fails.
SQLi Discovery (Testing for Injection)
Example: inject a single quote
User input (username field):
Resulting query:
Visible result
SQL syntax error is returned.
Application behavior changes from “Login failed” to an error.
Inline decoding
An odd number of quotes breaks SQL parsing.
This confirms user input is inserted directly into the query.
The parameter is injectable.
OR Injection (Logic Subversion)
Objective
Make the
WHEREclause evaluate to true regardless of password.
Key precedence rule (from earlier)
ANDis evaluated beforeOR.
Order:
Comparisons
ANDOR
Crafting an Always-True Condition
Example: constant true condition
Always evaluates to true.
Injected username payload
Why this form
Removes the closing quote.
Uses the original trailing quote to balance syntax.
Resulting Query
Logical Evaluation (Step-by-Step)
Step 1: AND evaluated first
Step 2: OR evaluated
Final outcome
Query returns at least one row.
Authentication succeeds.
Password check is effectively bypassed.
Auth Bypass in Practice (Known Username)
Injected username
Injected password
Executed query
What is visible
Login succeeds as
admin.
Auth Bypass (Unknown Username)
Injected username
Resulting query
Evaluation
username='notAdmin'→ FALSE'1'='1' AND password='something'→ FALSEFALSE OR FALSE→ FALSE
Visible result
No rows returned.
Login fails.
OR Injection in Password Field
Injected password
Resulting query
Logical Evaluation
'1'='1'→ TRUEMultiple
ORconditions ensure the fullWHEREclause evaluates to true.Entire table is returned.
Application logs in the first row (commonly
admin).
Minimal Payload Auth Bypass
Injected username
Injected password
Resulting query
What is visible
Conditions always evaluate to true.
Authentication succeeds without knowing any credentials.
Key Observations
ORcan override authentication logic when combined with operator precedence.Balancing quotes is critical to avoid syntax errors.
No comments are required in this example, only logical subversion.
Returned row order determines which user is logged in.
Using Comments
SQL Comments
Example: line comment with --
Visible result
Inline decoding
Everything after
--is ignored by the SQL engine.A space after
--is required for the comment to begin.In URL contexts, the space is commonly encoded as
+, resulting in--+.
Example: line comment with #
Visible result
Inline decoding
#comments out the remainder of the query line.In browsers,
#must be URL encoded as%23or it will be treated as a fragment identifier.Any SQL logic after the comment marker is ignored during execution.
Authentication Bypass Using Comments
Original authentication query
Injected username
Resulting query
What is visible
The username comparison remains intact.
The password condition is commented out and never evaluated.
The query is syntactically valid.
Effect
The database returns the admin row.
Authentication succeeds without validating the password.
Why Comment Injection Works Here
At the point where user input is injected:
The closing quote ends the string.
The comment marker causes the SQL parser to ignore the remainder of the statement.
No additional logical manipulation is required.
Parentheses and Enforced Conditions
More restrictive authentication query
What this enforces
Parentheses force
usernameandidto be evaluated together.id > 1blocks the admin account (id = 1).Password is hashed before comparison, preventing password-field injection.
Valid admin credentials still fail
Evaluation
username='admin'→ TRUEid > 1→ FALSE(TRUE AND FALSE)→ FALSELogin fails.
Failed Comment Injection Due to Syntax
Injected username
Resulting query
What is visible
SQL syntax error is returned.
Inline decoding
The comment removes the remainder of the expression.
The opening parenthesis
(is never closed.SQL parsing fails before execution.
Balancing Parentheses to Restore Valid SQL
Injected username
Resulting query
What is visible
The injected
)closes the open parenthesis.All remaining logic is commented out.
The query is syntactically valid again.
Effective Executed Query
Effect
Only the username check is evaluated.
The admin row is returned.
Authentication is bypassed successfully.
Union Clause
UNION (Baseline Behavior)
Example: view contents of a single table
Visible result
Example: view contents of another table
Visible result
UNION Combining Results
Example: combine two SELECT statements
Visible result
Inline decoding
UNIONmerges result sets vertically.Output column names are taken from the first SELECT.
Rows from both tables appear in a single result set.
Column Count Requirement
Example: mismatched column counts
Visible result
Inline decoding
Every SELECT in a UNION must return the same number of columns.
Column datatypes must also be compatible by position.
UNION in SQL Injection Context
Original vulnerable query
Injected payload
Final executed query
What is visible
Results from
productsandpasswordsare merged.Data from an unrelated table is returned in the same response.
Handling Uneven Columns (Padding)
Constraint
Original query and injected query must return the same number of columns.
Example: original query returns two columns
Injected UNION selecting only one real column
Final query
Visible result
Inline decoding
2is filler data to satisfy column count.Filler values must match the expected datatype.
Numeric fillers are commonly used for simplicity.
Padding with Multiple Columns
Example: original query returns four columns
Injected UNION
Final executed query
Visible result
Inline decoding
Attacker-controlled data appears in the column position of
username.Numeric fillers occupy unused columns.
Column position determines where extracted data is visible.
Using NULL as Filler
Example
Inline decoding
NULLis valid for most datatypes.Commonly used in real-world SQL injection.
Reduces datatype mismatch errors.
Practical Outcome of UNION Injection
What UNION enables
Extract data from arbitrary tables.
Combine attacker-selected data with legitimate query output.
Read database contents directly through application responses.
Constraint that always applies
Column count and datatype alignment must match the original query.
Union Injection
Observable baseline behavior
Initial request (no injection)
Result shows three visible fields rendered as a table:
Port Code
Port City
Port Volume
Example rows rendered:
Error-based discovery (confirming injection point)
Injected input
Observed behavior
The page returns a SQL syntax error instead of results.
Key observable change:
Normal table output disappears
SQL error message is rendered
This establishes:
User input is concatenated into a SQL query
Errors are reflected to the client
Union-based injection is viable because query results are normally rendered
Detecting number of columns using ORDER BY
ORDER BY mechanics (decoded inline)
ORDER BY nmeans “sort by the nth selected column”If
nexceeds the number of columns in the SELECT statement, the database errors--begins a SQL commentThe trailing space after
--is required by many SQL parsers
ORDER BY 1
Observed behavior
Page renders normal table output
No error
Conclusion:
At least 1 column exists
ORDER BY 2
Observed behavior
Table still renders
Row ordering changes
Conclusion:
At least 2 columns exist
Sorting affects visible output, confirming the column is rendered
ORDER BY 3 and ORDER BY 4
Observed behavior
Table continues to render
Ordering continues to change
Conclusion:
At least 4 columns exist
ORDER BY 5 (failure point)
Observed behavior
SQL error: unknown column in ORDER BY clause
Table output disappears
Conclusion:
The SELECT statement contains exactly 4 columns
Highest successful ORDER BY index equals column count
Detecting number of columns using UNION
UNION mechanics (decoded inline)
UNION SELECTmust match:Number of columns
Column order
Mismatch causes an error instead of output
This method fails until the column count is correct
UNION with 3 columns (failure)
Observed behavior
SQL error stating mismatched column count
No table output
Conclusion:
Fewer than required columns
UNION with 4 columns (success)
Observed behavior
Table renders
Visible output shows:
2 | 3 | 4
Conclusion:
UNION column count matches original query
Confirms 4 total columns
Identifying visible (printed) columns
Observing which columns render
Injected payload returns values 1, 2, 3, 4, but only the following appear:
Observable facts:
Column 1 does not render
Columns 2, 3, and 4 render to the page
Conclusion:
Injection output must be placed in columns 2–4
Any data in column 1 will not be visible
Verifying data extraction (non-numeric test)
Purpose of numeric placeholders
Numeric literals (
1,2,3,4) make column position mapping visibleReplacing one literal with a function tests real data extraction
Version extraction test
Inline decoding:
@@versionreturns the database server version string
Observed behavior
Rendered output includes:
Conclusion:
Arbitrary query output can be rendered
Column 2 is a valid injection location
Union-based extraction is confirmed functional
Exploitation
Database Enumeration
MySQL fingerprinting via observable behavior
Version string extraction (full output available)
Inline decoding:
@@versionreturns the DBMS version string if supportedRequires visible (printed) column placement
Observed output
Conclusion:
DBMS is MariaDB (MySQL-compatible)
Direct query output is available
No blind or numeric-only fingerprinting required
Alternative MySQL-only probes (behavior reference)
Returns
1on MySQLErrors on non-MySQL DBMS
Delays response by ~5 seconds on MySQL
No delay on non-MySQL DBMS
(No execution required here due to direct version output already observed.)
INFORMATION_SCHEMA usage (cross-database enumeration)
Inline decoding:
INFORMATION_SCHEMAis a metadata databaseTables must be referenced with
database.tableDefault query context remains the application database
Enumerating databases (SCHEMATA)
Local reference query
Observed structure
Inline decoding:
SCHEMA_NAMEholds database namesFirst three are default system databases
Injection-based enumeration
Observed output
Conclusion:
Non-default databases:
ilfreight,dev
Identifying active database
Inline decoding:
database()returns the current query context database
Observed output
Conclusion:
Application queries run against
ilfreightdevis a separate database of interest
Enumerating tables (TABLES)
Inline decoding:
TABLE_NAMEis the table identifierTABLE_SCHEMAindicates owning databaseFiltering avoids cross-database noise
Observed output
Conclusion:
Four tables exist in
devcredentialsis a high-value target
Enumerating columns (COLUMNS)
Inline decoding:
COLUMN_NAMElists field namesFiltering by table name narrows scope
Observed output
Conclusion:
credentialstable containsusernameandpassword
Dumping table data
Inline decoding:
dev.credentialsusesdatabase.tablenotationInjected fields must occupy printed columns
Observed output
Conclusion:
Full data extraction achieved
Sensitive credentials successfully retrieved
Reading Files
Privilege requirements (observable constraints)
Inline decoding:
File read operations in MySQL require the
FILEprivilegePrivileges are bound to the database user, not the web application user
DBA or superuser roles commonly include
FILE
Identifying current database user
User identification queries
Injection variant (printed column placement):
Observed output
Conclusion:
Active DB user is
rootHighly likely to be a DBA-level account
Enumerating user privileges
Superuser privilege check
Inline decoding:
super_priv = 'Y'indicates superuser status
Observed output
Conclusion:
Superuser privileges confirmed
Full privilege enumeration
Inline decoding:
information_schema.user_privilegeslists granted global privilegesgranteeidentifies the user-host tuple
Observed output (sample)
Conclusion:
FILEprivilege is presentFile read operations are permitted
Reading files with LOAD_FILE()
LOAD_FILE mechanics (decoded inline)
LOAD_FILE(path)returns file contents as a stringRequires:
FILEprivilegeOS-level read permission for MySQL process
Output must be placed in a printed column
Reading /etc/passwd
Observed output (excerpt)
Conclusion:
Arbitrary system file read confirmed
DBMS runs with sufficient OS privileges
Reading application source code
Target identification
Observable facts:
Current endpoint:
search.phpDefault Apache web root:
/var/www/html
Reading PHP source
Observed behavior
Browser renders HTML output
Viewing page source (
Ctrl+U) reveals embedded PHP source
Observed content (summary)
PHP code constructs SQL query using
port_codeDirect string concatenation used
Database query execution visible
Conclusion:
Full application source disclosure achieved
Credentials and additional vulnerabilities may be extractable
Writing Files
Preconditions for file writes (MySQL/MariaDB)
Inline decoding:
FILEprivilege: required for read/write file functions andINTO OUTFILEsecure_file_priv: server-side restriction on allowed read/write directoriesOS permissions: the MySQL process user must be able to write to the target path
Required conditions (as stated in source):
User has
FILEprivilegesecure_file_privis not restricting writes (""empty) and notNULLTarget directory is writable by the MySQL process user
Checking secure_file_priv (write restriction control)
secure_file_priv (write restriction control)Baseline query (non-UNION)
Constraint (decoded inline):
SHOWis not directly embedded inside aUNION SELECTpayloadUse
information_schema.global_variablesinstead
information_schema.global_variables fields:
variable_namevariable_value
Targeted query:
UNION injection payload (4-column requirement preserved via junk data):
Observed output
Rendered row includes:
Observable inference from source statement:
The
variable_valueis empty (""), meaning no directory restriction
Conclusion:
secure_file_priv = ""(empty) permits read/write anywhere (subject to OS permissions)
Writing files with SELECT ... INTO OUTFILE
SELECT ... INTO OUTFILEInline decoding:
INTO OUTFILE '/path/file'writes the full result set of theSELECTto a new fileThe file is created by the MySQL process user
The written content is the row output of the SELECT, not just the injected string
Exporting table output to a file (server-side proof)
Observed file content (as shown via shell on server):
Writing an arbitrary string to a file (standalone proof)
Observed file content:
Observed file metadata:
Conclusion:
File is owned by
mysql:mysqlConfirms DBMS writes as the MySQL service OS user
Tip (decoded inline):
FROM_BASE64("...")can be used to write longer or binary content by decoding base64 server-side
Writing files through SQL injection (webroot write test)
Target path:
Web root assumed:
/var/www/html(Apache default)
Goal:
Write a proof file accessible via the web application
Standalone SQL:
UNION injection payload (4 columns):
Observed browser response
Empty table rendered
No error message displayed
Observed verification via web access
File exists and is readable through the web server
Rendered content shows the full UNION row:
Conclusion:
Write succeeded into webroot
INTO OUTFILEwrote the entire UNION result set, including junk columns
Cleanup refinement (decoded inline):
Replace numeric junk with empty strings to avoid extra tokens in file output
Writing a PHP web shell (RCE via webroot)
Web shell content:
Inline decoding:
system(...)executes an OS command$_REQUEST[0]reads parameter0from query string or POST body
UNION injection payload (clean output via empty strings):
Observed browser response
Empty table rendered
No error message displayed
Observed command execution via web request
Visiting
/shell.php?0=idreturns command output
Conclusion:
Remote command execution confirmed
Commands run under the web server OS account (
www-data)
Mitigations
Mitigating SQL Injection
Input sanitization (escaping special characters)
Vulnerable pattern (direct string concatenation)
Inline decoding:
$_POST['username'],$_POST['password']: user-controlled request body inputsQuery is built by concatenating raw input into SQL text
Any injected quote (
'/") changes SQL structure because it is interpreted as SQL syntax
Observable behavior in prior exploit model
Injected quotes cause SQL errors or allow altered predicates
Query text is printed via
echo "Executing query: " ..., making successful injection easier to iterate
Escaping input with mysqli_real_escape_string()
Inline decoding:
mysqli_real_escape_string($conn, $value)escapes characters like'and"so they are treated as data, not SQL delimiters
Observed behavior (as stated)
Injection attempts fail because embedded quotes are escaped, preventing SQL structure changes
Related function (identifier fidelity):
pg_escape_string()serves the same purpose for PostgreSQL
Input validation (rejecting nonconforming data)
Vulnerable pattern (direct use of GET parameter)
Inline decoding:
$_GET["port_code"]: user-controlled URL parameterilike: case-insensitive pattern match (PostgreSQL)'%...%': wildcard match, expands attack surface because injected symbols are inside SQL text
Restricting allowed characters with preg_match()
Inline decoding:
preg_match($pattern, $code): returns whether$codematches the regex/^[A-Za-z\s]+$/:^start of string[A-Za-z\s]+one or more letters or whitespace$end of string
Any other character (e.g.,
',;,-) triggers termination viadie(...)
Injection test payload (rejected):
Observed behavior (as stated)
Input containing injected query tokens is rejected by the server
Normal search UI remains, but injected request does not execute
User privileges (least privilege DB accounts)
Inline decoding:
DBMS users can be restricted to specific operations and objects
Web applications should not use superusers/DBA accounts
Creating a restricted MariaDB user
Inline decoding:
'reader'@'localhost': DB user bound to local host originGRANT SELECT ON ilfreight.ports: only allows reading that single tableIDENTIFIED BY '...': sets password at grant time (as shown)
Verifying restricted access
Login:
Listing allowed table after selecting database:
Enumerating databases (limited visibility shown):
Attempting unauthorized table read:
Conclusion:
Application user can access only the required
portstableAccess to
credentialsis denied, containing blast radius if SQL injection exists
Web Application Firewall (WAF)
Inline decoding:
WAF inspects HTTP requests for malicious patterns and blocks them before reaching application logic
Examples:
ModSecurity (open-source)
Cloudflare (premium)
Rule example (as stated):
Requests containing
INFORMATION_SCHEMAmay be rejected due to common SQLi enumeration patterns
Parameterized queries (prepared statements)
Vulnerable pattern replacement: placeholders + bind
Inline decoding:
?placeholders: positions for user data, not SQL textmysqli_prepare(...): compiles SQL with placeholdersmysqli_stmt_bind_param($stmt, 'ss', ...):'ss'means two string parametersvalues are supplied separately from SQL structure
Driver escapes and binds values so injected quotes do not alter SQL grammar
Observable effect (as stated)
Quotes in input are treated as literal characters inside parameter values, not delimiters
Last updated