diff --git a/lib/environment.rb b/lib/environment.rb
index 76a669ec..f4e63b67 100644
--- a/lib/environment.rb
+++ b/lib/environment.rb
@@ -23,7 +23,7 @@ rescue LoadError => e
puts "[ERROR] #{e}"
if missing_gem = e.to_s[%r{ -- ([^\s]+)}, 1]
- puts "[TIP] Try to run 'gem install #{missing_gem}' or 'gem install --user-install #{missing_gem}'. If you still get an error, Please see README file or http://code.google.com/p/wpscan/"
+ puts "[TIP] Try to run 'gem install #{missing_gem}' or 'gem install --user-install #{missing_gem}'. If you still get an error, Please see README file or https://github.com/wpscanteam/wpscan"
end
exit(1)
end
diff --git a/lib/wpstools/generate_plugin_list.rb b/lib/wpstools/generate_plugin_list.rb
deleted file mode 100644
index a8ea0d1b..00000000
--- a/lib/wpstools/generate_plugin_list.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/usr/bin/env ruby
-
-#
-# WPScan - WordPress Security Scanner
-# Copyright (C) 2011 Ryan Dewhurst AKA ethicalhack3r
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-# ryandewhurst at gmail
-#
-
-# This tool generates a plugin list to use for plugin enumeration
-
-class Generate_Plugin_List
-
- attr_accessor :pages, :verbose
-
- def initialize(pages, verbose)
- @pages = pages.to_i
- @verbose = verbose
- @browser = Browser.instance
- @hydra = @browser.hydra
- end
-
- # Send a HTTP request to the WordPress most popular plugins webpage
- # parse the response for the plugin names.
-
- def parse_popular_plugins
-
- found_plugins = []
- page_count = 1
- queue_count = 0
-
- (1...@pages).each do |page|
-
- request = @browser.forge_request('http://wordpress.org/extend/plugins/browse/popular/page/'+page.to_s+'/')
-
- queue_count += 1
-
- request.on_complete do |response|
- puts "[+] Parsing page " + page_count.to_s if @verbose
- page_count += 1
- response.body.scan(%r{
}i).each do |plugin|
- found_plugins << plugin[0]
- end
- end
-
- @hydra.queue(request)
-
- if queue_count == @browser.max_threads
- @hydra.run
- queue_count = 0
- end
-
- end
-
- @hydra.run
-
- found_plugins.uniq
- end
-
- def parse_full_plugins
- found_plugins = []
- index = @browser.get('http://plugins.svn.wordpress.org/').body
- index.scan(%r{(.*)/}i).each do |plugin|
- found_plugins << plugin[0]
- end
- found_plugins.uniq
- end
-
- # Use the WordPress plugin SVN repo to find a
- # valid plugin file. This will cut down on
- # false positives. See issue 39.
-
- def parse_plugin_files(plugins)
-
- plugins_with_paths = ""
- queue_count = 0
-
- plugins.each do |plugin|
-
- request = @browser.forge_request('http://plugins.svn.wordpress.org/' + plugin + '/trunk/')
-
- request.on_complete do |response|
-
- puts "[+] Parsing plugin " + plugin + " [" + response.code.to_s + "]" if @verbose
- file = response.body[%r{.+}i, 1]
- if file
- # Only count Plugins with contents
- plugin += "/" + file
- plugins_with_paths << plugin + "\n"
- end
- end
-
- queue_count += 1
- @hydra.queue(request)
-
- # the wordpress server stops
- # responding if we dont use this.
- if queue_count == @browser.max_threads
- @hydra.run
- queue_count = 0
- end
-
- end
-
- @hydra.run
-
- plugins_with_paths
- end
-
- # Save the file
-
- def save_file(full=false)
- begin
- if (full)
- plugins = parse_full_plugins
- else
- plugins = parse_popular_plugins
- end
- puts "[*] We have parsed " + plugins.size.to_s
- plugins_with_paths = parse_plugin_files(plugins)
- File.open(DATA_DIR + '/plugins.txt', 'w') { |f| f.write(plugins_with_paths) }
- puts "New data/plugin.txt file created with " + plugins_with_paths.scan(/\n/).size.to_s + " entries."
- rescue => e
- puts "ERROR: Something went wrong :( " + e.inspect
- end
- end
-
-end
diff --git a/lib/wpstools/parse_svn.rb b/lib/wpstools/parse_svn.rb
new file mode 100644
index 00000000..2e5d9027
--- /dev/null
+++ b/lib/wpstools/parse_svn.rb
@@ -0,0 +1,128 @@
+#!/usr/bin/env ruby
+
+#
+# WPScan - WordPress Security Scanner
+# Copyright (C) 2012
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# This Class Parses SVN Repositories via HTTP
+class Svn_Parser
+
+ attr_accessor :verbose, :svn_root, :keep_empty_dirs
+
+ def initialize(svn_root, verbose, keep_empty_dirs = false)
+ @svn_root = svn_root
+ @verbose = verbose
+ @keep_empty_dirs = keep_empty_dirs
+ @svn_browser = Browser.instance
+ @svn_hydra = @svn_browser.hydra
+ end
+
+ def parse(dirs = nil)
+ if dirs == nil
+ dirs = get_root_directories
+ end
+ urls = get_svn_project_urls(dirs)
+ entries = get_svn_file_entries(urls)
+ return entries
+ end
+
+ # Gets all directories in the SVN root
+ def get_root_directories
+ dirs = []
+ rootindex = @svn_browser.get(@svn_root).body
+ rootindex.scan(%r{(.*)/}i).each do |dir|
+ dirs << dir[0]
+ end
+ dirs.uniq
+ dirs.sort
+ return dirs
+ end
+
+ def get_svn_project_urls(dirs)
+ urls = []
+ queue_count = 0
+ # First get all trunk or version directories
+ dirs.each do |dir|
+ svnurl = @svn_root + dir + "/"
+ request = @svn_browser.forge_request(svnurl)
+ request.on_complete do |response|
+ # trunk folder present
+ if contains_trunk(response)
+ puts "[+] Adding trunk on #{dir}" if @verbose
+ urls << (svnurl << "trunk/")
+ # no trunk folder. This is true on theme svn repos
+ else
+ folders = response.body.scan(%r{^\s*.*/$}i)
+ if folders != nil and folders.length > 0
+ last_version = folders.last[0]
+ puts "[+] Adding #{last_version} on #{dir}" if @verbose
+ urls << (svnurl + last_version + "/")
+ else
+ puts "[+] No content in #{dir}" if @verbose
+ end
+ end
+ end
+ queue_count += 1
+ @svn_hydra.queue(request)
+ # the wordpress server stops
+ # responding if we dont use this.
+ if queue_count == @svn_browser.max_threads
+ @svn_hydra.run
+ queue_count = 0
+ end
+ end
+ @svn_hydra.run
+ return urls
+ end
+
+ # Get a file in each directory
+ def get_svn_file_entries(urls)
+ entries = []
+ queue_count = 0
+ urls.each do |url|
+ request = @svn_browser.forge_request(url)
+ request.on_complete do |response|
+ puts "[+] Parsing url #{url} [#{response.code.to_s}]" if @verbose
+ file = response.body[%r{.*}i, 1]
+ # TODO: recursive parsing of subdirectories if there is no file in the root directory
+ if file
+ url += "/" + file
+ entries << url
+ elsif @keep_empty_dirs
+ entries << url
+ end
+ end
+ queue_count += 1
+ @svn_hydra.queue(request)
+ # the wordpress server stops
+ # responding if we dont use this.
+ if queue_count == @svn_browser.max_threads
+ @svn_hydra.run
+ queue_count = 0
+ end
+ end
+ @svn_hydra.run
+ return entries
+ end
+
+ def contains_trunk(body)
+ contains = false
+ if !!(body =~ %r[trunk/]i)
+ contains = true
+ end
+ return contains
+ end
+end
diff --git a/lib/wpstools/wpstools_helper.rb b/lib/wpstools/wpstools_helper.rb
index 373145f8..68823652 100644
--- a/lib/wpstools/wpstools_helper.rb
+++ b/lib/wpstools/wpstools_helper.rb
@@ -15,6 +15,12 @@ def usage()
puts "- Generate a new full plugin list"
puts "ruby " + script_name + " --generate_full_plugin_list"
puts
+ puts "- Generate a new 'most popular' theme list, up to 150 pages ..."
+ puts "ruby " + script_name + " --generate_theme_list 150"
+ puts
+ puts "- Generate a new full theme list"
+ puts "ruby " + script_name + " --generate_full_theme_list"
+ puts
puts "See README for further information."
puts
end
@@ -29,5 +35,10 @@ def help()
puts "--gpl Alias for --generate_plugin_list"
puts "--generate_full_plugin_list Generate a new full data/plugins.txt file"
puts "--gfpl Alias for --generate_full_plugin_list"
+
+ puts "--generate_theme_list [number of pages] Generate a new data/themes.txt file. (supply number of *pages* to parse, default : 150)"
+ puts "--gtl Alias for --generate_theme_list"
+ puts "--generate_full_theme_list Generate a new full data/themes.txt file"
+ puts "--gftl Alias for --generate_full_theme_list"
puts
end
diff --git a/wpscan.rb b/wpscan.rb
index 728ecb37..672601cc 100755
--- a/wpscan.rb
+++ b/wpscan.rb
@@ -301,6 +301,7 @@ begin
puts '[+] Finished at ' + Time.now.asctime
exit() # must exit!
rescue => e
- puts "[ERROR] #{e}"
- puts "Trace : #{e.backtrace}"
+ puts "[ERROR] #{e.message}"
+ puts "Trace :"
+ puts e.backtrace.join("\n")
end
diff --git a/wpstools.rb b/wpstools.rb
index 2ac22c3b..f610691f 100755
--- a/wpstools.rb
+++ b/wpstools.rb
@@ -38,8 +38,12 @@ begin
["--verbose", "-v", GetoptLong::NO_ARGUMENT],
["--generate_plugin_list", GetoptLong::OPTIONAL_ARGUMENT],
["--generate_full_plugin_list", GetoptLong::NO_ARGUMENT],
+ ["--generate_theme_list", GetoptLong::OPTIONAL_ARGUMENT],
+ ["--generate_full_theme_list", GetoptLong::NO_ARGUMENT],
["--gpl", GetoptLong::OPTIONAL_ARGUMENT],
["--gfpl", GetoptLong::OPTIONAL_ARGUMENT],
+ ["--gtl", GetoptLong::OPTIONAL_ARGUMENT],
+ ["--gftl", GetoptLong::OPTIONAL_ARGUMENT],
["--update", "-u", GetoptLong::NO_ARGUMENT]
)
@@ -59,23 +63,46 @@ begin
end
@generate_plugin_list = true
+ when "--generate_theme_list", "--gtl"
+ if argument == ''
+ puts "Number of pages not supplied, defaulting to 150 pages ..."
+ @number_of_pages = 150
+ else
+ @number_of_pages = argument.to_i
+ end
+
+ @generate_theme_list = true
when "--update"
@update = true
when "--generate_full_plugin_list", "--gfpl"
@generate_full_plugin_list = true
+ when "--generate_full_theme_list", "--gftl"
+ @generate_full_theme_list = true
end
end
if @generate_plugin_list
puts "[+] Generating new most popular plugin list"
puts
- Generate_Plugin_List.new(@number_of_pages, @verbose).save_file(false)
+ Generate_List.new('plugins', @verbose).generate_popular_list(@number_of_pages)
end
if @generate_full_plugin_list
puts "[+] Generating new full plugin list"
puts
- Generate_Plugin_List.new(-1, @verbose).save_file(true)
+ Generate_List.new('plugins', @verbose).generate_full_list
+ end
+
+ if @generate_theme_list
+ puts "[+] Generating new most popular theme list"
+ puts
+ Generate_List.new('themes', @verbose).generate_popular_list(@number_of_pages)
+ end
+
+ if @generate_full_theme_list
+ puts "[+] Generating new full theme list"
+ puts
+ Generate_List.new('themes', @verbose).generate_full_list
end
if @update
@@ -88,6 +115,7 @@ begin
end
rescue => e
- puts "[ERROR] #{e}"
- puts "Trace : #{e.backtrace}"
+ puts "[ERROR] #{e.message}"
+ puts "Trace :"
+ puts e.backtrace.join("\n")
end