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

Introduction to writing payloads

Rob edited this page Aug 11, 2018 · 2 revisions

What are payloads?

Exploit modules support (and require) the use of payloads to function. A payload, in WPXF, is simply the arbitrary PHP code that we intend to run on the target system via the vulnerability that the module exploits.

Example payload

The below example illustrates how to register an option and use it when generating the payload.

module Wpxf::Payloads
  class YourPayload < Wpxf::Payload
    include Wpxf
    include Wpxf::Options

    def initialize
      super

      register_options([
        StringOption.new(
          name: 'greeting',
          desc: 'A greeting to display',
          required: true,
          default: 'Hello, world!',
        )
      ])
    end

    def greeting
      escape_single_quotes(datastore['greeting'])
    end

    def raw
      "echo '#{greeting}';"
    end
  end
end

When this payload is generated in WPXF, assuming encoding is disabled, the final PHP script would look like this (assuming the user didn't change the greeting option):

<?php echo 'Hello, world!'; ?>

Using random variable names

In most (if not all) of the existing payloads, the majority of variable names are generated randomly at runtime in order to make fingerprinting the payload more difficult. A helper method, generate_vars exists to aid in doing this, by accepting an array of keys and generating a random and unique variable name for each one and returning them in a hash.

If you want to contribute a payload to the project, it's encouraged you use this technique.

An example of how to do this manually can be seen below:

module Wpxf::Payloads
  class YourPayload < Wpxf::Payload
    include Wpxf
    include Wpxf::Options

    def initialize
      super

      register_options([
        StringOption.new(
          name: 'greeting',
          desc: 'A greeting to display',
          required: true,
          default: 'Hello, world!',
        )
      ])
    end

    def greeting
      escape_single_quotes(datastore['greeting'])
    end

    def raw
      vars = generate_vars([:greeting])

      <<-END_OF_PHP_CODE
        $#{vars[:greeting]} = '#{greeting}'
        echo $#{vars[:greeting]};"
      END_OF_PHP_CODE
    end
  end
end

Alternatively, you can use the obfuscated_variables helper method to automatically obfuscate multiple variables. An example of this can be found in the bind_tcp payload:

def obfuscated_variables
  super +
    [
      'cmd', 'disabled', 'output', 'handle', 'pipes', 'fp',
      'port', 'scl', 'sock', 'ret', 'msg_sock', 'r', 'w', 'e'
    ]
end

In this case, any instances of $cmd, $disabled etc. will be replaced with names that are generated at runtime. The benefit of using this helper method is that it removes the need to write interpolated code, as seen in the first example.

The first example could be re-written using obfuscated_variables like this:

module Wpxf::Payloads
  class YourPayload < Wpxf::Payload
    include Wpxf
    include Wpxf::Options

    def initialize
      super

      register_options([
        StringOption.new(
          name: 'greeting',
          desc: 'A greeting to display',
          required: true,
          default: 'Hello, world!',
        )
      ])
    end

    def greeting
      escape_single_quotes(datastore['greeting'])
    end

    def raw
      <<-END_OF_PHP_CODE
        $greeting_var = '#{greeting}'
        echo $greeting_var;
      END_OF_PHP_CODE
    end
  end
end

Defining PHP constants

By overriding the constants method, it is possible to define a hash of values that should be replaced in the final PHP script generated by the payload.

If it was overridden to return foo and bar as constants, any instance of $foo or bar would be replaced with the values assigned to them:

def constants
  { 'foo' => 'replaced foo', 'bar' => 'replaced bar' }
end

For example:

<?php
  echo $foo;
  echo $bar; 
?>

Would be transformed into:

<?php
  echo 'replaced foo';
  echo 'replaced bar';
?>

Checking for potential issues when loading a payload

In some cases, it may be a good idea to run checks when the user loads the payload to make sure that there are no conflicts or to simply provide information to the user about the payload. An example of this can be found in the bind_php payload:

def check(mod)
  if mod.get_option('proxy')
    mod.emit_warning 'The proxy option for this module is only used for '\
                     'HTTP connections and will NOT be used for the TCP '\
                     'connection that the payload establishes'
  end
end

As the TCP connection that is made via the payload does not go through the proxy set in the proxy option, we raise a warning to the user if there is a proxy option present in the module that the payload has been loaded into.

Pre and post exploit actions

In some scenarios, some preparation will be required to be taken before we execute the payload; such as starting a TCP server in the reverse_tcp payload.

To implement pre-exploit actions, override the prepare method in your payload and return true if the preparation was successful. This will be executed prior to Module#run being invoked.

By the same token, there may be resources associated with the payload that need to be cleaned up once we have finished running the module. To achieve this, override the post_exploit method and again return true if the cleanup was successful.