ronin-rb/ronin-payloads

View on GitHub
lib/ronin/payloads/cli/commands/build.rb

Summary

Maintainability
A
15 mins
Test Coverage
# frozen_string_literal: true
#
# ronin-payloads - A Ruby micro-framework for writing and running exploit
# payloads.
#
# Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-payloads is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-payloads is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-payloads.  If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/payloads/cli/payload_command'
require 'ronin/payloads/cli/format_option'
require 'ronin/payloads/cli/encoder_methods'
require 'ronin/core/cli/options/param'

module Ronin
  module Payloads
    class CLI
      module Commands
        #
        # Loads and builds a payload.
        #
        # ## Usage
        #
        #     ronin-payloads build [options] {-f FILE | NAME}
        #
        # ## Options
        #
        #     -f, --file FILE                  The payload file to load
        #     -F hex|c|shell|powershell|xml|html|js|ruby,
        #         --format                     Formats the outputed data
        #     -p, --param NAME=VALUE           Sets a param
        #     -o, --output FILE                Output file to write the built payload to
        #     -e, --encoder ENCODER            Adds the encoder to the payload
        #         --encoder-param ENCODER.NAME=VALUE
        #                                      Sets a param for one of the encoders
        #     -D, --debug                      Enables debugging messages
        #     -h, --help                       Print help information
        #
        # ## Arguments
        #
        #     [NAME]                           The payload name to load
        #
        class Build < PayloadCommand

          include FormatOption
          include EncoderMethods
          include Core::CLI::Options::Param

          option :output, short: '-o',
                          value: {
                            type:  String,
                            usage: 'FILE'
                          },
                          desc: 'Output file to write the built payload to'

          option :encoder, short: '-e',
                           value: {
                             type:  String,
                             usage: 'ENCODER'
                           },
                           desc: 'Adds the encoder to the payload' do |name|
                             @encoder_ids << name
                           end

          option :encoder_param, value: {
                                   type: /\A[^\.=]+\.[^=]++=.+\z/,
                                   usage: 'ENCODER.NAME=VALUE'
                                 },
                                 desc: 'Sets a param for one of the encoders' do |str|
                                   name, value              = str.split('=',2)
                                   encoder_name, param_name = name.split('.',2)

                                   @encoder_params[encoder_name][param_name.to_sym] = value
                                 end

          option :debug, short: '-D',
                         desc: 'Enables debugging messages' do
                           Support::CLI::Printing.debug = true
                         end

          description 'Loads and builds a payload'

          man_page 'ronin-payloads-build.1'

          # The encoder names to load.
          #
          # @return [Array<String>]
          attr_reader :encoder_ids

          # The params for the encoders.
          #
          # @return [Hash{String => Hash{String => String}}]
          attr_reader :encoder_params

          #
          # Initializes the `ronin-payloads build` command.
          #
          # @param [Hash{Symbol => Object}] kwargs
          #   Additional keyword arguments.
          #
          def initialize(**kwargs)
            super(**kwargs)

            @encoder_ids    = []
            @encoder_params = Hash.new { |hash,key| hash[key] = {} }
          end

          #
          # Runs the `ronin-payloads build` command.
          #
          # @param [String, nil] name
          #   The name of the payload to load.
          #
          def run(name=nil)
            super(name)

            load_encoders
            initialize_payload
            validate_payload
            build_payload

            if options[:output] then write_payload
            else                     print_payload
            end
          end

          #
          # Loads the encoders using {#encoder_ids} which are populated by
          # the `-E,--encoder` option.
          #
          def load_encoders
            @encoders = @encoder_ids.map do |encoder_id|
              encoder_class = load_encoder(encoder_id)
              params        = @encoder_params[encoder_id]
              encoder       = initialize_encoder(encoder_class, params: params)

              validate_encoder(encoder)
              encoder
            end
          end

          #
          # Initializes the payload with `--param` and `--encoder` options.
          #
          def initialize_payload
            super(params: @params, encoders: @encoders)
          end

          #
          # Builds the {#payload}.
          #
          def build_payload
            @payload.perform_build
          rescue PayloadError => error
            print_error "failed to build the payload #{@payload_class.id}: #{error.message}"
            exit(-1)
          rescue => error
            print_exception(error)
            print_error "an unhandled exception occurred while building the payload #{@payload.class_id}"
            exit(-1)
          end

          #
          # The built payload.
          #
          # @return [String]
          #
          def built_payload
            @payload.built_payload
          end

          #
          # The built and encoded payload.
          #
          # @return [String]
          #
          def encoded_payload
            @payload.encoded_payload
          end

          #
          # Writes the built and optionally encoded payload to the output file.
          #
          def write_payload
            File.binwrite(options[:output],format_data(encoded_payload))
          end

          #
          # Prints the built and optionally encoded payload.
          #
          def print_payload
            print_data(@payload.encode_payload)
          end

        end
      end
    end
  end
end