Knowledge

Step by Step: How SQL Injection created the MOVEit zero-day vulnerability for data breach

Take action: SQL Injection is one of the top 10 problems in all web applications. Always test your application for SQL injection, and build logic to prevent the exposure of SQL injection vulnerabilities. It takes several more days when developing, but saves months of problems when it will be exploited (and it will).


Learn More

Progress Software Corporation recently notified its customers about a critical vulnerability in its MOVEit Transfer and MOVEit Cloud products

MOVEit Transfer is a system designed to facilitate the storage and sharing of files within teams, departments, companies, or supply chains. Per the specification from Progress Software, it offers secure collaboration, automated file transfers, and advanced workflow automation capabilities without requiring scripting.

The vulnerability specifically affected the web-based front end of MOVEit, which allows users to share and manage files using a web browser. This method is generally considered more secure than sharing files via email, as it reduces the risk of files being misdirected or lost. However, it was discovered that the web-based front end had a SQL injection vulnerability.

What is an SQL Injection vulnerability?

SQL injection vulnerabilities occur when an HTTP request submitted to a web server is insecurely converted into a query command, which is then executed by the server to perform a database lookup and construct an appropriate HTTP reply. For instance, a database search triggered by a web page might result in a URL like

"https://search.example.com/?type=file&name=duck".

The "duck" text in the name parameter of the URL would be extracted, converted into database query syntax, and incorporated into a command sent to the database server. If the backend data is stored in a SQL database, the URL could be converted into a SQL command like the following:

SELECT filename FROM filesdb WHERE name LIKE '%duck%'

In this example, the "%" characters indicate that the search term can appear anywhere in the retrieved filename, and the single quote characters are used as markers to indicate a SQL text string. The resulting data from the query can then be formatted, converted to HTML, and sent back as an HTTP reply to the user's browser, potentially displaying a list of matching files for download.

However, a malicious user could create and request a URL like this: "https://search.example.com/?type=file&name=duck';DROP table filesdb;--". If the search term is blindly converted into a query string, it could trick the web server into sending a command to the SQL server that looks like this:

SELECT filename FROM filesdb WHERE name LIKE '%duck';DROP TABLE filesdb;--%'

In SQL, a semicolon acts as a statement separator, so the above command is equivalent to sending three consecutive commands:

SELECT filename FROM filesdb WHERE name LIKE '%duck' -- matches names ending duck
DROP TABLE filesdb -- deletes the entire database
--% -- comment, does nothing

The additional commands are effectively combined into a single destructive command that deletes the entire database. This demonstrates how SQL injection attacks can allow malicious actors to manipulate the database and potentially cause significant harm.

How does the MOVEit vulnerability work?

The web based interface of the MOVEit tool can be exploited to inject SQL commands into the system. This is paired with the native functionality of the tool being a file transfer tool so it will accept file upload from the customers. Combined these two facts open multiple mechanism for attack:

  1. Exfiltration of data in the MOVEit database - by mostly using SQL injection and SQL commands
  2. Gaining access to the system and upload malicious files like ransomware or remote access tools which then enable the attackers to go deeper into the MOVEit application and exfiltrate more data or inject more bad stuff

We will not show the real commands for obvious reasons, but the process for the second type of attack is:

  1. Use SQL injection to find the ID of an admin user from the MOVEit database
  2. Use SQL injection to find the session id for an admin user from the MOVEit database
  3. Craft http packets using the session id to establish a connection to the MOVEit web server impersonating an admin user
  4. Use the established session to upload remote access tool
  5. Use SQL injection to reconfigure the database to allow execution of the uploaded remote access tool
  6. Use SQL injection to execute the remote access tool
  7. The remote access tool enables a reverse shell (makes the MOVEit server connect remotely to a listener on your computer and open a command prompt).
  8. You own the MOVEit server.
What to do about MOVEit Servers?

The vulnerability in MOVEit Transfer was patched by Progress Software Corporation once it was discovered. The company released patches for all supported versions of MOVEit, including its cloud-based service. Therefore, if users are utilizing the cloud version, they should already have the updated and secure version. For those running MOVEit on their own network, it is recommended to ensure that the software is patched to the latest version to mitigate the vulnerability.

Unfortunately, the vulnerability in MOVEit was a zero-day, meaning that it was discovered after malicious actors had already been exploiting it. There is even an automated script that executes the attack while trying to compromise the several different flavours of SQL backend database that is used in MOVEit. The consequences of attacks can include deletion of existing data, exfiltration of sensitive information, modification of data, and implantation of new

What to do about SQL Injection?

Primary Defenses against SQL Injection when developing code

  1. Use a development framework that provides SQL injection controls out-of-the box. It's usually the easiest not to reinvent the wheel and use a well known framework which provides good SQL injection protection by way of it's implementation of database handling. A great example is Django Framework ORM mechanism which is designed to prevent SQL injection.
  2. Use Prepared Statements (with Parameterized Queries): Prepared statements are a method of writing database queries that help prevent SQL injection. They involve defining the SQL code first and then passing in parameters later. This separation of code and data makes it difficult for attackers to manipulate the query's intent. Prepared statements are available in various programming languages like Java, .NET, PHP, and more. Java example below:
String custname = request.getParameter("customerName"); 
String query = "SELECT account_balance FROM user_data WHERE user_name = ? "; PreparedStatement pstmt = connection.prepareStatement(query); 
pstmt.setString(1, custname); 
ResultSet results = pstmt.executeQuery();

3. Stored Procedures: Stored procedures are a database feature that allows defining and storing SQL code in the database itself. When used safely, they can provide protection against SQL injection. The stored procedures should be created to accept parameters and use them in the query, rather than dynamically generating SQL code. This approach is available in languages like Java and .NET. Java example below:

String custname = request.getParameter("customerName");
try { CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
cs.setString(1, custname);
ResultSet results = cs.executeQuery();
// ... }
catch (SQLException se)
{ // error handling }

4. Allow-list Input Validation: Input validation involves validating and ensuring that user-supplied input matches the expected format or values. In cases where certain parts of the SQL query cannot use parameterized queries, such as table or column names, input validation can be used. It maps user input to expected values to avoid unvalidated input being used in the query. However, it is essential to note that input validation should always be used as a secondary defense in combination with other methods.

String tableName;
switch(PARAM):
case "Value1": tableName = "fooTable"; break;
case "Value2": tableName = "barTable"; break;
...
default : throw new InputValidationException("unexpected value provided for table name");

5. Escaping All User-Supplied Input: Escaping user-supplied input involves modifying the input by adding escape characters to ensure it is treated as data rather than executable code. This technique is used as a last resort when other defenses are not feasible. It is database-specific and should be used cautiously, as it can be prone to vulnerabilities. Using established libraries or APIs for database-specific encoding/escaping is recommended.

String query = "SELECT user_id FROM user_data WHERE user_name = '" + ESAPI.encoder().encodeForSQL(ORACLE_CODEC, req.getParameter("userID")) + "' and user_password = '" + ESAPI.encoder().encodeForSQL(ORACLE_CODEC, req.getParameter("pwd")) + "'";

It's important to note that a combination of these defenses is often the best approach for mitigating SQL injection attacks. Also, don't forget to reduce privileges of the SQL server and Web server to a normal user, since the execution of a malicious payload will have much less effect if the user executing the program (the SQL server user) has limited powers.

Step by Step: How SQL Injection created the MOVEit zero-day vulnerability for data breach