A free school-project PHP app dropped on GitHub with a textbook SQL injection, dressed up with a 7.3
SourceCodester's *Class and Exam Timetabling System* v1.0 is a free PHP/MySQL learning project distributed on sourcecodester.com — the kind of codebase students download to learn CRUD. CVE-2026-13485 is an unauthenticated SQL injection in /preview.php via the course_year_section parameter, which is concatenated directly into a query with no sanitization. The bug class is canonical CWE-89 (the CWE-74 label is the parent injection bucket). The same researcher set has filed near-identical SQLi CVEs against preview6.php, preview7.php, archive.php, and others in the same codebase (CVE-2026-13486, -13488, etc.) — the entire project is riddled with the same anti-pattern.
Vendor severity of 7.3 (HIGH) is a mechanical CVSS output, not a reflection of risk. The vector AV:N/AC:L/PR:N/UI:N is technically accurate — anyone who can reach the page can dump the DB — but the population that actually runs this software in production is a rounding error. There is no enterprise install base, no vendor patch pipeline, and no support contract. Treating this like an Oracle or SAP SQLi distorts the patch queue. The honest call is LOW: real, exploitable, public PoC, but blast radius bounded to a handful of student / hobbyist deployments.
4 steps from start to impact.
Locate an exposed instance
inurl:preview.php course_year_section) or Shodan/FOFA HTML title match. The application has no telemetry; instances are usually on shared LAMP hosting or a lab VM.- Instance is reachable from attacker's network position
- Default PHP routing exposes /preview.php
- Effectively zero enterprise deployment footprint
- Most installs are on localhost / classroom LAN, not internet-exposed
Inject into course_year_section
GET /preview.php?course_year_section=1' UNION SELECT ...-- - using sqlmap (sqlmap -u 'http://target/preview.php?course_year_section=1' --dbs). The parameter is concatenated unsafely into a MySQL query, so UNION-based and boolean-based payloads both work. PoC is public on VulDB and GitHub issue trackers.- No WAF in front of the app
- MySQL backend with default error verbosity
- Any generic WAF (ModSecurity CRS, Cloudflare free tier) catches the canonical sqlmap signatures
- Hosting providers commonly block UNION SELECT patterns at the edge
Exfiltrate schema and data
information_schema.tables, dumps user/account tables, and harvests credentials. If MySQL is running as a privileged account with FILE privilege and secure_file_priv is unset, sqlmap's --os-shell can write a PHP webshell into the docroot.- DB user has SELECT on target tables (default in this app)
- FILE privilege + writable webroot for shell upload
- Modern MySQL defaults restrict
secure_file_priv, blocking file write - Shared hosting separates DB user from webroot ownership
Impact bounded to one tenant
- Successful DB compromise from step 3
- No corporate identity integration to abuse
- Database is single-app, single-host by design
The supporting signals.
| In-the-wild exploitation | None observed. No campaigns tracked. EPSS 0.00412 (≈0.4%) reflects negligible predicted exploitation. |
|---|---|
| Proof-of-concept | Public PoC on VulDB and researcher GitHub issues (lffaker/cybersec, yan-124/yan). Trivial sqlmap one-liner. |
| EPSS | 0.00412 — bottom of the distribution. Realistic given the audience. |
| KEV status | Not listed. Will not be — CISA does not catalog SourceCodester student-project bugs. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L → 7.3 HIGH. CVSS v4.0 reassessed to 6.9 MEDIUM, closer to reality. |
| CWE | CWE-89 (SQL Injection) — parent label CWE-74 used in NVD entry. Classic concatenation flaw. |
| Affected versions | SourceCodester Class and Exam Timetabling System v1.0 (only published version). Codebase has sibling CVEs: -13484, -13486, -13487, -13488. |
| Fixed version | None. SourceCodester does not maintain a patch pipeline for community-uploaded projects. |
| Exposure data | Shodan/Censys: no meaningful fingerprintable footprint. Effectively zero enterprise install base. |
| Disclosure | 2026-06-28 via VulDB. Researcher credit per VulDB entry; coordinated only in the loosest sense. |
noisgate verdict.
Downgraded from vendor HIGH (7.3) to LOW because the decisive factor is installed-base reality: SourceCodester is a free PHP learning-project hosting site with no enterprise deployment footprint, no vendor patch pipeline, and effectively zero exposure in Shodan/Censys data. The bug is real and trivially exploitable, but it does not belong in an enterprise patch queue alongside vendor-supported software.
Why this verdict
- Installed base near zero in enterprise: SourceCodester is a free code-sample repository, not an enterprise product. If you don't run it (and you almost certainly don't), this CVE is noise.
- Role multiplier — low-value role only: affected component is a standalone PHP class-scheduling demo. It is never deployed as IdP, hypervisor, CA, backup, EDR, or any high-value role. Blast radius caps at one app database on one host — there is no fleet, domain, or supply-chain pivot path. Floor stays at LOW.
- Friction — discovery is the bottleneck: there is no Shodan/Censys fingerprint for this product, no fleet scanning, and EPSS predicts <0.5% exploitation probability. Even motivated attackers ignore it because the loot tables are student schedules.
- No vendor patch pipeline: SourceCodester does not maintain CVE-keyed patches for community uploads, so the realistic remediation is *remove the app* or *replace with a maintained scheduler*, not 'apply update'.
Why not higher?
MEDIUM and above would imply this belongs in a normal patch queue with real ticket velocity. It does not — the product has no enterprise footprint, no patch supply chain, and the only realistic victim population (student-project deployers) is not the audience defending a 10,000-host fleet. Calling this HIGH inflates the queue and dilutes signal on bugs that matter.
Why not lower?
IGNORE would be wrong because the vulnerability IS trivially exploitable with public PoC, and a handful of organizations *might* genuinely run a SourceCodester app inside a lab or staging environment. If your asset inventory surfaces one, you need to act — just at a backlog-hygiene priority, not at incident pace.
What to do — in priority order.
- Inventory: confirm you do not run any SourceCodester PHP app — Grep your asset inventory and webserver vhost configs for
sourcecodester,preview.php,class-and-exam-timetabling. If zero hits, close the ticket. If hits exist, proceed. There is no mitigation SLA at LOW severity — handle within the 365-day remediation window. - If found, take the instance off the public internet immediately — Bind to localhost or put behind VPN / authenticated reverse proxy. Public exposure of this codebase is indefensible regardless of CVE — it has at least five sibling SQLi CVEs in the same project.
- Drop a WAF rule blocking SQLi payloads on /preview.php and siblings — ModSecurity CRS paranoia level 2+ or any commercial WAF will catch sqlmap signatures. Apply to
/preview*.php,/archive.phpat minimum. - Replace the application — The entire codebase is unmaintained and bug-saturated. Swap for a maintained scheduling tool (e.g., FET, Untis, or any institutional LMS module). This is the only durable fix.
- Waiting for a vendor patch — SourceCodester does not ship CVE-keyed patches for community uploads.
- Input validation in the calling page alone — sibling CVEs show the same anti-pattern across the whole codebase, so per-file fixes leave you exposed.
- Database least-privilege as a primary control — it reduces blast radius but the C:L/I:L/A:L impact still lands; do it, but don't call it remediation.
Crowdsourced verification payload.
Run on any auditor workstation with network reach to the suspected host. Requires curl only; no privileges needed beyond outbound HTTP. Invoke as ./check_cve_2026_13485.sh https://target.example.com — script returns VULNERABLE if the app fingerprints as the SourceCodester scheduler and the vulnerable parameter responds with SQL-error-shaped output.
#!/usr/bin/env bash
# noisgate verifier — CVE-2026-13485
# SourceCodester Class and Exam Timetabling System 1.0 — SQLi in /preview.php
# Usage: ./check_cve_2026_13485.sh <base_url>
set -u
BASE="${1:-}"
if [[ -z "$BASE" ]]; then
echo "Usage: $0 <base_url> e.g. https://timetable.lab.local" >&2
exit 2
fi
BASE="${BASE%/}"
UA="noisgate-verifier/1.0"
TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT
# Step 1: fingerprint the app at all
FP=$(curl -sk -A "$UA" --max-time 10 -o "$TMP" -w "%{http_code}" "$BASE/preview.php?course_year_section=1")
if [[ "$FP" == "000" || "$FP" == "404" ]]; then
echo "UNKNOWN — /preview.php not reachable (HTTP $FP). Asset may not run this product."
exit 0
fi
# Step 2: probe with a benign single-quote — a vulnerable build returns a MySQL error
PROBE=$(curl -sk -A "$UA" --max-time 10 "$BASE/preview.php?course_year_section=1%27")
if echo "$PROBE" | grep -qiE 'you have an error in your sql syntax|mysql_fetch|mysqli_|sqlstate|warning: mysql'; then
echo "VULNERABLE — /preview.php course_year_section emits SQL error on single quote."
exit 1
fi
# Step 3: boolean differential — same length response for 1=1 vs 1=2 means likely sanitized
TRUE_LEN=$(curl -sk -A "$UA" --max-time 10 "$BASE/preview.php?course_year_section=1%20AND%201%3D1" | wc -c)
FALSE_LEN=$(curl -sk -A "$UA" --max-time 10 "$BASE/preview.php?course_year_section=1%20AND%201%3D2" | wc -c)
DIFF=$(( TRUE_LEN > FALSE_LEN ? TRUE_LEN - FALSE_LEN : FALSE_LEN - TRUE_LEN ))
if (( DIFF > 50 )); then
echo "VULNERABLE — boolean-based differential detected (delta=$DIFF bytes)."
exit 1
fi
echo "PATCHED or NOT-AFFECTED — no SQL error, no boolean differential on /preview.php."
exit 0
If you remember one thing.
sourcecodester or class-and-exam-timetabling. If you have zero hits — which is the expected outcome for any enterprise — document the rationale and close the ticket. If you DO find an instance, take it off the public internet today, drop a WAF rule on /preview*.php and /archive.php, and queue replacement of the entire codebase since it has multiple sibling SQLi CVEs and no upstream patch pipeline. Per the noisgate mitigation SLA for LOW there is no formal mitigation deadline; per the noisgate remediation SLA treat this as backlog hygiene with a ≤365-day window (sooner if internet-exposed). Do not let the vendor 7.3 push this above genuinely-HIGH bugs in your queue.Sources
- VulDB — CVE-2026-13485
- Vulnerability-Lookup CIRCL — CVE-2026-13485
- GHSA-Q3VP-2MQC-929R (CIRCL mirror)
- Sibling CVE-2026-13488 (preview7.php SQLi)
- Researcher PoC — lffaker/cybersec issue #6 (preview.php)
- Researcher PoC — lffaker/cybersec issue #4 (archive.php)
- Researcher PoC — yan-124/yan issue #4 (preview7.php)
- SourceCodester project home
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.