FlawOpen

flawopen.com/Incidents/moveit-transfer-sql-injection-may-2023

● Progress Software · CVE-2023-34362 · May 2023 · CVSS 9.8 · Critical
FlawOpen Security Research

MOVEit Transfer: SQL injection and mass data theft

CVE-2023-34362 was unauthenticated SQL injection in MOVEit Transfer. Mandiant observed exploitation from 27 May 2023, followed by LEMURLOOT web shells and data theft.[4], [3]

💡 Plain English Explainer (ELI5)

Imagine a form has a “folder number” field, but a clerk copies it directly into an instruction sent to the filing system. If the field can change the instruction instead of naming a folder, the requester can make the system do more than intended. SQL injection crosses that same boundary: untrusted input is treated as SQL syntax instead of a value.[5]

Core Concepts & Subsystem Terms

SQL injection (CWE-89)
Input that is concatenated into a database query changes what the query does, instead of being treated as a value.[5]
Managed file transfer (MFT)
Software organisations use to exchange large or sensitive files with partners. One server can hold many organisations' data.[3]
Web shell
A script an attacker plants on a web server to keep access. Mandiant tracked the one used here as LEMURLOOT.[3]
Parameterised query
A query whose values are sent separately from its text, so the database never parses data as SQL.[5]
Affected and fixed releases
Progress lists releases before 2021.0.6, 2021.1.4, 2022.0.4, 2022.1.5, and 2023.0.1 as affected on their respective branches; those listed releases contain the fix. Confirm your branch and current upgrade path in the Progress advisory.[1]

Framework mappings

Incident Timeline

2023-05-27

Mandiant reports the earliest evidence it observed of MOVEit Transfer exploitation: 27 May 2023.[3]

2023-05-31

Progress discloses the vulnerability and urges customers to cut off HTTP and HTTPS traffic until patched.[1]

2023-06-02

CISA adds CVE-2023-34362 to its Known Exploited Vulnerabilities catalog.[9]

2023-06-07

CISA and the FBI publish joint advisory AA23-158A describing the campaign as CL0P ransomware activity. Mandiant later updated its attribution, merging UNC4857 into FIN11 based on overlaps.[2], [3]

Step-by-Step Attack Flow

Step 1

An unauthenticated request

Mandiant observed crafted POST requests to MOVEit Transfer’s guestaccess.aspx before LEMURLOOT appeared; the activity did not require a normal user login.[3]

Step 2

Input changes query meaning

The request reached a vulnerable SQL operation. CISA described the flaw as allowing unauthenticated database access, including inference or alteration/deletion depending on the database engine.[2], [5]

Step 3

A web shell is installed

After exploitation, Mandiant observed the LEMURLOOT web shell. It could enumerate and retrieve files and manipulate a named application user; the sources do not establish generic administrative control.[3]

Step 4

Files are stolen in bulk

Mandiant observed large-scale file theft. CISA and the FBI described the broader campaign as CL0P ransomware activity; Mandiant’s later attribution update linked the activity to FIN11.[2], [3]

Source Code: Flaw vs. Secure Implementation

Illustrative C#, not MOVEit source. The hardened method parses the identifier into the type the column actually holds and rejects anything else, then sends it as a typed parameter, so the value is never part of the SQL text. Either control alone stops this injection; together they also reject malformed identifiers before the query runs.[5]

UNPATCHED FLAW
using System.Data.SqlClient;

public static class FolderLookup
{
    public static SqlDataReader Find(SqlConnection conn, string folderId)
    {
        // Query text and request data are concatenated, so the database
        // cannot tell which part the developer intended as data.
        string sql = "SELECT id, name FROM folders WHERE id = '" + folderId + "'";
        var cmd = new SqlCommand(sql, conn);
        return cmd.ExecuteReader();
    }
}
HARDENED SECURE PATCH
using System;
using System.Data;
using System.Data.SqlClient;

public static class FolderLookup
{
    public static SqlDataReader Find(SqlConnection conn, string folderId)
    {
        if (!long.TryParse(folderId, out long id) || id <= 0)
            throw new ArgumentException("folderId must be a positive integer");

        // The value is sent as a typed parameter, separate from the query text.
        var cmd = new SqlCommand("SELECT id, name FROM folders WHERE id = @id", conn);
        cmd.Parameters.Add("@id", SqlDbType.BigInt).Value = id;
        return cmd.ExecuteReader();
    }
}

Engineering & System Hardening Checklist

Sources