This is a sharp knife left in a toy kitchen, dangerous if you actually put it on the counter
CVE-2025-0204 is a SQL injection flaw in code-projects Online Shoe Store 1.0, specifically in /details.php via the id parameter. The published proof of concept shows the app reading $_GET['id'] directly into SELECT * FROM product WHERE product_id = '$id', which makes database extraction straightforward if this sample PHP app is deployed and reachable.
The vendor's MEDIUM 6.3 baseline is technically plausible for a single exposed app, but it overstates fleet-level urgency for most enterprises. The big reality check is *reach*: this is a low-prevalence code-projects sample app, not Exchange, Ivanti, or a common edge platform; there is no KEV listing, no observed mass exploitation signal in the sources reviewed, and the scary sqlmap-to-shell outcome depends on extra database and filesystem privileges that many real deployments will not grant.
4 steps from start to impact.
Find a live instance
- The organization is actually running
code-projects Online Shoe Store 1.0 - The app is reachable over HTTP/HTTPS from the attacker position
- This product has very low enterprise prevalence
- There is no strong internet-wide fingerprint in the reviewed sources, so finding targets at scale is noisy
- Many enterprises will not run code-projects sample apps in production at all
/details.php?id=; scanner coverage is mostly custom rather than turnkey.Exploit details.php?id= with sqlmap
th4s1s shows sqlmap-style exploitation against /details.php by manipulating the id parameter. The CVE record says PR:L, but the code path and product behavior strongly suggest this page is part of normal product browsing and likely reachable without authentication.- The vulnerable
details.phpcode is present - The page accepts attacker-controlled
idinput - The database query is not parameterized
- If the site is tucked behind VPN, IP allowlists, or private ingress, the bug becomes post-initial-access
- A WAF with competent SQLi signatures may block commodity payloads
- Some deployments may have modified the sample code
/details.php?id=1' are effective. Generic SAST/secret scanning will not help unless you scan the PHP source.Read or modify the app database
- The DB account used by the app has useful read/write access
- The underlying DB error handling does not fully suppress exploitation
- Least-privilege DB users can sharply limit impact
- Data value may be low if the app is only a demo or coursework deployment
- If the database is separate and tightly permissioned, blast radius stays local to this app
sqlmap-style request bursts only indirectly through web logs.Attempt OS-level pivot
sqlmap file-write or command-execution features. That is *not* the default outcome of SQLi; it usually requires database features like FILE, stacked queries, writable web roots, or risky DB-to-OS integrations.- Database user has elevated privileges beyond normal app reads/writes
- The DBMS and web stack allow file write or command execution paths
- The attacker can place or execute a web shell or equivalent payload
- Many real deployments will not grant DB
FILEprivilege or equivalent - Separate DB hosts and non-writable web roots break the easy
sqlmap --os-shellstory - Modern EDR or file integrity monitoring can catch shell drops
LOW verdict there is no formal deadline, so handle as backlog hygiene unless the app is actually exposed.sqlmap-style request patterns on /details.php and especially the id parameter. This is a stopgap, not a fix, but it meaningfully raises attacker cost where no vendor patch exists; for LOW, treat this as opportunistic hardening rather than SLA work.LOW, but worth doing if the app must stay up.EDR alonedoes not prevent SQL injection; it may help only if the attacker reaches shell or drops files.Patching PHP/MySQLdoes not fix application-layer string concatenation indetails.php.MFAis irrelevant if the vulnerable page is publicly reachable and does not require login.
The supporting signals.
| In-the-wild status | No authoritative in-the-wild exploitation evidence found in the reviewed sources. CISA KEV: not listed as of the latest catalog page reviewed (KEV catalog). |
|---|---|
| Public PoC availability | Yes. A public gist by th4s1s shows the vulnerable code path and sqlmap exploitation workflow (gist). |
| EPSS | User-supplied EPSS is 0.00056 (~0.056%), which is very low and consistent with a niche target population. FIRST documents EPSS as a daily probability model via its public API (FIRST EPSS, API). |
| KEV / CISA status | The CVE is present in CISA's weekly vulnerability bulletin for the week of December 30, 2024, but not in the Known Exploited Vulnerabilities catalog (weekly bulletin, KEV catalog). |
| CVSS vector reality check | Published vector is CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L (6.3 / MEDIUM). The PR:L field looks questionable because the vulnerable path is /details.php?id= and the PoC targets normal product-detail browsing rather than an admin-only function (CVE JSON, PoC gist). |
| Affected versions | Officially affected version range is just Online Shoe Store 1.0 in the CVE record (CVE JSON, NVD). |
| Fixed version | No vendor-fixed version is identified in the CVE record or product page review. Treat this as no official patch published from the sources reviewed (OpenCVE, product page). |
| Scanning / exposure data | No trustworthy internet-scale exposure count was found during review. Analyst assessment: this is likely a small, long-tail exposure set made up of demo, lab, or neglected PHP sites rather than widely managed enterprise fleet software. |
| Disclosure and reporter | Published on 2025-01-04; timeline notes advisory disclosure on 2025-01-03. Reporter credit in the CVE JSON is lio346 (VulDB User) (CVE JSON). |
| Research context | The same CISA weekly bulletin lists several *other* code-projects Online Shoe Store CVEs from the same disclosure wave, which suggests a broadly insecure codebase rather than a one-off defect (CISA bulletin). |
Sources.
Why this verdict
- Start from 6.3 MEDIUM: an exposed SQLi on a public product page is a legitimate application compromise path.
- Reachability cuts hard: attacker position is
unauthenticated remote, but that only matters if you actually run this obscure sample app; most enterprises do not, so exposed population is tiny compared with true patch-priority software. - The
PR:Lmetric likely understates exploitability on a vulnerable host: the PoC and code path suggest normal browse access to/details.php?id=, so I am *not* dropping this to IGNORE. - Blast radius is usually app-local: the reliable outcome is database compromise for this one application; turning SQLi into OS shell requires extra DB and filesystem privileges that many deployments will not have.
- Threat intel is quiet: no KEV listing, no authoritative active-exploitation evidence found, and the EPSS supplied is extremely low.
Why not higher?
Because this is not a common enterprise platform, the reachable victim population is the whole story. A pre-auth SQLi on a forgotten demo site can absolutely burn that site, but it does not create the kind of broad, repeatable fleet risk that justifies MEDIUM-to-HIGH prioritization across a large enterprise program.
Why not lower?
Because if you *do* have this app exposed, exploitation is not hypothetical. There is public PoC material, the vulnerable code pattern is straightforward, and a single internet-facing instance is enough for direct database compromise.
Crowdsourced verification payload.
Run this on the target web host or against a checked-out application directory, not from an auditor workstation. Invoke it as python verify_cve_2025_0204.py /var/www/html/onl-shoe or python .\verify_cve_2025_0204.py C:\xampp\htdocs\onl-shoe; it needs only read access to the app files.
#!/usr/bin/env python3
"""
verify_cve_2025_0204.py
Static verifier for CVE-2025-0204 in code-projects Online Shoe Store 1.0.
Checks whether details.php appears to read $_GET['id'] and interpolate it
into the product query without prepared statements.
Exit codes:
0 = PATCHED
1 = VULNERABLE
2 = UNKNOWN
3 = USAGE ERROR
"""
import os
import re
import sys
from pathlib import Path
VULN_PATTERNS = [
re.compile(r"\$_GET\s*\[\s*['\"]id['\"]\s*\]", re.I),
re.compile(r"\$id\s*=\s*\$_GET\s*\[\s*['\"]id['\"]\s*\]", re.I),
re.compile(r"SELECT\s+\*\s+FROM\s+product\s+WHERE\s+product_id\s*=\s*['\"]\$id['\"]", re.I),
re.compile(r"query\s*\(\s*['\"].*product_id\s*=\s*['\"]\$id['\"]", re.I | re.S),
]
SAFE_HINTS = [
re.compile(r"prepare\s*\(", re.I),
re.compile(r"bind_param\s*\(", re.I),
re.compile(r"PDO\s*::\s*prepare", re.I),
re.compile(r"mysqli_prepare\s*\(", re.I),
re.compile(r"intval\s*\(\s*\$_GET\s*\[\s*['\"]id['\"]\s*\]\s*\)", re.I),
re.compile(r"filter_input\s*\(\s*INPUT_GET\s*,\s*['\"]id['\"]", re.I),
]
def load_file(base: Path):
candidates = [
base / "details.php",
base / "onl shoe" / "details.php",
base / "online shoe store" / "details.php",
]
for candidate in candidates:
if candidate.is_file():
try:
return candidate, candidate.read_text(encoding="utf-8", errors="ignore")
except Exception:
pass
found = list(base.rglob("details.php"))
for candidate in found:
if candidate.is_file():
try:
return candidate, candidate.read_text(encoding="utf-8", errors="ignore")
except Exception:
continue
return None, None
def main():
if len(sys.argv) != 2:
print("UNKNOWN")
print("Usage: python verify_cve_2025_0204.py <application_root>", file=sys.stderr)
sys.exit(3)
root = Path(sys.argv[1]).expanduser().resolve()
if not root.exists() or not root.is_dir():
print("UNKNOWN")
print(f"Path not found or not a directory: {root}", file=sys.stderr)
sys.exit(2)
target, content = load_file(root)
if not target or content is None:
print("UNKNOWN")
print("details.php not found under supplied root", file=sys.stderr)
sys.exit(2)
vuln_hits = sum(1 for p in VULN_PATTERNS if p.search(content))
safe_hits = sum(1 for p in SAFE_HINTS if p.search(content))
if vuln_hits >= 3 and safe_hits == 0:
print("VULNERABLE")
print(f"Matched vulnerable code pattern in: {target}")
sys.exit(1)
if safe_hits >= 1 and vuln_hits < 3:
print("PATCHED")
print(f"Found sanitization/prepared-statement indicators in: {target}")
sys.exit(0)
print("UNKNOWN")
print(f"Manual review needed for: {target}")
sys.exit(2)
if __name__ == "__main__":
main()