[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$faGBCzpgGcWY8Q5586YGRGo2x6SYuSKrEet0fyvRfvrk":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":28,"research_verified":29,"research_rounds_completed":30,"research_plan":31,"research_summary":32,"research_vulnerable_code":33,"research_fix_diff":34,"research_exploit_outline":35,"research_model_used":36,"research_started_at":37,"research_completed_at":38,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":29,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":29,"source_links":39},"CVE-2026-49070","knit-pay-cashfree-instamojo-razorpay-paypal-and-more-missing-authorization","Knit Pay – Cashfree, Instamojo, Razorpay, PayPal and more \u003C= 9.4.0.0 - Missing Authorization","The Knit Pay – Cashfree, Instamojo, Razorpay, PayPal and more plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 9.4.0.0. This makes it possible for unauthenticated attackers to perform an unauthorized action.","knit-pay",null,"\u003C=9.4.0.0","9.4.0.1","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-06-08 00:00:00","2026-06-15 19:23:55",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F770fd2b9-b226-4b6a-84f6-674061ddad93?source=api-prod",8,[22,23,24,25,26,27],"composer.lock","gateways\u002FPayPal\u002FGateway.php","knit-pay.php","readme.txt","secondary-packages\u002Fvendor\u002Fcomposer\u002Finstalled.php","vendor\u002Fcomposer\u002Finstalled.php","researched",false,3,"# CVE-2026-49070 Knit Pay – Missing Authorization Research Plan\n\n## Vulnerability Summary\nThe **Knit Pay** plugin (\u003C= 9.4.0.0) is vulnerable to **Missing Authorization** in its PayPal gateway AJAX handlers. Specifically, the functions `ajax_create_order` and `ajax_capture_order` in `gateways\u002FPayPal\u002FGateway.php` perform security checks using WordPress nonces (`check_ajax_referer`) but fail to verify if the current user has the authority to modify the specific payment object identified by the `payment_id` parameter.\n\nBecause these handlers are likely registered as `wp_ajax_nopriv_` actions (to support guest checkout), an unauthenticated attacker can obtain a valid nonce from any public payment page and then use it to manipulate the state or metadata (e.g., `transaction_id`) of any other `pronamic_payment` post in the system.\n\n## Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Actions**: \n    - `knitpay_paypal_create_order` (inferred from `ajax_create_order`)\n    - `knitpay_paypal_capture_order` (inferred from `ajax_capture_order`)\n- **Parameters**:\n    - `action`: The AJAX action name.\n    - `nonce`: A valid nonce for the `knitpay_paypal_checkout` action.\n    - `payment_id`: The integer ID of a `pronamic_payment` post.\n    - `order_id`: (For capture) A PayPal order ID string.\n- **Preconditions**:\n    - The PayPal gateway must be active.\n    - An attacker needs to find a valid `payment_id` (enumerable via post IDs).\n    - A valid nonce must be extracted from a page where the PayPal gateway is initialized.\n\n## Code Flow\n1. **Entry Point**: A POST request is sent to `admin-ajax.php` with the action `knitpay_paypal_capture_order`.\n2. **Nonce Check**: `Gateway::ajax_capture_order()` calls `check_ajax_referer( 'knitpay_paypal_checkout', 'nonce', false )`. This checks if the nonce is valid for the current user (or guest session).\n3. **Object Retrieval**: The code calls `$payment = \\get_pronamic_payment( $payment_id )`.\n4. **Vulnerable Sink**: Without checking if the current user \"owns\" the payment session or has administrative privileges, the code proceeds to:\n    - `$payment->set_transaction_id( $order_id )`\n    - `$gateway->update_status( $payment )`\n    - **`$payment->save()`**\n5. **Impact**: The `transaction_id` meta field of the payment post is updated in the database. If the provided `order_id` corresponds to a completed PayPal transaction (even from a different context), `update_status` may incorrectly mark the target payment as **Success**.\n\n## Nonce Acquisition Strategy\nThe nonce `knitpay_paypal_checkout` is generated for guest users (UID 0) and is valid for all guest sessions within the 12-24 hour window.\n1. **Identify Script Localization**: The plugin likely uses `wp_localize_script` to pass the nonce to the frontend PayPal Smart Buttons.\n2. **Trigger Script Loading**: The scripts are enqueued when a payment page for a PayPal-configured gateway is loaded.\n3. **Extraction**:\n    - Create a payment link using the Knit Pay admin interface (or CLI).\n    - Navigate to the payment link URL using `browser_navigate`.\n    - Use `browser_eval` to extract the nonce:\n      ```javascript\n      \u002F\u002F Example target (based on common patterns in this plugin)\n      window.knitpay_paypal_checkout_data?.nonce \n      \u002F\u002F OR look for localized data in the page source\n      ```\n4. **Action Matching**: In `gateways\u002FPayPal\u002FGateway.php`, both handlers use the same action string: `'knitpay_paypal_checkout'`.\n\n## Exploitation Strategy\n1. **Setup**:\n    - Enable the PayPal gateway in Knit Pay.\n    - Create a \"target\" payment (e.g., via a WooCommerce checkout or a Payment Link). Note its `payment_id`.\n2. **Acquire Nonce**: \n    - Create a second \"dummy\" payment.\n    - Visit the dummy payment page.\n    - Extract the `nonce` for `knitpay_paypal_checkout`.\n3. **Execute Attack**:\n    - Send a POST request to `admin-ajax.php`.\n    - `action=knitpay_paypal_capture_order`\n    - `nonce=[EXTRACTED_NONCE]`\n    - `payment_id=[TARGET_PAYMENT_ID]`\n    - `order_id=MALICIOUS_TRANSACTION_123`\n4. **Verify**: Check the metadata of the target payment post to confirm the `transaction_id` was changed.\n\n## Test Data Setup\n- **Gateway**: Create a `pronamic_gateway` post with `post_title=\"PayPal\"` and configure it for the PayPal integration.\n- **Payment**: Create a `pronamic_payment` post representing a pending transaction.\n- **Page**: Create a WordPress page containing a payment button or link that uses the PayPal gateway to ensure the nonce is generated and localized.\n\n## Expected Results\n- The AJAX request returns a `200 OK` (or `500` if the PayPal API call fails, but only *after* the `transaction_id` is set).\n- The target `pronamic_payment` post will have its `_pronamic_payment_transaction_id` (or similar meta key) updated to `MALICIOUS_TRANSACTION_123`.\n- An unauthenticated user successfully modifies data belonging to a transaction they did not initiate.\n\n## Verification Steps\nUse WP-CLI to inspect the payment meta before and after the attack:\n```bash\n# Get current transaction ID\nwp post meta get [PAYMENT_ID] _pronamic_payment_transaction_id\n\n# After exploit, check again\nwp post meta get [PAYMENT_ID] _pronamic_payment_transaction_id\n```\n\n## Alternative Approaches\nIf `ajax_capture_order` fails due to strict PayPal API validation, target **`ajax_create_order`**:\n- Call `knitpay_paypal_create_order` with a target `payment_id`.\n- This triggers `$gateway->api->create_order(...)` and `$payment->save()`.\n- Successful execution will overwrite the `paypal_order_id` meta and `transaction_id` of the target payment, which can be verified via WP-CLI.","The Knit Pay plugin for WordPress is vulnerable to missing authorization in its PayPal gateway AJAX handlers. Unauthenticated attackers can manipulate the transaction ID and status of arbitrary payment records by leveraging a publicly accessible nonce to call sensitive functions without ownership verification.","\u002F\u002F gateways\u002FPayPal\u002FGateway.php:93\npublic static function ajax_create_order() {\n\tif ( ! check_ajax_referer( 'knitpay_paypal_checkout', 'nonce', false ) ) {\n\t\twp_send_json_error( [ 'message' => 'Security check failed. Please refresh the page and try again.' ], 403 );\n\t}\n\n\t$payment_id = isset( $_POST['payment_id'] ) ? absint( $_POST['payment_id'] ) : 0;\n\t\u002F\u002F ...\n\t$payment = \\get_pronamic_payment( $payment_id );\n\t\u002F\u002F ...\n\t\t\tif ( isset( $payment_order->id ) ) {\n\t\t\t\t$payment->set_meta( 'paypal_order_id', $payment_order->id );\n\t\t\t\t$payment->set_transaction_id( $payment_order->id );\n\t\t\t\t$payment->save();\n\n\n---\n\n\u002F\u002F gateways\u002FPayPal\u002FGateway.php:153\npublic static function ajax_capture_order() {\n\tif ( ! check_ajax_referer( 'knitpay_paypal_checkout', 'nonce', false ) ) {\n\t\twp_send_json_error( [ 'message' => 'Security check failed. Please refresh the page and try again.' ], 403 );\n\t}\n\n\t$order_id   = isset( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : '';\n\t$payment_id = isset( $_POST['payment_id'] ) ? absint( $_POST['payment_id'] ) : 0;\n\tif ( empty( $order_id ) || empty( $payment_id ) ) {\n\t\twp_send_json_error( [ 'message' => 'Invalid request parameters.' ], 400 );\n\t}\n\n\t$payment = \\get_pronamic_payment( $payment_id );\n\tif ( null === $payment ) {\n\t\twp_send_json_error( [ 'message' => 'Payment not found.' ], 404 );\n\t}\n\n\ttry {\n\t\t$integration = new Integration();\n\t\t$gateway     = $integration->get_gateway( $payment->get_config_id() );\n\n\t\t$payment->set_transaction_id( $order_id );\n\t\t$gateway->update_status( $payment );\n\t\t$payment->save();","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fknit-pay\u002F9.4.0.0\u002Fcomposer.lock \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fknit-pay\u002F9.4.0.1\u002Fcomposer.lock\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fknit-pay\u002F9.4.0.0\u002Fcomposer.lock\t2026-05-31 20:24:08.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fknit-pay\u002F9.4.0.1\u002Fcomposer.lock\t2026-06-04 08:44:12.000000000 +0000\n@@ -3719,16 +3719,16 @@\n         },\n         {\n             \"name\": \"composer\u002Fcomposer\",\n-            \"version\": \"2.10.0\",\n+            \"version\": \"2.10.1\",\n             \"source\": {\n                 \"type\": \"git\",\n                 \"url\": \"https:\u002F\u002Fgithub.com\u002Fcomposer\u002Fcomposer.git\",\n-                \"reference\": \"c13824d95608b15913a7c0def0a3dea4474b71fc\"\n+                \"reference\": \"4120703b9bda8795075047b40361d7ec4d2abe49\"\n             },\n             \"dist\": {\n                 \"type\": \"zip\",\n-                \"url\": \"https:\u002F\u002Fapi.github.com\u002Frepos\u002Fcomposer\u002Fcomposer\u002Fzipball\u002Fc13824d95608b15913a7c0def0a3dea4474b71fc\",\n-                \"reference\": \"c13824d95608b15913a7c0def0a3dea4474b71fc\",\n+                \"url\": \"https:\u002F\u002Fapi.github.com\u002Frepos\u002Fcomposer\u002Fcomposer\u002Fzipball\u002F4120703b9bda8795075047b40361d7ec4d2abe49\",\n+                \"reference\": \"4120703b9bda8795075047b40361d7ec4d2abe49\",\n                 \"shasum\": \"\"\n             },\n             \"require\": {\n@@ -3816,7 +3816,7 @@\n                 \"irc\": \"ircs:\u002F\u002Firc.libera.chat:6697\u002Fcomposer\",\n                 \"issues\": \"https:\u002F\u002Fgithub.com\u002Fcomposer\u002Fcomposer\u002Fissues\",\n                 \"security\": \"https:\u002F\u002Fgithub.com\u002Fcomposer\u002Fcomposer\u002Fsecurity\u002Fpolicy\",\n-                \"source\": \"https:\u002F\u002Fgithub.com\u002Fcomposer\u002Fcomposer\u002Ftree\u002F2.10.0\"\n+                \"source\": \"https:\u002F\u002Fgithub.com\u002Fcomposer\u002Fcomposer\u002Ftree\u002F2.10.1\"\n             },\n             \"funding\": [\n                 {\n@@ -3828,7 +3828,7 @@\n                     \"type\": \"github\"\n                 }\n             ],\n-            \"time\": \"2026-05-28T09:22:08+00:00\"\n+            \"time\": \"2026-06-04T08:25:59+00:00\"\n         },\n         {\n             \"name\": \"composer\u002Fmetadata-minifier\",\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fknit-pay\u002F9.4.0.0\u002Fgateways\u002FPayPal\u002FGateway.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fknit-pay\u002F9.4.0.1\u002Fgateways\u002FPayPal\u002FGateway.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fknit-pay\u002F9.4.0.0\u002Fgateways\u002FPayPal\u002FGateway.php\t2026-05-14 13:29:52.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fknit-pay\u002F9.4.0.1\u002Fgateways\u002FPayPal\u002FGateway.php\t2026-06-04 08:44:12.000000000 +0000\n@@ -153,9 +153,8 @@\n \t\t\twp_send_json_error( [ 'message' => 'Security check failed. Please refresh the page and try again.' ], 403 );\n \t\t}\n \n-\t\t$order_id   = isset( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : '';\n \t\t$payment_id = isset( $_POST['payment_id'] ) ? absint( $_POST['payment_id'] ) : 0;\n-\t\tif ( empty( $order_id ) || empty( $payment_id ) ) {\n+\t\tif ( empty( $payment_id ) ) {\n \t\t\twp_send_json_error( [ 'message' => 'Invalid request parameters.' ], 400 );\n \t\t}\n \n@@ -164,11 +163,15 @@\n \t\t\twp_send_json_error( [ 'message' => 'Payment not found.' ], 404 );\n \t\t}\n \n+\t\t$order_id = $payment->get_meta( 'paypal_order_id' );\n+\t\tif ( empty( $order_id ) ) {\n+\t\t\twp_send_json_error( [ 'message' => 'No PayPal order found for this payment.' ], 400 );\n+\t\t}\n+\n \t\ttry {\n \t\t\t$integration = new Integration();\n \t\t\t$gateway     = $integration->get_gateway( $payment->get_config_id() );\n \n-\t\t\t$payment->set_transaction_id( $order_id );\n \t\t\t$gateway->update_status( $payment );\n \t\t\t$payment->save();","An unauthenticated attacker can manipulate payment records by first obtaining a valid 'knitpay_paypal_checkout' AJAX nonce from any public-facing page where the PayPal gateway is initialized. With this nonce, the attacker can send a POST request to admin-ajax.php with the action 'knitpay_paypal_capture_order'. By providing a target 'payment_id' (which can be enumerated) and a controlled 'order_id' string, the attacker forces the plugin to update the transaction ID and re-verify the payment status for that record. Since the function lacks ownership checks, this allows an attacker to modify metadata and potentially trigger status updates for payments they did not initiate.","gemini-3-flash-preview","2026-06-26 02:47:39","2026-06-26 02:49:00",{"type":40,"vulnerable_version":41,"fixed_version":11,"vulnerable_browse":42,"vulnerable_zip":43,"fixed_browse":44,"fixed_zip":45,"all_tags":46},"plugin","9.4.0.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fknit-pay\u002Ftags\u002F9.4.0.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fknit-pay.9.4.0.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fknit-pay\u002Ftags\u002F9.4.0.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fknit-pay.9.4.0.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fknit-pay\u002Ftags"]