Land #6373, joomla mixin

bug/bundler_fix
Christian Mehlmauer 2015-12-22 21:10:46 +01:00
commit 57b850c7af
No known key found for this signature in database
GPG Key ID: DCF54A05D6E62591
7 changed files with 317 additions and 128 deletions

View File

@ -0,0 +1,29 @@
# -*- coding: binary -*-
module Msf
class Exploit
class Remote
module HTTP
module Joomla
require 'msf/core/exploit/http/joomla/base'
require 'msf/core/exploit/http/joomla/version'
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::Joomla::Base
include Msf::Exploit::Remote::HTTP::Joomla::Version
def initialize(info = {})
super
register_options(
[
Msf::OptString.new('TARGETURI', [true, 'The base path to the Joomla application', '/'])
], Msf::Exploit::Remote::HTTP::Joomla
)
end
end
end
end
end
end

View File

@ -0,0 +1,30 @@
# -*- coding: binary -*-
module Msf::Exploit::Remote::HTTP::Joomla::Base
# Checks if Joomla is up and running.
#
# @return [TrueClass] Joomla is up and running.
# @return [FalseClass] Joomla is not up.
def joomla_and_online?
# Possible paths that we might see the generator tag.
paths = [ '/', '/administrator/' ]
paths.each do |path|
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, path)
})
if res
elements = res.get_html_meta_elements
elements.each_entry do |e|
if e.attributes['content'] && /joomla!/i === e.attributes['content'].value
return true
end
end
end
end
false
end
end

View File

