[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f42JVNDEBmyAcQDW_vQKdq6NrlpdXcbnAarJQUrB60WA":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":27,"research_verified":28,"research_rounds_completed":29,"research_plan":30,"research_summary":31,"research_vulnerable_code":32,"research_fix_diff":33,"research_exploit_outline":34,"research_model_used":35,"research_started_at":36,"research_completed_at":37,"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":28,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":28,"source_links":38},"CVE-2026-39562","client-invoicing-by-sprout-invoices-missing-authorization-3","Client Invoicing by Sprout Invoices \u003C= 20.8.10 - Missing Authorization","The Client Invoicing by Sprout Invoices plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 20.8.10. This makes it possible for unauthenticated attackers to perform an unauthorized action.","sprout-invoices",null,"\u003C=20.8.10","20.8.11","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-03-19 00:00:00","2026-04-15 21:29:32",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F735b20ff-a708-4e1c-b96e-bcdec72aad95?source=api-prod",28,[22,23,24,25,26],"Sprout_Invoices.class.php","controllers\u002F_Controller.php","readme.txt","resources\u002Fadmin\u002Fjs\u002Fest_and_invoices.js","resources\u002Fadmin\u002Fjs\u002Fsprout_invoice.js","researched",false,3,"# Exploitation Research Plan - CVE-2026-39562\n\n## 1. Vulnerability Summary\nThe **Client Invoicing by Sprout Invoices** plugin (\u003C= 20.8.10) contains a missing authorization vulnerability within its AJAX handlers. Specifically, the functions `maybe_create_private_note` and `maybe_change_status` are registered with both `wp_ajax_` and `wp_ajax_nopriv_` hooks in `controllers\u002F_Controller.php`, but they fail to perform adequate capability checks (e.g., `current_user_can( 'edit_sprout_invoices' )`). \n\nThis allows unauthenticated attackers to perform unauthorized actions such as creating private notes on invoices\u002Festimates or changing the status of a document (e.g., marking an invoice as \"paid\" or \"void\").\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action**: `si_change_doc_status` (for status manipulation) or `sa_create_private_note` (for adding notes).\n- **Authentication**: None required (exploiting the `nopriv` hooks).\n- **Parameters**:\n    - `action`: `si_change_doc_status`\n    - `id`: The post ID of the target `sa_invoice` or `sa_estimate`.\n    - `status`: The desired new status (e.g., `paid`, `void`, `temp","The Sprout Invoices plugin for WordPress is vulnerable to unauthorized status manipulation due to missing authorization and capability checks in its AJAX handlers. This allows unauthenticated attackers to change the status of any invoice or estimate—for example, marking an invoice as 'paid' or 'void'—by exploiting functions registered with unauthenticated (nopriv) access hooks.","\u002F\u002F controllers\u002F_Controller.php\n\n\u002F\u002F Line 98-101: Registration of nopriv hooks allows unauthenticated access to status change functions\nadd_action( 'wp_ajax_sa_create_private_note',  array( static::class, 'maybe_create_private_note' ), 10, 0 );\nadd_action( 'wp_ajax_nopriv_sa_create_private_note',  array( static::class, 'maybe_create_private_note' ), 10, 0 );\nadd_action( 'wp_ajax_si_change_doc_status',  array( static::class, 'maybe_change_status' ), 10, 0 );\nadd_action( 'wp_ajax_nopriv_si_change_doc_status',  array( static::class, 'maybe_change_status' ), 10, 0 );\n\n---\n\n\u002F\u002F controllers\u002F_Controller.php around line 1090\n\npublic static function maybe_change_status() {\n    \u002F\u002F ... nonce checks usually follow, but specific capability checks are missing ...\n    $doc_id = ( isset( $_REQUEST['id'] ) ) ? $_REQUEST['id'] : 0 ;\n    if ( ! $doc_id ) {\n        return;\n    }\n\n    $view = '';\n    $new_status = sanitize_text_field( wp_unslash( $_REQUEST['status'] ) );\n    switch ( get_post_type( $doc_id ) ) {\n        case SI_Invoice::POST_TYPE:\n            $doc = SI_Invoice::get_instance( $doc_id );\n            $doc->set_status( $new_status );\n            $view = self::load_view( 'admin\u002Fsections\u002Finvoice-status-toggle', array(\n                'id' => $doc_id,\n                'status' => $new_status,\n            ), false );\n            break;\n        case SI_Estimate::POST_TYPE:\n            $doc = SI_Estimate::get_instance( $doc_id );\n            $doc->set_status( $new_status );\n            $view = self::load_view( 'admin\u002Fsections\u002Festimate-status-toggle', array(\n                'id' => $doc_id,\n                'status' => $new_status,\n            ), false );\n            break;\n        default:\n            break;\n    }\n    \u002F\u002F ...","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsprout-invoices\u002F20.8.10\u002Fcontrollers\u002F_Controller.php\t2026-02-16 21:08:28.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsprout-invoices\u002F20.8.11\u002Fcontrollers\u002F_Controller.php\t2026-03-16 15:33:18.000000000 +0000\n@@ -1092,7 +1090,19 @@\n \n \t\t$view = '';\n \t\t$new_status = sanitize_text_field( wp_unslash( $_REQUEST['status'] ) );\n-\t\tswitch ( get_post_type( $doc_id ) ) {\n+\t\t$post_type  = get_post_type( $doc_id );\n+\n+\t\t\u002F\u002F Unauthenticated users are limited to accepting or declining estimates.\n+\t\t\u002F\u002F No frontend UI exposes any other status action to unauthenticated clients:\n+\t\t\u002F\u002F all four themes and the embeds bundle only render accept\u002Fdecline buttons on\n+\t\t\u002F\u002F estimates, and no invoice templates expose a status-change button at all.\n+\t\tif ( ! is_user_logged_in() ) {\n+\t\t\tif ( SI_Estimate::POST_TYPE !== $post_type || ! in_array( $new_status, array( 'accept', 'decline' ), true ) ) {\n+\t\t\t\tself::ajax_fail( 'You do not have permission to change this status.' );\n+\t\t\t}\n+\t\t}\n+\n+\t\tswitch ( $post_type ) {\n \t\t\tcase SI_Invoice::POST_TYPE:\n \t\t\t\t$doc = SI_Invoice::get_instance( $doc_id );\n \t\t\t\t$doc->set_status( $new_status );","The exploit targets the AJAX endpoint `\u002Fwp-admin\u002Fadmin-ajax.php`. An unauthenticated attacker sends a POST request with the action `si_change_doc_status`. The payload includes the target document's post ID (`id`) and the desired status (e.g., `paid`, `void`, or `temp`). Because the plugin registers this action via `wp_ajax_nopriv_si_change_doc_status` and fails to check user capabilities or restrict unauthenticated requests to legitimate 'accept\u002Fdecline' estimate transitions, the backend updates the invoice or estimate status based on the attacker's input. Attackers may also exploit `sa_create_private_note` to inject unauthorized comments onto private records.","gemini-3-flash-preview","2026-04-18 02:20:34","2026-04-18 02:21:55",{"type":39,"vulnerable_version":40,"fixed_version":11,"vulnerable_browse":41,"vulnerable_zip":42,"fixed_browse":43,"fixed_zip":44,"all_tags":45},"plugin","20.8.10","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsprout-invoices\u002Ftags\u002F20.8.10","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsprout-invoices.20.8.10.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsprout-invoices\u002Ftags\u002F20.8.11","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsprout-invoices.20.8.11.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsprout-invoices\u002Ftags"]