Merge pull request #27 from FireFart/themes

Generate Theme List
This commit is contained in:
erwanlr
2012-09-13 06:15:21 -07:00
13 changed files with 23599 additions and 15724 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1475
data/themes.txt Normal file

File diff suppressed because it is too large Load Diff

6207
data/themes_full.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -103,7 +103,7 @@ module WpPlugins
targets_url.flatten!
targets_url.uniq!
# randomize the plugins array to *maybe* help in some crappy IDS/IPS/WAF detection
targets_url.sort_by { rand }
targets_url.sort_by! { rand }
end
# http://code.google.com/p/wpscan/issues/detail?id=42

View File

@@ -82,7 +82,7 @@ module WpTimthumbs
targets.uniq!
# randomize the array to *maybe* help in some crappy IDS/IPS/WAF evasion
targets.sort_by { rand }
targets.sort_by! { rand }
end
def self.timthumbs_file(timthumbs_file_path = nil)

View File

@@ -0,0 +1,107 @@
#!/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 <http://www.gnu.org/licenses/>.
# This tool generates a list to use for plugin and theme enumeration
class Generate_List
attr_accessor :verbose
# type = themes | plugins
def initialize(type, verbose)
if type =~ /plugins/i
@type = "plugin"
@svn_url = 'http://plugins.svn.wordpress.org/'
@file_name = DATA_DIR + '/plugins.txt'
@popular_url = 'http://wordpress.org/extend/plugins/browse/popular/'
@popular_regex = %r{<h3><a href="http://wordpress.org/extend/plugins/(.+)/">.+</a></h3>}i
elsif type =~ /themes/i
@type = "theme"
@svn_url = 'http://themes.svn.wordpress.org/'
@file_name = DATA_DIR + '/themes.txt'
@popular_url = 'http://wordpress.org/extend/themes/browse/popular/'
@popular_regex = %r{<h3><a href="http://wordpress.org/extend/themes/(.+)">.+</a></h3>}i
else
raise "Type #{type} not defined"
end
@verbose = verbose
@browser = Browser.instance
@hydra = @browser.hydra
end
def generate_full_list
items = Svn_Parser.new(@svn_url, @verbose).parse
save items
end
def generate_popular_list(pages)
popular = get_popular_items(pages)
items = Svn_Parser.new(@svn_url, @verbose).parse(popular)
save items
end
# Send a HTTP request to the WordPress most popular theme or plugin webpage
# parse the response for the names.
def get_popular_items(pages)
found_items = []
page_count = 1
queue_count = 0
(1...(pages.to_i+1)).each do |page|
# First page has another URL
url = (page == 1) ? @popular_url : @popular_url + 'page/' + page.to_s + '/'
request = @browser.forge_request(url)
queue_count += 1
request.on_complete do |response|
puts "[+] Parsing page " + page_count.to_s if @verbose
page_count += 1
response.body.scan(@popular_regex).each do |item|
puts "[+] Found popular #{@type}: #{item}" if @verbose
found_items << item[0]
end
end
@hydra.queue(request)
if queue_count == @browser.max_threads
@hydra.run
queue_count = 0
end
end
@hydra.run
found_items.sort!
found_items.uniq!
return found_items
end
# Save the file
def save(items)
items.sort!
items.uniq!
puts "[*] We have parsed #{items.length} #{@type}s"
File.open(@file_name, 'w') { |f| f.puts(items) }
puts "New #{@file_name} file created"
end
end

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
# 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{<h3><a href="http://wordpress.org/extend/plugins/(.*)/">.+</a></h3>}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{<li><a href=".*">(.*)/</a></li>}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{<li><a href="(\d*?[a-zA-Z].*\..*)">.+</a></li>}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

132
lib/wpstools/parse_svn.rb Normal file
View File

@@ -0,0 +1,132 @@
#!/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 <http://www.gnu.org/licenses/>.
# 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{<li><a href=".+">(.+)/</a></li>}i).each do |dir|
dirs << dir[0]
end
dirs.sort!
dirs.uniq!
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 << { :name => dir, :folder => "trunk"}
# no trunk folder. This is true on theme svn repos
else
folders = response.body.scan(%r{^\s*<li><a href="(.+)/">.+/</a></li>$}i)
if folders != nil and folders.length > 0
last_version = folders.last[0]
puts "[+] Adding #{last_version} on #{dir}" if @verbose
urls << { :name => dir, :folder => 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(dirs)
entries = []
queue_count = 0
dirs.each do |dir|
url = @svn_root + dir[:name] + "/" + dir[:folder] + "/"
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{<li><a href="(.+\.[^/]+)">.+</a></li>}i, 1]
# TODO: recursive parsing of subdirectories if there is no file in the root directory
path = dir[:name] + "/"
if file
path += file
entries << path
puts "[+] Added #{path}" if @verbose
elsif @keep_empty_dirs
entries << path
puts "[+] Added #{path}" if @verbose
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[<li><a href="trunk/">trunk/</a></li>]i)
contains = true
end
return contains
end
end

View File

@@ -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

View File

@@ -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

View File

@@ -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