Skip to content
This repository was archived by the owner on Oct 22, 2020. It is now read-only.

Introduction to writing modules

Rob edited this page Aug 11, 2018 · 5 revisions

Choosing between the Auxiliary or Exploit namespace

A module needs to be placed in either the auxiliary or exploit namespace. If your module does not support or require a payload to be used (i.e. executing of arbitrary PHP code on the remote system), then your module should be considered an auxiliary module.

To declare your module in the auxiliary namespace, simply prefix the class name with Wpxf::Auxiliary:

class Wpxf::Auxiliary::YourModuleName < Wpxf::Module
end

Examples of auxiliary modules are:

  • Modules which use SQL injection vulnerabilities to extract information from the target
  • Modules which allow for files to be downloaded from the target
  • Modules which provide directory listing disclosures on the target system
  • Modules which escalate privileges on the target system

Alternatively, if your module does need to utilize a payload, it can be declared as an exploit as below:

class Wpxf::Exploit::YourModuleName < Wpxf::Module
end

Registering the module information

In the initialize method of your module, the first thing that should be done (after calling super) is to register the module information. This will be used to serve information about your module when the user executes the info command and will also appear in search results.

As can be seen in the code extract below, taken from the auxiliary/dos/load_scripts_dos module, we can use the update_info method to specify a hash of data that defines the module.

update_info(
  name: 'WordPress "load-scripts.php" DoS',
  desc: %(
    All versions of WordPress, as of March, 2018, are vulnerable to a
    denial of service attack by making large amounts of requests to the
    load-scripts.php file. This module allows users to configure a maximum
    number of requests (via `max_requests`), and the number of threads to
    use (`max_http_concurrency`) and will execute the requests and then
    check the status of the website.
  ),
  author: [
    'Barak Tawily', # Vulnerability disclosure
    'rastating'     # WPXF module
  ],
  references: [
    ['CVE', '2018-6389'],
    ['WPVDB', '9021'],
    ['URL', 'https://baraktawily.blogspot.co.uk/2018/02/how-to-dos-29-of-world-wide-websites.html']
  ],
  date: 'Feb 05 2018'
)

The keys available to the update_info method are as follows:

Name Description Required?
name The name of the module Yes
desc The description of the module Yes
author An array of author names Yes
references An array of reference arrays No
date The date the vulnerability that the module uses was disclosed Yes

When specifying the references, the first element in each reference array is the type identifier and the second element is the reference identifier, or the URL if using the URL reference type. All supported reference types are as follows:

Key Description
CVE A CVE-ID reference
EDB An Exploit DB reference
OSVDB An OSVDB reference
URL A URL with relevant information (such as initial public disclosure)
WPVDB A WPScan Vulnerability Database reference

Registering and using options

To allow users to configure the module you need to register and use options in the module. In the auxiliary/dos/post_grid_file_deletion module, an option is registered to allow the user to specify the file to delete.

register_options([
  StringOption.new(
    name: 'remote_file',
    desc: 'The relative or absolute path of the file to delete (relative to /wp-admin/)',
    required: true
  )
])

For a full list of available keys that can be specified for an option, have a look at the documentation for the initialize method for the appropriate option class.

To access the value that the user has specified, you can use get_option_value, normalized_option_value or access the raw value entered directly using the datastore attribute.

If we had a boolean option called "test", these three methods of accessing its value would appear as follows if the option was set to 1 by the user using set test 1:

puts get_option_value('test')         # Outputs 1
puts normalized_option_value('test')  # Outputs true
puts datastore['test']                # Outputs 1

The option classes available are:

Checking if the target is vulnerable

In most cases, there are non-intrusive checks we can perform to determine whether or not the target is vulnerable to the module we're writing. If this is the case, you should implement the check method.

The return value of check indicates to WPXF whether or not the module is vulnerable and accepts the following values:

Return Value Description
:safe The target is not vulnerable to this module
:vulnerable The target appears to be vulnerable to this module
:unknown A status could not be determined for the target

Usually, a target's vulnerability status can be determined by doing some basic fingerprinting. To aid with this, a number of helper methods can be found in the Wpxf::WordPress::Fingerprint mixin.

Implementing the module logic

All the code used to run the module should be placed in the run method. This method returns true if the module was executed successfully or false if anything went wrong.

The first line of the method should be a call to super, which will verify that the target is running WordPress and is online or return false. If you wanted to make a module that verifies the target is online and running WordPress and then outputs an arbitrary string to the console, you could implement like this:

def run
  return false unless super # Verify that it's online or return false
  emit_info 'Hello world!' # Output "Hello world!" to the console
  true # Return true to indicate the module ran successfully
end

Making HTTP requests

The Wpxf::Net::HttpClient mixin provides a variety of helper functions in order to make HTTP interaction easier. All of the methods provided to execute HTTP requests return a Wpxf::Net::HttpResponse which contains the response code, body, headers, cookies and a boolean indicating whether or not the request timed_out?.

Below are some examples taken from existing modules that demonstrate how the various methods can be used.

Downloading a binary file to disk

res = download_file(
  url: downloader_url,
  method: :get,
  params: { 'file_link' => remote_file },
  local_filename: export_path
)

Executing a simple GET request

res = execute_get_request(url: payload_url)

Executing a simple POST request

res = execute_post_request(
  url: wordpress_url_admin_ajax,
  params: { 'action' => 'import_data' },
  body: { 'name' => name, 'code' => encoded_value },
  cookie: cookie
)

Executing a POST request using Wpxf::Utility::BodyBuilder

def payload_body_builder(payload_name)
  builder = Utility::BodyBuilder.new
  builder.add_field('elementCode', 'ajaxUpload')
  builder.add_file_from_string('wpshop_file', payload.encoded, payload_name)
  builder
end

builder = payload_body_builder(payload_name)
emit_info 'Uploading payload...'
res = nil
builder.create do |body|
  res = execute_post_request(url: uploader_url, body: body)
end

Accessing the selected payload

In the context of an exploit module, you will have access to the payload object, which is an instance of Wpxf::Payload. This object allows you to access the encoded payload and the payload in its raw form. In most cases, the method you'll want to use is encoded.

Uploading the payload as a WordPress plugin

Included in WPXF is a helper method for uploading the selected payload as a WordPress plugin, for instances where you are able to acquire a valid WordPress admin session.

Below is an example taken from exploit/shell/admin_shell_upload which demonstrates how to do this:

emit_info 'Uploading payload...'
res = upload_payload_as_plugin_and_execute(
  Utility::Text.rand_alpha(10),
  Utility::Text.rand_alpha(10),
  session_cookie
)

In order for this example to work, the requires_authentication method must be overridden and return true; otherwise, session_cookie will be nil. Alternatively, as long as a valid session cookie is passed into upload_payload_as_plugin_and_execute, the use of requires_authentication is not needed.

Writing information to the screen

You are encouraged to use the emit_* methods wherever possible to output information to the screen as this allows for a consistent output style to be maintained, however, falling back to puts, print etc. may be required in some instances and within reason is acceptable.

Documentation for all the emit_* methods can be found in the documentation here: https://rastating.github.io/wordpress-exploit-framework/Wpxf/OutputEmitters.html

Further reading

For more information about the WPXF API, take a look at the documentation at https://rastating.github.io/wordpress-exploit-framework