@ -0,0 +1,96 @@
# -*- coding: binary -*-
module Msf::Exploit::Remote::HTTP::Joomla::Version
# Returns the Joomla version.
#
# @return [String] Joomla version (if found)
# @return [NilClass] No Joomla version found.
def joomla_version
files = [
'administrator/manifests/files/joomla.xml',
'language/en-GB/en-GB.xml',
'templates/system/css/system.css',
'media/system/js/mootools-more.js',
'language/en-GB/en-GB.ini',
'htaccess.txt',
'language/en-GB/en-GB.com_media.ini'
]
files.each do |file|
version = check_file_version(file)
return version if version
end
nil
end
private
# Returns the Joomla version based on a response.
#
# @param res [Rex::Proto::Http::Response] Response to fingerprint from.
# @return [String] Joomla version (if found)
# @return [NilClass] No Joomla version found.
def fingerprint(res)
version = nil
case res.body
when /<version.*\/?>[[:print:]]+<\/version\/?>/i
version = res.get_html_document.at('version').text
when /system\.css 20196 2011\-01\-09 02\:40\:25Z ian/,
/MooTools\.More\=\{version\:\"1\.3\.0\.1\"/,
/en-GB\.ini 20196 2011\-01\-09 02\:40\:25Z ian/,
/en-GB\.ini 20990 2011\-03\-18 16\:42\:30Z infograf768/,
/20196 2011\-01\-09 02\:40\:25Z ian/
version = '1.6'
when /system\.css 21322 2011\-05\-11 01\:10\:29Z dextercowley /,
/MooTools\.More\=\{version\:\"1\.3\.2\.1\"/,
/22183 2011\-09\-30 09\:04\:32Z infograf768/,
/21660 2011\-06\-23 13\:25\:32Z infograf768/
version = '1.7'
when /Joomla! 1\.5/,
/MooTools\=\{version\:\'1\.12\'\}/,
/11391 2009\-01\-04 13\:35\:50Z ian/
version = '1.5'
when /Copyright \(C\) 2005 \- 2012 Open Source Matters/,
/MooTools.More\=\{version\:\"1\.4\.0\.1\"/
version = '2.5'
when /<meta name=\"Keywords\" content=\"(.*)\">\s+<meta name/
version = $1.split(/,/)[0]
when /(Copyright \(C\) 2005 \- 200(6|7))/,
/47 2005\-09\-15 02\:55\:27Z rhuk/,
/423 2005\-10\-09 18\:23\:50Z stingrey/,
/1005 2005\-11\-13 17\:33\:59Z stingrey/,
/1570 2005\-12\-29 05\:53\:33Z eddieajau/,
/2368 2006\-02\-14 17\:40\:02Z stingrey/,
/4085 2006\-06\-21 16\:03\:54Z stingrey/,
/4756 2006\-08\-25 16\:07\:11Z stingrey/,
/5973 2006\-12\-11 01\:26\:33Z robs/,
/5975 2006\-12\-11 01\:26\:33Z robs/
version = '1.0'
end
version
end
# Returns the Joomla version based on a file path.
#
# @param file [String] File path to check.
# @return [String] Joomla version (if found)
# @return [NilClass] No Joomla version found.
def check_file_version(file)
version = nil
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, file)
})
if res && res.code == 200
version = fingerprint(res)
end
version
end
end

View File

@ -110,6 +110,7 @@ require 'msf/core/exploit/browser_autopwn2'
# Custom HTTP Modules
require 'msf/core/exploit/http/wordpress'
require 'msf/core/exploit/http/joomla'
require 'msf/core/exploit/http/typo3'
require 'msf/core/exploit/http/jboss'

View File

@ -2,11 +2,12 @@
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::Joomla
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
@ -22,146 +23,49 @@ class Metasploit3 < Msf::Auxiliary
'Author' => [ 'newpid0' ],
'License' => MSF_LICENSE
)
register_options(
[
OptString.new('TARGETURI', [ true, "The path to the Joomla install", '/'])
], self.class)
end
def os_fingerprint(response)
if not response.headers.has_key?('Server')
return "Unknown OS (No Server Header)"
end
case response.headers['Server']
when /Win32/, /\(Windows/, /IIS/
os = "Windows"
when /Apache\//
os = "*Nix"
else
os = "Unknown Server Header Reporting: "+response.headers['Server']
end
return os
end
def fingerprint(response)
case response.body
when /<version.*\/?>(.+)<\/version\/?>/i
v = $1
out = (v =~ /^6/) ? "Joomla #{v}" : " #{v}"
when /system\.css 20196 2011\-01\-09 02\:40\:25Z ian/,
/MooTools\.More\=\{version\:\"1\.3\.0\.1\"/,
/en-GB\.ini 20196 2011\-01\-09 02\:40\:25Z ian/,
/en-GB\.ini 20990 2011\-03\-18 16\:42\:30Z infograf768/,
/20196 2011\-01\-09 02\:40\:25Z ian/
out = "1.6"
when /system\.css 21322 2011\-05\-11 01\:10\:29Z dextercowley /,
/MooTools\.More\=\{version\:\"1\.3\.2\.1\"/,
/22183 2011\-09\-30 09\:04\:32Z infograf768/,
/21660 2011\-06\-23 13\:25\:32Z infograf768/
out = "1.7"
when /Joomla! 1.5/,
/MooTools\=\{version\:\'1\.12\'\}/,
/11391 2009\-01\-04 13\:35\:50Z ian/
out = "1.5"
when /Copyright \(C\) 2005 \- 2012 Open Source Matters/,
/MooTools.More\=\{version\:\"1\.4\.0\.1\"/
out = "2.5"
when /<meta name=\"Keywords\" content=\"(.*)\">\s+<meta name/
out = $1.split(/,/)[0]
when /(Copyright \(C\) 2005 - 200(6|7))/,
/47 2005\-09\-15 02\:55\:27Z rhuk/,
/423 2005\-10\-09 18\:23\:50Z stingrey/,
/1005 2005\-11\-13 17\:33\:59Z stingrey/,
/1570 2005\-12\-29 05\:53\:33Z eddieajau/,
/2368 2006\-02\-14 17\:40\:02Z stingrey/,
/4085 2006\-06\-21 16\:03\:54Z stingrey/,
/4756 2006\-08\-25 16\:07\:11Z stingrey/,
/5973 2006\-12\-11 01\:26\:33Z robs/,
/5975 2006\-12\-11 01\:26\:33Z robs/
out = "1.0"
else
out = 'Unknown Joomla'
end
return out
end
def check_file(tpath, file, ip)
def get_server_header
# This module used to determine the operating system by the server header. But this is
# not an accurate way to do OS detection, so we have toned it down to just returning the
# header, and let the user decide.
res = send_request_cgi({
'uri' => "#{tpath}#{file}",
'method' => 'GET'
'uri' => normalize_uri(target_uri.path)
})
return :abort if res.nil?
res.body.gsub!(/[\r|\n]/, ' ')
if (res.code == 200)
os = os_fingerprint(res)
out = fingerprint(res)
return false if not out
if(out =~ /Unknown Joomla/)
print_error("#{peer} - Unable to identify Joomla Version with #{file}")
return false
else
print_good("#{peer} - Joomla Version:#{out} from: #{file} ")
print_good("#{peer} - OS: #{os}")
report_note(
:host => ip,
:port => datastore['RPORT'],
:proto => 'http',
:ntype => 'joomla_version',
:data => out
)
return true
end
elsif (res.code == 403)
if(res.body =~ /secured with Secure Sockets Layer/ or res.body =~ /Secure Channel Required/ or res.body =~ /requires a secure connection/)
vprint_status("#{ip} denied access to #{ip} (SSL Required)")
elsif(res.body =~ /has a list of IP addresses that are not allowed/)
vprint_status("#{ip} restricted access by IP")
elsif(res.body =~ /SSL client certificate is required/)
vprint_status("#{ip} requires a SSL client certificate")
else
vprint_status("#{ip} denied access to #{ip} #{res.code} #{res.message}")
end
return :abort
if res && res.headers['Server']
return res.headers['Server']
end
return false
rescue OpenSSL::SSL::SSLError
vprint_error("#{peer} - SSL error")
return :abort
rescue Errno::ENOPROTOOPT, Errno::ECONNRESET, ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::ArgumentError
vprint_error("#{peer} - Unable to Connect")
return :abort
rescue ::Timeout::Error, ::Errno::EPIPE
vprint_error("#{peer} - Timeout error")
return :abort
nil
end
def run_host(ip)
tpath = normalize_uri(target_uri.path)
if tpath[-1,1] != '/'
tpath += '/'
unless joomla_and_online?
print_error("It doesn't look like Joomla is up and running at #{target_uri.to_s}")
return
end
files = [
'language/en-GB/en-GB.xml',
'templates/system/css/system.css',
'media/system/js/mootools-more.js',
'language/en-GB/en-GB.ini',
'htaccess.txt',
'language/en-GB/en-GB.com_media.ini'
]
server = get_server_header
version = joomla_version
vprint_status("#{peer} - Checking Joomla version")
files.each do |file|
joomla_found = check_file(tpath, file, ip)
return if joomla_found == :abort
break if joomla_found
if server
print_status("Server: #{server}")
else
print_error("Unable to determine server.")
end
if version
print_status("Joomla version: #{version}")
report_note(
host: ip,
port: datastore['RPORT'],
proto: ssl ? 'https' : 'http',
ntype: 'joomla.version',
data: version
)
else
print_error("Unable to find Joomla version.")
end
end

View File

@ -0,0 +1,50 @@
require 'msf/core'
require 'msf/core/exploit'
require 'rex/proto/http/response'
require 'msf/core/exploit/http/joomla'
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Base do
subject do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Exploit::Remote::HTTP::Joomla
mod.send(:initialize)
mod
end
let(:joomla_body) do
%Q|
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb" lang="en-gb" dir="ltr">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="http://172.16.23.131/joomla/" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="test" />
<meta name="generator" content="Joomla! - Open Source Content Management" />
<title>Home</title>
</head>
</html>
|
end
let(:rex_http_response) do
res = Rex::Proto::Http::Response.new
res.code = 200
res.body = joomla_body
res
end
before(:each) do
allow(subject).to receive(:send_request_cgi) do |opts|
rex_http_response
end
end
describe '#joomla_and_online?' do
context 'When site is Joomla' do
it { expect(subject.joomla_and_online?).to be_truthy }
end
end
end

View File

@ -0,0 +1,79 @@
require 'msf/core'
require 'msf/core/exploit'
require 'rex/proto/http/response'
require 'msf/core/exploit/http/joomla'
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Version do
subject do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Exploit::Remote::HTTP::Joomla
mod.send(:initialize)
mod
end
# From /joomla/language/en-GB/en-GB.xml
let(:joomla_body) do
%Q|
<?xml version="1.0" encoding="utf-8"?>
<metafile version="3.4" client="site">
<name>English (en-GB)</name>
<version>3.4.3</version>
<creationDate>2013-03-07</creationDate>
<author>Joomla! Project</author>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<copyright>Copyright (C) 2005 - 2015 Open Source Matters. All rights reserved.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<description>en-GB site language</description>
<metadata>
<name>English (en-GB)</name>
<tag>en-GB</tag>
<rtl>0</rtl>
<locale>en_GB.utf8, en_GB.UTF-8, en_GB, eng_GB, en, english, english-uk, uk, gbr, britain, england, great britain, uk, united kingdom, united-kingdom</locale>
<firstDay>0</firstDay>
<weekEnd>0,6</weekEnd>
</metadata>
<params />
</metafile>
|
end
let(:expected_version) do
'3.4.3'
end
let(:version_file) do
'/joomla/language/en-GB/en-GB.xml'
end
let(:rex_http_response) do
res = Rex::Proto::Http::Response.new
res.code = 200
res.body = joomla_body
res
end
before(:each) do
allow(subject).to receive(:send_request_cgi) do |opts|
rex_http_response
end
end
describe '#joomla_version' do
context 'When a version file is found' do
it { expect(subject.joomla_version).to eq(expected_version) }
end
end
describe '#fingerprint' do
context 'When a version is found' do
it { expect(subject.send(:fingerprint, rex_http_response)).to eq(expected_version) }
end
end
describe '#check_file_version' do
context 'When a version is found' do
it { expect(subject.send(:check_file_version, version_file)).to eq(expected_version) }
end
end
end