# class Object - RDoc Documentation

# class Object

### Constants

CACHE_DIR
COLLECTIONS_LIB_DIR
COMMON_LIB_DIR
COMMON_PLUGINS_DIR

  Plugins directories

CONF_DIR
DATA_DIR
LIB_DIR
LOCAL_FILES_FILE
LOCAL_FILES_XSD
LOG_FILE
MODELS_LIB_DIR
PLUGINS_FILE

  Data files

PLUGINS_FULL_FILE
PLUGINS_VULNS_FILE
REVISION
ROOT_DIR
THEMES_FILE
THEMES_FULL_FILE
THEMES_VULNS_FILE
UPDATER_LIB_DIR
VULNS_XSD
WPSCAN_LIB_DIR
WPSCAN_PLUGINS_DIR
WPSCAN_VERSION
WPSTOOLS_LIB_DIR
WPSTOOLS_PLUGINS_DIR
WP_VERSIONS_FILE
WP_VERSIONS_XSD
WP_VULNS_FILE

### Public Instance Methods

add_http_protocol(url) click to toggle source

Add protocol

    # File lib/common/common_helper.rb, line 58
    def add_http_protocol(url)
      url =~ /^https?:/ ? url : "http://#{url}"
    end

add_trailing_slash(url) click to toggle source

    # File lib/common/common_helper.rb, line 62
    def add_trailing_slash(url)
      url =~ /\/$/ ? url : "#{url}/"
    end

banner() click to toggle source

our 1337 banner

    # File lib/common/common_helper.rb, line 77
    def banner
      puts '____________________________________________________'
      puts ' __          _______   _____                  '
      puts ' \ \        / /  __ \ / ____|                 '
      puts '  \ \  /\  / /| |__) | (___   ___  __ _ _ __  '
      puts '   \ \/  \/ / |  ___/ \___ \ / __|/ _` | \_ \ '
      puts '    \  /\  /  | |     ____) | (__| (_| | | | |'
      puts "     \\/  \\/   |_|    |_____/ \\___|\\__,_|_| |_| v#{WPSCAN_VERSION}r#{REVISION}"
      puts
      puts '    WordPress Security Scanner by the WPScan Team'
      puts ' Sponsored by the RandomStorm Open Source Initiative'
      puts '_____________________________________________________'
      puts
    end

colorize(text, color_code) click to toggle source

    # File lib/common/common_helper.rb, line 92
    def colorize(text, color_code)
      "\e[#{color_code}m#{text}\e[0m"
    end

green(text) click to toggle source

    # File lib/common/common_helper.rb, line 100
    def green(text)
      colorize(text, 32)
    end

help() click to toggle source

command help

    # File lib/wpscan/wpscan_helper.rb, line 56
    def help
      puts 'Help :'
      puts
      puts 'Some values are settable in conf/browser.conf.json :'
      puts '  user-agent, proxy, proxy-auth, threads, cache timeout and request timeout'
      puts
      puts '--update   Update to the latest revision'
      puts '--url   | -u <target url>  The WordPress URL/domain to scan.'
      puts '--force | -f Forces WPScan to not check if the remote site is running WordPress.'
      puts '--enumerate | -e [option(s)]  Enumeration.'
      puts '  option :'
      puts '    u        usernames from id 1 to 10'
      puts '    u[10-20] usernames from id 10 to 20 (you must write [] chars)'
      puts '    p        plugins'
      puts '    vp       only vulnerable plugins'
      puts '    ap       all plugins (can take a long time)'
      puts '    tt       timthumbs'
      puts '    t        themes'
      puts '    vt       only vulnerable themes'
      puts '    at       all themes (can take a long time)'
      puts '  Multiple values are allowed : "-e t,p" will enumerate timthumbs and plugins'
      puts '  If no option is supplied, the default is "vt,tt,u,vp"'
      puts
      puts '--exclude-content-based "<regexp or string>" Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied'
      puts '                                             You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)'
      puts '--config-file | -c <config file> Use the specified config file'
      puts '--follow-redirection  If the target url has a redirection, it will be followed without asking if you wanted to do so or not'
      puts '--wp-content-dir <wp content dir>  WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specified it. Subdirectories are allowed'
      puts '--wp-plugins-dir <wp plugins dir>  Same thing than --wp-content-dir but for the plugins directory. If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed'
      puts '--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).'
      puts '                                 HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used'
      puts '--proxy-auth <username:password>  Supply the proxy login credentials (will override the one from conf/browser.conf.json).'
      puts '--basic-auth <username:password>  Set the HTTP Basic authentication'
      puts '--wordlist | -w <wordlist>  Supply a wordlist for the password bruter and do the brute.'
      puts '--threads  | -t <number of threads>  The number of threads to use when multi-threading requests. (will override the value from conf/browser.conf.json)'
      puts '--username | -U <username>  Only brute force the supplied username.'
      puts '--help     | -h This help screen.'
      puts '--verbose  | -v Verbose output.'
      puts
    end

