# -*- 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 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 # 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