SQL Injection Vulnerability Patched in Paid Membership Subscriptions Plugin

Published 28 August 2025
Table of Contents

This blog post is about an unauthenticated SQL injection vulnerability in the Paid Membership Subscriptions plugin. If you're a Paid Membership Subscriptions plugin user, please update the plugin to version 2.15.2.

The vulnerabilities mentioned here were discovered and reported by Patchstack Alliance community member ChuongVN.

✌️ Our users are protected from this vulnerability. Are yours?

Web developers

Mitigate vulnerabilities in real-time without changing code.

See pricing
Plugin developers

Identify vulnerabilities in your plugins and get recommendations for fixes.

Request audit
Hosting companies

Protect your users, improve server health and earn additional revenue.

Patchstack for hosts

About Paid Membership Subscriptions plugin

The plugin Paid Membership Subscriptions, which has over 10,000 active installations, allows site owners to optimize the site with membership and recurring subscriptions in just a few clicks. It allows integration of various payment methods and offers a smooth subscription styled payments.

Unauthenticated SQL Injection

In versions 2.15.1 and below, the plugin is vulnerable to an SQL injection, which allows any unauthenticated attacker to inject SQL queries into the database. The vulnerability has been patched in version 2.15.2 and is tracked with CVE-2025-49870.

The root cause of the issue lies in the process_webhooks function:

public function process_webhooks() {

    if( !isset( $_GET['pay_gate_listener'] ) || $_GET['pay_gate_listener'] !== 'paypal_ipn' )
        return;

    // Init IPN Verifier
    $ipn_verifier = new PMS_IPN_Verifier();

    if( pms_is_payment_test_mode() )
        $ipn_verifier->is_sandbox = true;


    $verified = false;

    // Process the IPN
    try {
        if( $ipn_verifier->checkRequestPost() )
            $verified = $ipn_verifier->validate();
    } catch ( Exception $e ) {

    }


    if( $verified ) {

        $post_data = $_POST;

        // Get payment id from custom variable sent by IPN
        $payment_id = isset( $post_data['custom'] ) ? $post_data['custom'] : 0;

        // Get the payment
        $payment = pms_get_payment( $payment_id );

        // Get user id from the payment
        $user_id = $payment->user_id;

        // TRIMMED
    }
}

The function takes the user-input as $post_data, extracts the $payment_id value from it, and calls the pms_get_payment function.

function pms_get_payment( $payment_id = 0 ) {

    return new PMS_Payment( $payment_id );

}

The function just calls a new instance of the PMS_Payment class. The constructor of the respective class gets called.

public function __construct( $id = 0 ) {

    // Return if no id provided
    if( $id == 0 ) {
        $this->id = 0;
        return;
    }

    // Get payment data from the db
    $data = $this->get_data( $id );

    // Return if data is not in the db
    if( is_null($data) ) {
        $this->id = 0;
        return;
    }

    // Populate the data
    $this->set_instance( $data );

}

The constructor of the class calls get_data which is vulnerable to SQL injection due to improper concatenation of user input.

public function get_data( $id ) {

    global $wpdb;

    $result = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}pms_payments WHERE id = {$id}", ARRAY_A );

    return $result;

}

The patch

In version 2.15.2, the vendor patched the SQL injection by ensuring that $id is numeric while passing to the get_data function and used proper prepared statements.

Conclusion

For the SQL query process, always do a safe escape and format the user's input before performing a query. The best practice is always to use a prepared statement and also cast each of the used variables to its intended usage.

Want to learn more about finding and fixing vulnerabilities?

Explore our Academy to master the art of finding and patching vulnerabilities within the WordPress ecosystem. Dive deep into detailed guides on various vulnerability types, from discovery tactics for researchers to robust fixes for developers. Join us and contribute to our growing knowledge base.

Timeline

02 June, 2025We received the vulnerability report and notified the vendor.
11 June, 2025The vendor publishes the patched version to the WP repository.
03 July, 2025We published the vulnerability entry to the database.
28 August, 2025Security advisory article publicly released.

🤝 You can help us make the Internet a safer place

Plugin developer?

Streamline your disclosure process to fix vulnerabilities faster and comply with CRA.

Get started for free
Hosting company?

Protect your users too! Improve server health and earn added revenue with proactive security.

Patchstack for hosts
Security researcher?

Report vulnerabilities to our gamified bug bounty program to earn monthly cash rewards.

Learn more

The latest in Security Advisories

Looks like your browser is blocking our support chat widget. Turn off adblockers and reload the page.
crossmenu