main() click to toggle source

    # File wpscan.rb, line 6
    def main
      # delete old logfile, check if it is a symlink first.
      File.delete(LOG_FILE) if File.exist?(LOG_FILE) and !File.symlink?(LOG_FILE)

      banner()

      begin
        wpscan_options = WpscanOptions.load_from_arguments

        unless wpscan_options.has_options?
          usage()
          raise('No argument supplied')
        end

        if wpscan_options.help
          help()
          usage()
          exit(0)
        end

        # Check for updates
        if wpscan_options.update
          if !@updater.nil?
            if @updater.has_local_changes?
              puts "#{red('[!]')} Local file changes detected, an update will override local changes, do you want to continue updating? [y/n]"
              Readline.readline =~ /^y/ ? @updater.reset_head : raise('Update aborted')
            end
            puts @updater.update()
          else
            puts 'Svn / Git not installed, or wpscan has not been installed with one of them.'
            puts 'Update aborted'
          end
          exit(0)
        end

        wp_target = WpTarget.new(wpscan_options.url, wpscan_options.to_h)

        # Remote website up?
        unless wp_target.online?
          raise "The WordPress URL supplied '#{wp_target.uri}' seems to be down."
        end

        if wpscan_options.proxy
          proxy_response = Browser.get(wp_target.url)

          unless WpTarget::valid_response_codes.include?(proxy_response.code)
            raise "Proxy Error :\r\n#{proxy_response.headers}"
          end
        end

        redirection = wp_target.redirection
        if redirection
          if wpscan_options.follow_redirection
            puts "Following redirection #{redirection}"
            puts
          else
            puts "The remote host tried to redirect us to #{redirection}"
            puts 'Do you want follow the redirection ? [y/n]'
          end

          if wpscan_options.follow_redirection or Readline.readline =~ /^y/
            wpscan_options.url = redirection
            wp_target = WpTarget.new(redirection, wpscan_options.to_h)
          else
            puts 'Scan aborted'
            exit(0)
          end
        end

        if wp_target.has_basic_auth? && wpscan_options.basic_auth.nil?
          raise 'Basic authentication is required, please provide it with --basic-auth <login:password>'
        end

        # Remote website is wordpress?
        unless wpscan_options.force
          unless wp_target.wordpress?
            raise 'The remote website is up, but does not seem to be running WordPress.'
          end
        end

        unless wp_target.wp_content_dir
          raise 'The wp_content_dir has not been found, please supply it with --wp-content-dir'
        end

        unless wp_target.wp_plugins_dir_exists?
          puts "The plugins directory '#{wp_target.wp_plugins_dir}' does not exist."
          puts 'You can specify one per command line option (don\t forget to include the wp-content directory if needed)'
          puts 'Continue? [y/n]'
          unless Readline.readline =~ /^y/
            exit(0)
          end
        end

        # Output runtime data
        start_time = Time.now
        puts "| URL: #{wp_target.url}"
        puts "| Started on #{start_time.asctime}"
        puts

        if wp_target.has_robots?
          puts green('[+]') + " robots.txt available under '#{wp_target.robots_url}'"
        end

        if wp_target.has_readme?
          puts red('[!]') + " The WordPress '#{wp_target.readme_url}' file exists"
        end

        if wp_target.has_full_path_disclosure?
          puts red('[!]') + " Full Path Disclosure (FPD) in '#{wp_target.full_path_disclosure_url}'"
        end

        if wp_target.has_debug_log?
          puts red('[!]') + " Debug log file found : #{wp_target.debug_log_url}"
        end

        wp_target.config_backup.each do |file_url|
          puts red("[!] A wp-config.php backup file has been found '#{file_url}'")
        end

        if wp_target.search_replace_db_2_exists?
          puts red("[!] searchreplacedb2.php has been found '#{wp_target.search_replace_db_2_url}'")
        end

        if wp_target.multisite?
          puts green('[+]') + ' This site seems to be a multisite (http://codex.wordpress.org/Glossary#Multisite)'
        end

        if wp_target.registration_enabled?
          puts green('[+]') + ' User registration is enabled'
        end

        if wp_target.has_xml_rpc?
          puts green('[+]') + " XML-RPC Interface available under #{wp_target.xml_rpc_url}"
        end

        if wp_target.has_malwares?
          malwares = wp_target.malwares
          puts red('[!]') + " #{malwares.size} malware(s) found :"

          malwares.each do |malware_url|
            puts
            puts ' | ' + red("#{malware_url}")
          end
          puts
        end

        enum_options = {
          show_progression: true,
          exclude_content:  wpscan_options.exclude_content_based
        }

        if wp_version = wp_target.version(WP_VERSIONS_FILE)
          wp_version.output
        end

        if wp_theme = wp_target.theme
          puts
          # Theme version is handled in #to_s
          puts green('[+]') + " The WordPress theme in use is #{wp_theme}"
          wp_theme.output
        end

        if wpscan_options.enumerate_plugins == nil and wpscan_options.enumerate_only_vulnerable_plugins == nil
          puts
          puts green('[+]') + ' Enumerating plugins from passive detection ... '

          wp_plugins = WpPlugins.passive_detection(wp_target)
          if !wp_plugins.empty?
            puts "#{wp_plugins.size} plugins found :"

            wp_plugins.output
          else
            puts 'No plugins found :('
          end
        end

        # Enumerate the installed plugins
        if wpscan_options.enumerate_plugins or wpscan_options.enumerate_only_vulnerable_plugins or wpscan_options.enumerate_all_plugins
          puts
          puts green('[+]') + " Enumerating installed plugins #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_plugins} ..."
          puts

          wp_plugins = WpPlugins.aggressive_detection(wp_target,
            enum_options.merge(
              file: wpscan_options.enumerate_all_plugins ? PLUGINS_FULL_FILE : PLUGINS_FILE,
              only_vulnerable: wpscan_options.enumerate_only_vulnerable_plugins || false
            )
          )
          puts
          if !wp_plugins.empty?
            puts green('[+]') + " We found #{wp_plugins.size} plugins:"

            wp_plugins.output
          else
            puts 'No plugins found :('
          end
        end

        # Enumerate installed themes
        if wpscan_options.enumerate_themes or wpscan_options.enumerate_only_vulnerable_themes or wpscan_options.enumerate_all_themes
          puts
          puts green('[+]') + " Enumerating installed themes #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_themes} ..."
          puts

          wp_themes = WpThemes.aggressive_detection(wp_target,
            enum_options.merge(
              file: wpscan_options.enumerate_all_themes ? THEMES_FULL_FILE : THEMES_FILE,
              only_vulnerable: wpscan_options.enumerate_only_vulnerable_themes || false
            )
          )
          puts
          if !wp_themes.empty?
            puts green('[+]') + " We found #{wp_themes.size} themes:"

            wp_themes.output
          else
            puts 'No themes found :('
          end
        end

        if wpscan_options.enumerate_timthumbs
          puts
          puts green('[+]') + ' Enumerating timthumb files ...'
          puts

          wp_timthumbs = WpTimthumbs.aggressive_detection(wp_target,
            enum_options.merge(
              file: DATA_DIR + '/timthumbs.txt',
              theme_name: wp_theme ? wp_theme.name : nil
            )
          )
          puts
          if !wp_timthumbs.empty?
            puts green('[+]') + " We found #{wp_timthumbs.size} timthumb file/s :"
            puts

            wp_timthumbs.output

            puts
            puts red(' * Reference: http://www.exploit-db.com/exploits/17602/')
          else
            puts 'No timthumb files found :('
          end
        end

        # If we haven't been supplied a username, enumerate them...
        if !wpscan_options.username and wpscan_options.wordlist or wpscan_options.enumerate_usernames
          puts
          puts green('[+]') + ' Enumerating usernames ...'

          wp_users = WpUsers.aggressive_detection(wp_target,
            enum_options.merge(
              range: wpscan_options.enumerate_usernames_range,
              show_progression: false
            )
          )
          puts
          if wp_users.empty?
            puts 'We did not enumerate any usernames :('
            puts 'Try supplying your own username with the --username option'
            puts
            exit(1)
          else
            puts green('[+]') + " We found the following #{wp_users.size} user/s :"

            wp_users.output(margin_left: ' ' * 4)
          end

        else
          # FIXME : Change the .username to .login (and also the --username in the CLI)
          wp_users = WpUsers.new << WpUser.new(wp_target.uri, login: wpscan_options.username)
        end

        # Start the brute forcer
        bruteforce = true
        if wpscan_options.wordlist
          if wp_target.has_login_protection?

            protection_plugin = wp_target.login_protection_plugin()

            puts
            puts "The plugin #{protection_plugin.name} has been detected. It might record the IP and timestamp of every failed login. Not a good idea for brute forcing !"
            puts '[?] Do you want to start the brute force anyway ? [y/n]'

            bruteforce = false if Readline.readline !~ /^y/
          end
          puts
          if bruteforce
            puts green('[+]') + ' Starting the password brute forcer'

            wp_users.brute_force(wpscan_options.wordlist,
                                 show_progression: true,
                                 verbose: wpscan_options.verbose)
            puts
            wp_users.output(show_password: true, margin_left: ' ' * 2)
          else
            puts 'Brute forcing aborted'
          end
        end

        stop_time = Time.now
        puts
        puts green("[+] Finished at #{stop_time.asctime}")
        elapsed = stop_time - start_time
        puts green("[+] Elapsed time: #{Time.at(elapsed).utc.strftime('%H:%M:%S')}")
        exit(0) # must exit!
      rescue => e
        if e.backtrace[0] =~ /main/
          puts red(e.message)
        else
          puts red("[ERROR] #{e.message}")
          puts red('Trace :')
          puts red(e.backtrace.join("\n"))
        end
        exit(1)
      end
    end

