The administrative summary page in itsourcecode Online Admission System 1.0 places the form field id inside a quoted SQL predicate and sends database errors directly to the response. The public report uses a GTID_SUBSET() parsing error to expose a constant marker assembled by an SQL expression.
CVE-2026-108928, reported by YuLi, is tracked as VDB-416751. GTID itself is not the vulnerable component: external input crosses the SQL data boundary, and error handling provides an observable channel. Remediation must address query construction, error propagation, and administrative write controls.
Record and verification scope
- Affected
- itsourcecode Online Admission System 1.0
- CWE
- CWE-89
- disclosed
| Item | Confirmed detail |
|---|---|
| Entry point | POST /admin/sum.php |
| Input location | Form-body field id |
| Branch condition | isset($_POST['update']) |
| Query target | Table summary, predicate on stud_id |
| Demonstrated technique | Error-based SQL injection |
| Fixed version | Not specified in the advisories checked |
VulDB's CNA record supplies CVSS 4.0 at 5.3, medium, and separately a CVSS 3.1 base score of 6.3. This article and the wall use the former and retain PR:L.
The product page links to Online-Admission-System-Project-In-PHP-MySQL-Source-Code.zip, SHA-256 62d1f2b31b71bccbdadafc481e666e19b10cc189265651d775ae93d5afa98e7b. The entry point is chmscIT2/admin/sum.php within that archive. The analysis combines static inspection of this snapshot with the public screenshot. The original environment was not redeployed, and its exact database version has not been established.
A form ID reaches a quoted UPDATE predicate
if (isset($_POST['update'])) {
$id = $_POST['id'];
// ... calculate scores and fetch the default school year ...
$query = "UPDATE `summary` SET `entrance_score`='$entrance',`general_ave`='$gen_av',`interview`='$interview',`total`='$total',sy_id = '".$rows4['sy_id']."' WHERE `stud_id`='$id'";
mysqli_query($db, $query) or die(mysqli_error($db));
}The excerpt omits score calculation but preserves the branch, input, UPDATE, and error handler. Line 118 reads $_POST['id'] without parameter binding. Line 140 inserts it into WHERE stud_id='$id'. An integer database column does not protect this position: SQL parsing happens before value comparison or conversion.
The browser stores the student ID in a hidden field. That does not prevent the client from changing it. The server needs business-type validation, object authorization, and separate transmission of statement structure and values.
The processing branch is also inside the loop that renders summary rows. An empty result set may skip it entirely; several rows may process the same POST repeatedly if execution continues. A JavaScript navigation instruction affects the browser, not the running PHP loop. Regression tests need empty and multi-row datasets rather than a single status-code check.
The error probe and constant marker
This request preserves the public report's 114-byte form body, removes unrelated browser headers, and substitutes the target host. Its expression calculates the constant 1 rather than querying business tables or extracting records.
POST /admin/sum.php HTTP/1.1
Host: target.example
Content-Length: 114
Content-Type: application/x-www-form-urlencoded
id=1%27%20AND%20GTID_SUBSET(CONCAT(0x7162787a71,(SELECT%20(ELT(2361=2361,1))),0x71786b7171),2361)--%20dUaI&update=Although update= is empty, the field exists and satisfies isset($_POST['update']). After URL decoding, the ID has the following structure; line breaks are for readability only.
1' AND GTID_SUBSET(
CONCAT(
0x7162787a71,
(SELECT (ELT(2361=2361,1))),
0x71786b7171
),
2361
)-- dUaIThe opening quote terminates the original ID literal. AND adds an expression, and the trailing comment handles the remaining SQL text. The comparison 2361=2361 is true, so ELT(...,1) selects the first supplied value. The hexadecimal constants decode to qbxzq and qxkqq, producing qbxzq1qxkqq.
That string is not a valid GTID set. In the reported database environment, parsing it in GTID_SUBSET() raises an error containing the marker. The application then exposes that error through die(mysqli_error($db)). SQL injection does not depend on a replication deployment, and this behavior is not evidence of a flaw in MySQL's GTID implementation.
Response evidence and reproduction prerequisites
The decisive text in the public screenshot is:
Malformed GTID set specification 'qbxzq1qxkqq'.The response is not simply the complete submitted string reflected into HTML. The request contains hexadecimal literals, a condition, and function calls; the response contains their assembled result. Direct SQL interpolation and database-error output in the source explain that observation.
The request includes only id and update, omitting the normal entrance, gen_av, and interview fields. Before reaching UPDATE, the source reads and calculates those values and queries default scoring criteria and the school year. PHP version, error handling, and initial database contents can cause earlier termination. This shortened request is not a universal environment test.
For an independent check, prepare valid default criteria, a school year, and summary rows. Capture a legitimate save request, hold its fields fixed, and change only id. Compare normal and marker-error responses in a restorable database, recording statements and row state. HTTP 200 does not imply the absence of an error; HTTP 302 does not prove server-side execution stopped.
The report contradicts itself on whether authentication is required. In the downloaded snapshot, admin/include/header.php:2-5 checks the session and sets a Location header without an immediate exit. That warrants a separate check of termination semantics, not a replacement for deployment-level session testing. This article retains the CNA's PR:L rather than presenting the static observation as a newly reproduced authentication finding.
Business impact and limits of the evidence
This handler updates admission summaries: entrance score, general average, interview result, total, and school year. External control of SQL structure can move record selection and error behavior beyond the intended operation. Database-account privileges constrain further reach.
The public PoC demonstrates evaluation of an SQL expression and disclosure of a constant through an error. It does not show extraction of student data or an actual score change. Although the vulnerable statement is UPDATE, the probe raises an evaluation error; row changes require before/after database evidence. The available material does not establish operating-system execution, a full database dump, or effects in other versions.
Detection should correlate unusual IDs sent to sum.php, GTID parsing errors, and database failures. Use request correlation IDs, routes, and outcome classes rather than logging sessions, student details, or entire forms. Function-name blacklists can help alerting, but they do not repair the query boundary.
Parameterization and error handling
The following is a modern PDO MySQL replacement pattern, not a vendor patch. Scores must already have been validated and calculated server-side, and the school-year ID obtained from a valid default record. A complete handler must implement those prerequisites.
<?php
// Illustrative replacement after authentication, authorization and CSRF.
$raw = $_POST['id'] ?? null;
$id = is_string($raw)
? filter_var($raw, FILTER_VALIDATE_INT, [
'options' => ['min_range' => 1, 'max_range' => 2147483647]
])
: false;
if ($id === false) {
http_response_code(400);
exit('Invalid id');
}
// Scores below are validated and calculated server-side.
// $schoolYearId comes from the validated default-year record.
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $pdo->prepare(
'UPDATE summary SET entrance_score = ?, general_ave = ?, '
. 'interview = ?, total = ?, sy_id = ? WHERE stud_id = ?'
);
$stmt->bindValue(1, $entrance, PDO::PARAM_STR);
$stmt->bindValue(2, $gen_av, PDO::PARAM_STR);
$stmt->bindValue(3, $interview, PDO::PARAM_STR);
$stmt->bindValue(4, $total, PDO::PARAM_STR);
$stmt->bindValue(5, $schoolYearId, PDO::PARAM_INT);
$stmt->bindValue(6, $id, PDO::PARAM_INT);
$stmt->execute();The example binds scores as strings, matching the snapshot's varchar(30) columns, and treats the identifiers as integers. Binding every value gives a clearer boundary than escaping only id while relying on assumptions about other fields.
Move POST handling ahead of rendering so that each request performs one business operation. Stop server-side execution on failed authorization, check permission for the specific student record, and validate CSRF. Revalidate read-only browser fields; recompute authoritative scores from server-side records where possible.
Replace die(mysqli_error()) with centralized exception handling. Keep diagnostics server-side and return a generic error with a correlation identifier. Hiding database errors only closes this observable channel; it does not fix the remaining interpolation. No fixed version is listed in the official material, so these are remediation recommendations rather than a verified upgrade instruction.
| Regression case | Acceptance criterion |
|---|---|
| Legitimate score save | One operation, affecting only the authorized target |
| ID with quotes, functions, or comments | Rejected before database execution |
| Missing fields, arrays, or out-of-range IDs | Consistent input errors |
| Missing session, object permission, or valid CSRF | No update occurs |
| Empty or multi-row summary | Processing is independent of the render loop |
| Database failure | No SQL, paths, or connection details in the response |
Disclosure timeline
| Date | Event | Qualification |
|---|---|---|
| 2026-09-24 | GitHub report #3 created | Platform creation timestamp |
| 2026-10-11 | CNA: Advisory disclosed | Matches the vulnerability wall |
| 2026-10-12 | CVE record published | The official datePublished field |
The VulDB submission is 997399 and the accepted entry is VDB-416751. GitHub creation, CNA disclosure, and CVE publication are separate events. The available material does not establish first vendor receipt, acknowledgment, or remediation dates.