Submit #884541: SourceCodester Inventory System (using PHP and MySQL) 1.0 Improper Privilege Managementinfo

TitleSourceCodester Inventory System (using PHP and MySQL) 1.0 Improper Privilege Management
DescriptionVENDOR INFORMATION: Vendor homepage: https://www.sourcecodester.com/ Product page: https://www.sourcecodester.com/php/18722/php-inventory-system.html TITLE: SourceCodester Inventory System 1.0 — Unauthenticated Privilege Escalation via role Parameter in api/users_handler.php (Registration Endpoint) VULNERABILITY TYPE: Unauthenticated Privilege Escalation via Improper Privilege Management CWE(s): CWE-269 — Improper Privilege Management CWE-284 — Improper Access Control CWE-285 — Improper Authorization SEVERITY: According to the CVSS v3.1 scoring system, this vulnerability has a severity rating of 9.8 (Critical) CVSS Score: 9.8 Vector String: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H Severity Level: Critical AFFECTED ENDPOINT: /api/users_handler.php VULNERABLE PARAMETER: role (POST) AFFECTED COMPONENT: Staff Registration Handler (register action), reached via register.php DESCRIPTION: A vulnerability classified as critical was found in SourceCodester Inventory System 1.0. This affects the function register of the file api/users_handler.php of the component Staff Registration Page. The manipulation of the argument role with an arbitrary value such as admin leads to improper privilege management. The attack can be initiated remotely. No authentication is required for exploitation. The attack complexity is low, requiring only a single crafted HTTP request with no special conditions or tooling. The public-facing registration form at register.php and its accompanying client-side JavaScript only ever transmit role=staff, giving the appearance of a restricted, staff-only self-registration flow. This restriction exists exclusively in the client. The server applies no corresponding restriction, the value submitted for role is inserted into the users table exactly as received, with no validation, allow-listing, or authentication check of any kind on this endpoint. ROOT CAUSE: The register action in api/users_handler.php reads the role parameter directly from client-supplied POST data: case 'register': $username = $_POST['username'] ?? ''; $password = $_POST['password'] ?? ''; $full_name = $_POST['full_name'] ?? ''; $role = $_POST['role'] ?? 'staff'; if (empty($username) || empty($password) || empty($full_name)) { echo json_encode(['success' => false, 'message' => 'Please fill in all fields.']); exit; } $stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE username = ?"); $stmt->execute([$username]); if ($stmt->fetchColumn() > 0) { echo json_encode(['success' => false, 'message' => 'Username already exists.']); exit; } $hashed_password = password_hash($password, PASSWORD_DEFAULT); $stmt = $pdo->prepare("INSERT INTO users (username, password, full_name, role) VALUES (?, ?, ?, ?)"); if ($stmt->execute([$username, $hashed_password, $full_name, $role])) { echo json_encode(['success' => true, 'message' => 'User registered successfully!']); } else { echo json_encode(['success' => false, 'message' => 'Registration failed.']); } The code validates that required fields are non-empty and that the username is not already taken, but performs no equivalent validation on the role value. Any value supplied for role is passed unmodified into the INSERT statement. STEPS TO REPRODUCE: Step 1: On a clean, unmodified installation, navigate to the public registration page at register.php. Note that the form only exposes Full Name, Username, and Password fields; no role selector exists anywhere in the UI. Step 2: Submit the form normally with any test values. Inspect the outgoing HTTP request via browser developer tools (Network tab) Step 3: In the Network tab, find the request to api/users_handler.php (or users_handler.php) — it'll show as a POST with status 200. This confirms the client transmits role=staff as a hardcoded value alongside the submitted fields (Request payload). Step 4: Right-click on users_handler.php request → Copy Value → Copy as cURL (Windows) Step 5: Replay the same request directly to the server, with no browser session and no prior authentication, changing only the role value from staff to admin and supplying a new, unused username: curl "http://localhost/Product_Inventory/api/users_handler.php" \ --data-raw "full_name=Attacker+Admin&username=attacker_admin3&password=Test1234!&action=register&role=admin" NOTE: Excluding browser headers. Otherwise, the cURL command will be very long because it includes browser-specific headers such as User-Agent, Accept-Language, Sec-Fetch-Dest, and Priority when you use Copy as cURL. Step 6: The server responds: {"success":true,"message":"User registered successfully!"} Step 7: Authenticate using the newly created credentials. The account is granted immediate, full administrator access. PROOF OF CONCEPT: The escalation was verified end-to-end on a clean installation: 1) A legitimate registration request was captured, confirming the client hardcodes role=staff. 2) A modified request with role=admin was sent directly to the server with no authentication and returned a success response. 3) Login with the new credentials confirmed full administrative access via the application's own User Management panel, where the new account is listed with Role: Admin, identical in privilege to a legitimately created administrator. 4) From this account, full create/read/update/delete access was confirmed over all other existing user accounts, including the ability to edit credentials of, or delete, the original legitimate administrator account. 5) Direct inspection of the underlying users database table confirmed the escalated privilege is persisted as role=admin, and is not a session-layer artifact. 6) Full technical write-up, including annotated screenshots of every step above, is published at the Advisory link provided with this submission. IMPACT: This vulnerability allows an unauthenticated remote attacker to create a fully privileged administrator account in a single HTTP request, with no prior access, no approval workflow, and no user interaction from any legitimate user. Once created, the attacker account has complete administrative control over the application, including the ability to view, modify, and delete all customer, vendor, product, sales, and purchase records, and to view, edit, delete, or reassign the role of any existing user including taking over or deleting the legitimate administrator account, leading to full compromise of the application and all data it manages. SUGGESTED REMEDIATION: 1) Server-Side Enforcement Never trust client-supplied values for privilege-sensitive fields. The public registration endpoint should hardcode role = 'staff' server-side regardless of what the client sends: $role = 'staff'; // always force staff on public self-registration 2) Separate Privileged Workflow Any elevation to admin should only be possible through a distinct, authenticated endpoint that itself requires an existing admin session — never as a value accepted, even implicitly, from an unauthenticated request. DISCOVERED BY: Ali Raza (Independent Security Researcher)
Source⚠️ https://medium.com/@developerali15/sourcecodester-inventory-system-1-0-c986f47aeccd
User
 aliraza15 (UID 60676)
Submission07/09/2026 21:46 (2 months ago)
Moderation08/23/2026 18:11 (1 month later)
StatusDuplicate
VulDB entry369139 [SourceCodester Inventory System 1.0 Account Creation users_handler.php ROLE improper authorization]
Points0

Do you need the next level of professionalism?

Upgrade your account now!