puts(o = '') click to toggle source

Override for puts to enable logging

Calls superclass method

    # File lib/common/hacks.rb, line 64
    def puts(o = '')
      # remove color for logging
      if o.respond_to?(:gsub)
        temp = o.gsub(/\e\[\d+m(.*)?\e\[0m/, '\1')
        File.open(LOG_FILE, 'a+') { |f| f.puts(temp) }
      end
      super(o)
    end

red(text) click to toggle source

    # File lib/common/common_helper.rb, line 96
    def red(text)
      colorize(text, 31)
    end

redefine_constant(constant, value) click to toggle source

    # File lib/common/common_helper.rb, line 110
    def redefine_constant(constant, value)
      Object.send(:remove_const, constant)
      Object.const_set(constant, value)
    end

require_files_from_directory(absolute_dir_path, files_pattern = '*.rb') click to toggle source

TODO : add an exclude pattern ?

    # File lib/common/common_helper.rb, line 44
    def require_files_from_directory(absolute_dir_path, files_pattern = '*.rb')
      files = Dir[File.join(absolute_dir_path, files_pattern)]

      # Files in the root dir are loaded first, then thoses in the subdirectories
      files.sort_by { |file| [file.count("/"), file] }.each do |f|
        f = File.expand_path(f)
        #puts "require #{f}" # Used for debug
        require f
      end
    end

usage() click to toggle source

wpscan usage

    # File lib/wpscan/wpscan_helper.rb, line 7
    def usage
      script_name = $0
      puts
      puts 'Examples :'
      puts
      puts '-Further help ...'
      puts "ruby #{script_name} --help"
      puts
      puts "-Do 'non-intrusive' checks ..."
      puts "ruby #{script_name} --url www.example.com"
      puts
      puts '-Do wordlist password brute force on enumerated users using 50 threads ...'
      puts "ruby #{script_name} --url www.example.com --wordlist darkc0de.lst --threads 50"
      puts
      puts "-Do wordlist password brute force on the 'admin' username only ..."
      puts "ruby #{script_name} --url www.example.com --wordlist darkc0de.lst --username admin"
      puts
      puts '-Enumerate installed plugins ...'
      puts "ruby #{script_name} --url www.example.com --enumerate p"
      puts
      puts '-Enumerate installed themes ...'
      puts "ruby #{script_name} --url www.example.com --enumerate t"
      puts
      puts '-Enumerate users ...'
      puts "ruby #{script_name} --url www.example.com --enumerate u"
      puts
      puts '-Enumerate installed timthumbs ...'
      puts "ruby #{script_name} --url www.example.com --enumerate tt"
      puts
      puts '-Use a HTTP proxy ...'
      puts "ruby #{script_name} --url www.example.com --proxy 127.0.0.1:8118"
      puts
      puts '-Use a SOCKS5 proxy ... (cURL >= v7.21.7 needed)'
      puts "ruby #{script_name} --url www.example.com --proxy socks5://127.0.0.1:9000"
      puts
      puts '-Use custom content directory ...'
      puts "ruby #{script_name} -u www.example.com --wp-content-dir custom-content"
      puts
      puts '-Use custom plugins directory ...'
      puts "ruby #{script_name} -u www.example.com --wp-plugins-dir wp-content/custom-plugins"
      puts
      puts '-Update ...'
      puts "ruby #{script_name} --update"
      puts
      puts 'See README for further information.'
      puts
    end

xml(file) click to toggle source

    # File lib/common/common_helper.rb, line 104
    def xml(file)
      Nokogiri::XML(File.open(file)) do |config|
        config.noblanks
      end
    end