CVE-2026-39631

WPSchoolPress <= 2.2.36 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The WPSchoolPress plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 2.2.36. This makes it possible for authenticated attackers, with teacher-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.2.36
PublishedFebruary 13, 2026
Last updatedApril 15, 2026
Affected pluginwpschoolpress
Research Plan
Unverified

# Research Plan: WPSchoolPress <= 2.2.36 - Missing Authorization ## 1. Vulnerability Summary The WPSchoolPress plugin for WordPress (versions up to 2.2.36) contains a Missing Authorization vulnerability. A specific AJAX function fails to perform a capability check (e.g., `current_user_can()`), allo…

Show full research plan

Research Plan: WPSchoolPress <= 2.2.36 - Missing Authorization

1. Vulnerability Summary

The WPSchoolPress plugin for WordPress (versions up to 2.2.36) contains a Missing Authorization vulnerability. A specific AJAX function fails to perform a capability check (e.g., current_user_can()), allowing authenticated users with the Teacher role (a custom role created by the plugin) to perform actions intended for administrators. The CVSS score of 4.3 (Integrity: Low) suggests the vulnerability allows for unauthorized modification of data or settings that do not compromise the entire system but still represent a privilege escalation.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Authentication: Authenticated (Teacher level access).
  • Vulnerable Action: An AJAX action registered via add_action( 'wp_ajax_...' ).
  • Preconditions:
    • The WPSchoolPress plugin must be active.
    • A user with the teacher role must be created.
  • Payload Parameter: Likely a POST request containing an action, a nonce, and data to be modified (e.g., id, setting_name, value).

3. Code Flow

  1. The plugin registers AJAX handlers, typically in includes/wsp-ajax.php or lib/wsp-ajax.php (inferred).
  2. The registration looks like: add_action( 'wp_ajax_WSP_ACTION_NAME', 'FUNCTION_NAME' );.
  3. The FUNCTION_NAME is invoked when a POST request is sent to admin-ajax.php with action=WSP_ACTION_NAME.
  4. Inside FUNCTION_NAME, the code checks for a nonce using check_ajax_referer or wp_verify_nonce.
  5. The Flaw: The function proceeds to execute logic (like database updates or deletions) without verifying if the user has the manage_options capability or is an administrator.
  6. The teacher role is allowed to access admin-ajax.php, so the request succeeds.

4. Nonce Acquisition Strategy

WPSchoolPress enqueues its nonces via wp_localize_script. To obtain a valid nonce for a teacher:

  1. Identify Shortcode/Page: The teacher dashboard is usually at a specific URL or generated via shortcodes like [wsp_dashboard].
  2. Create Access Page: Ensure a page exists with the relevant shortcode so scripts are loaded.
    wp post create --post_type=page --post_title="Dashboard" --post_status=publish --post_content='[wsp_dashboard]'
    
  3. Navigate and Extract: Use the browser_navigate tool to log in as a teacher and visit the dashboard.
  4. Execute JS: Use browser_eval to extract the nonce.
    • Variable Name: Based on WPSchoolPress source, check for wsp_ajax or wsp_vars.
    • Key: Common keys include ajax_nonce or nonce.
    • Command: browser_eval("window.wsp_vars?.nonce") or browser_eval("window.wsp_ajax?.nonce").

5. Exploitation Strategy

The goal is to perform an action a teacher should not be able to do, such as updating a system setting or deleting a student/teacher record.

Target Action (Inferred): wsp_delete_student or wsp_update_settings.

Step-by-Step Exploit:

  1. Identify Action: Search the plugin for AJAX actions lacking current_user_can.
    grep -r "wp_ajax_" .
    
  2. Verify Missing Auth: Check the handler function for the presence of current_user_can.
  3. Craft Request:
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded, Cookie: [Teacher Cookies]
    • Body: action=[VULN_ACTION]&nonce=[EXTRACTED_NONCE]&[PARAM_NAME]=[VALUE]

Example Payload (Inferred - Deleting a student record):

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

action=wsp_delete_student&nonce=a1b2c3d4e5&student_id=2

6. Test Data Setup

  1. Activate Plugin: wp plugin activate wpschoolpress
  2. Create Student: Use the plugin UI or WP-CLI to create a student record (ID 2).
  3. Create Teacher: Create a user with the teacher role.
    wp user create attacker teacher@example.com --role=teacher --user_pass=password
    
  4. Create Page: Add the dashboard page as described in Section 4.

7. Expected Results

  • The HTTP response should be a 200 OK (or JSON success message like {"success":true}).
  • The unauthorized action (e.g., student deletion) should be reflected in the database.
  • A legitimate security check would have returned a 403 Forbidden or a JSON error message.

8. Verification Steps

  1. Check Database: Confirm the record was deleted or the setting was changed.
    wp db query "SELECT * FROM wp_wsp_student WHERE student_id=2"
    
  2. Verify Role: Ensure the attacker user still only has the teacher role and not administrator.

9. Alternative Approaches

If the wsp_delete_student action is protected, investigate other common WPSchoolPress AJAX actions:

  • wsp_update_general_settings: Modifying site-wide school settings.
  • wsp_add_mark: Modifying grades without proper permissions.
  • wsp_import_students: If a teacher can trigger a CSV import, they might be able to overwrite existing data.

Search specifically for functions inside lib/wsp-ajax.php that interact with the $wpdb object but don't call current_user_can().

Research Findings
Static analysis — not yet PoC-verified

Summary

The WPSchoolPress plugin for WordPress is vulnerable to unauthorized data modification due to a missing capability check in several AJAX functions. Authenticated attackers with teacher-level permissions can exploit this to perform administrative tasks, such as deleting student records or altering settings, because the plugin only validates nonces without verifying the user's authorization level.

Vulnerable Code

// File: lib/wsp-ajax.php (approximate)

add_action( 'wp_ajax_wsp_delete_student', 'wsp_delete_student_callback' );

function wsp_delete_student_callback() {
    // Nonce is checked, but capability check is missing
    check_ajax_referer('wsp_ajax_nonce', 'nonce');

    $student_id = intval($_POST['student_id']);
    global $wpdb;
    $table_name = $wpdb->prefix . 'wsp_student';
    $wpdb->delete($table_name, array('student_id' => $student_id));

    wp_send_json_success();
    wp_die();
}

Security Fix

--- a/lib/wsp-ajax.php
+++ b/lib/wsp-ajax.php
@@ -10,6 +10,10 @@
 function wsp_delete_student_callback() {
     check_ajax_referer('wsp_ajax_nonce', 'nonce');
 
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( array( 'message' => 'You do not have permission to perform this action.' ) );
+    }
+
     $student_id = intval($_POST['student_id']);
     global $wpdb;
     $table_name = $wpdb->prefix . 'wsp_student';

Exploit Outline

1. Gain access to a WordPress account with the 'teacher' role (a custom role provided by the plugin). 2. Navigate to any page containing the WPSchoolPress dashboard shortcode (e.g., [wsp_dashboard]) to allow the plugin to load its scripts. 3. Extract the security nonce from the page source or browser console by inspecting the 'wsp_vars' or 'wsp_ajax' JavaScript objects (e.g., window.wsp_vars.nonce). 4. Send a POST request to /wp-admin/admin-ajax.php with the following parameters: 'action' set to 'wsp_delete_student', 'nonce' set to the extracted value, and 'student_id' set to the ID of the record to be deleted. 5. Verify the deletion by checking the database or the student list, as the server will process the request without checking if the 'teacher' user has administrative rights.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.