106 lines
2.5 KiB
Ruby
106 lines
2.5 KiB
Ruby
# -*- coding: binary -*-
|
|
|
|
module Msf
|
|
module Exploit::Remote::HTTP::Drupal
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
def initialize(info = {})
|
|
super
|
|
|
|
register_options([
|
|
OptString.new('TARGETURI', [true, 'Path to Drupal install', '/'])
|
|
])
|
|
end
|
|
|
|
def setup
|
|
super
|
|
|
|
# Ensure we don't hit a redirect (e.g., /drupal -> /drupal/)
|
|
# XXX: Naughty datastore modification instead of send_request_cgi!
|
|
datastore['TARGETURI'] = normalize_uri(datastore['TARGETURI'], '/')
|
|
end
|
|
|
|
# Determine Drupal version
|
|
#
|
|
# @return [Gem::Version] Version as Gem::Version
|
|
def drupal_version
|
|
res = send_request_cgi(
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path)
|
|
)
|
|
|
|
return unless res && res.code == 200
|
|
|
|
# Check for an X-Generator header
|
|
version = version_match(res.headers['X-Generator'])
|
|
|
|
return version if version
|
|
|
|
# Check for a <meta> tag
|
|
generator = res.get_html_document.at(
|
|
'//meta[@name = "Generator"]/@content'
|
|
)
|
|
|
|
return unless generator
|
|
|
|
version_match(generator.value)
|
|
end
|
|
|
|
# Return CHANGELOG.txt
|
|
#
|
|
# @param version [Gem::Version] Gem::Version or version string
|
|
# @return [String] CHANGELOG.txt as a string
|
|
def drupal_changelog(version)
|
|
return unless version && Gem::Version.correct?(version)
|
|
|
|
uri = Gem::Version.new(version) < Gem::Version.new('8') ?
|
|
normalize_uri(target_uri.path, 'CHANGELOG.txt') :
|
|
normalize_uri(target_uri.path, 'core/CHANGELOG.txt')
|
|
|
|
res = send_request_cgi(
|
|
'method' => 'GET',
|
|
'uri' => uri
|
|
)
|
|
|
|
return unless res && res.code == 200
|
|
|
|
res.body
|
|
end
|
|
|
|
# Check CHANGELOG.txt for patch level
|
|
#
|
|
# @param changelog [String] CHANGELOG.txt to search
|
|
# @param patch [String] Patch to check for (example: SA-CORE-2019-003)
|
|
# @return [Boolean, nil] Whether or not the patch was found or unknown
|
|
def drupal_patch(changelog, patch)
|
|
return unless changelog && patch
|
|
|
|
# HACK: Patch level removed since undetermined 8.x release
|
|
if changelog.include?('For a full list of fixes in the latest release')
|
|
return nil
|
|
elsif changelog.include?(patch)
|
|
return true
|
|
end
|
|
|
|
false
|
|
end
|
|
|
|
# Match a Drupal version
|
|
#
|
|
# @param string [String] String to match against
|
|
# @return [Gem::Version] Version as Gem::Version
|
|
def version_match(string)
|
|
return unless string
|
|
|
|
# Perl devs love me; Ruby devs hate me
|
|
string =~ /^Drupal ([\d.]+)/
|
|
|
|
return unless $1 && Gem::Version.correct?($1)
|
|
|
|
Gem::Version.new($1)
|
|
end
|
|
|
|
end
|
|
end
|