Land #6373, joomla mixin
commit
57b850c7af
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue