WPSTools plugins mode activated
This commit is contained in:
131
lib/wpstools/plugins/list_generator/generate_list.rb
Normal file
131
lib/wpstools/plugins/list_generator/generate_list.rb
Normal file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
#
|
||||
# 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 GenerateList
|
||||
|
||||
attr_accessor :verbose
|
||||
|
||||
# type = themes | plugins
|
||||
def initialize(type, verbose)
|
||||
if type =~ /plugins/i
|
||||
@type = "plugin"
|
||||
@svn_url = "http://plugins.svn.wordpress.org/"
|
||||
@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/"
|
||||
@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 set_file_name(type)
|
||||
case @type
|
||||
when "plugin"
|
||||
case type
|
||||
when :full
|
||||
@file_name = DATA_DIR + "/plugins_full.txt"
|
||||
when :popular
|
||||
@file_name = DATA_DIR + "/plugins.txt"
|
||||
else
|
||||
raise "Unknown type"
|
||||
end
|
||||
when "theme"
|
||||
case type
|
||||
when :full
|
||||
@file_name = DATA_DIR + "/themes_full.txt"
|
||||
when :popular
|
||||
@file_name = DATA_DIR + "/themes.txt"
|
||||
else
|
||||
raise "Unknown type"
|
||||
end
|
||||
else
|
||||
raise "Unknown type #@type"
|
||||
end
|
||||
end
|
||||
|
||||
def generate_full_list
|
||||
set_file_name(:full)
|
||||
items = SvnParser.new(@svn_url, @verbose).parse
|
||||
save items
|
||||
end
|
||||
|
||||
def generate_popular_list(pages)
|
||||
set_file_name(:popular)
|
||||
popular = get_popular_items(pages)
|
||||
items = SvnParser.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
|
||||
end
|
||||
|
||||
# Save the file
|
||||
def save(items)
|
||||
items.sort!
|
||||
items.uniq!
|
||||
puts "[*] We have parsed #{items.length} #@types"
|
||||
File.open(@file_name, 'w') { |f| f.puts(items) }
|
||||
puts "New #@file_name file created"
|
||||
end
|
||||
|
||||
end
|
||||
69
lib/wpstools/plugins/list_generator/list_generator_plugin.rb
Normal file
69
lib/wpstools/plugins/list_generator/list_generator_plugin.rb
Normal file
@@ -0,0 +1,69 @@
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
#
|
||||
# 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/>.
|
||||
#++
|
||||
|
||||
class ListGeneratorPlugin < Plugin
|
||||
|
||||
def initialize
|
||||
super(
|
||||
:author => "WPScanTeam - @FireFart"
|
||||
)
|
||||
|
||||
register_options(
|
||||
["--generate-plugin-list [NUMBER_OF_PAGES]", "--gpl", Integer, "Generate a new data/plugins.txt file. (supply number of *pages* to parse, default : 150)"],
|
||||
["--generate-full-plugin-list", "--gfpl", "Generate a new full data/plugins.txt file"],
|
||||
|
||||
["--generate-theme-list [NUMBER_OF_PAGES]", "--gtl", Integer, "Generate a new data/themes.txt file. (supply number of *pages* to parse, default : 150)"],
|
||||
["--generate-full-theme-list", "--gftl", "Generate a new full data/themes.txt file"],
|
||||
|
||||
["--generate-all", "--ga", "Generate a new full plugins, full themes, popular plugins and popular themes list"],
|
||||
)
|
||||
end
|
||||
|
||||
def run(options = {})
|
||||
verbose = options[:verbose] || false
|
||||
generate_all = options[:generate_all] || false
|
||||
|
||||
if options.has_key?(:generate_plugin_list) || generate_all
|
||||
number_of_pages = options[:generate_plugin_list] || 150
|
||||
|
||||
puts "[+] Generating new most popular plugin list"
|
||||
puts
|
||||
GenerateList.new('plugins', verbose).generate_popular_list(number_of_pages)
|
||||
end
|
||||
|
||||
if options[:generate_full_plugin_list] || generate_all
|
||||
puts "[+] Generating new full plugin list"
|
||||
puts
|
||||
GenerateList.new('plugins', verbose).generate_full_list
|
||||
end
|
||||
|
||||
if options.has_key?(:generate_theme_list) || generate_all
|
||||
number_of_pages = options[:generate_theme_list] || 150
|
||||
|
||||
puts "[+] Generating new most popular theme list"
|
||||
puts
|
||||
GenerateList.new('themes', verbose).generate_popular_list(number_of_pages)
|
||||
end
|
||||
|
||||
if options[:generate_full_theme_list] || generate_all
|
||||
puts "[+] Generating new full theme list"
|
||||
puts
|
||||
GenerateList.new('themes', verbose).generate_full_list
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
135
lib/wpstools/plugins/list_generator/svn_parser.rb
Normal file
135
lib/wpstools/plugins/list_generator/svn_parser.rb
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
#
|
||||
# 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 SvnParser
|
||||
|
||||
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)
|
||||
get_svn_file_entries(urls)
|
||||
end
|
||||
|
||||
#Private methods start here
|
||||
private
|
||||
|
||||
# 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
|
||||
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(URI.encode(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
|
||||
urls
|
||||
end
|
||||
|
||||
# Get a file in each directory
|
||||
# TODO: exclude files like Thumbs.db (Example: wordpress-23-related-posts-plugin/)
|
||||
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(URI.encode(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
|
||||
entries
|
||||
end
|
||||
|
||||
def contains_trunk(body)
|
||||
contains = false
|
||||
if !!(body =~ %r[<li><a href="trunk/">trunk/</a></li>]i)
|
||||
contains = true
|
||||
end
|
||||
contains
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user