wpscan 3.0.5 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9fd4bc758b868aa36848d44911b54335efdaefc
4
- data.tar.gz: e02f0a0f89271384cd00a1d9ab760205c5bd1837
3
+ metadata.gz: 798d7f0db156c00bfee19eb7f540ffd6a1884ef1
4
+ data.tar.gz: 89eba229e55e42aa5629e76a8f6ca38e6e1a36a8
5
5
  SHA512:
6
- metadata.gz: 955e80f693240687635f34d3b7081a302c452511e6d4191624edae196b7395f08759206b9aefc56f95449eccd5bdb9b91e6e48b613d9a27c681e1370759ed6bd
7
- data.tar.gz: 9944fa88fba74980f74df6225dd47a2cf2d419eb02da52cf844f296ab729b58a3c7d747d623614bd950832a680352d5fbd9de0c3d0eb0e8dddae3bf46c17a57f
6
+ metadata.gz: 8af7943d9c8ed54283853d0c9a89d397847d66262f53888c820f177cbc3d86642b6eba9131955cc3fe4406632a38c4fd61b765ac25cee578ae239a637f70a4ea
7
+ data.tar.gz: 2d356dcfe246a074bc47fd24a4e8bb7b52e253d47a6a0c491b6ff7a6912d283fabb0db7a495c0a1ab03ca7011699c09031e4664b02e6a0811a50732f63b4702e
@@ -10,11 +10,7 @@ module WPScan
10
10
  'If no --username/s option supplied, user enumeration will be run'],
11
11
  exists: true
12
12
  ),
13
- OptString.new(['--username USERNAME', '-u', 'The username to brute force']),
14
- OptFilePath.new(
15
- ['--usernames FILE-PATH', '-U', 'List of usernames to use during the brute forcing'],
16
- exists: true
17
- )
13
+ OptSmartList.new(['--usernames LIST', '-U', 'List of usernames to use during the brute forcing'])
18
14
  ]
19
15
  end
20
16
 
@@ -36,14 +32,10 @@ module WPScan
36
32
 
37
33
  # @return [ Array<Users> ] The users to brute force
38
34
  def users
39
- return target.users unless parsed_options[:usernames] || parsed_options[:username]
35
+ return target.users unless parsed_options[:usernames]
40
36
 
41
- if parsed_options[:username]
42
- [User.new(parsed_options[:username])]
43
- else
44
- File.open(parsed_options[:usernames]).reduce([]) do |acc, elem|
45
- acc << User.new(elem.chomp)
46
- end
37
+ parsed_options[:usernames].reduce([]) do |acc, elem|
38
+ acc << User.new(elem.chomp)
47
39
  end
48
40
  end
49
41
 
@@ -89,7 +81,7 @@ module WPScan
89
81
  def progress_bar(size, username)
90
82
  ProgressBar.create(
91
83
  format: '%t %a <%B> (%c / %C) %P%% %e',
92
- title: "Brute Forcing #{username}",
84
+ title: "Brute Forcing #{username} -",
93
85
  total: size
94
86
  )
95
87
  end
@@ -52,8 +52,6 @@ module WPScan
52
52
 
53
53
  super(false) # disable banner output
54
54
 
55
- DB.init_db
56
-
57
55
  load_server_module
58
56
 
59
57
  check_wordpress_state
@@ -31,7 +31,7 @@ module WPScan
31
31
  def create_plugins_comments_finders(mod, config)
32
32
  mod.const_set(
33
33
  :Comments, Class.new(Finders::Finder::PluginVersion::Comments) do
34
- const_set(:PATTERN, Regexp.new(config['pattern'], Regexp::IGNORECASE))
34
+ const_set(:PATTERN, config['pattern'])
35
35
  end
36
36
  )
37
37
  end
@@ -42,7 +42,7 @@ module WPScan
42
42
  # @return [ Array<OptParseValidator::OptBase> ]
43
43
  def cli_plugins_opts
44
44
  [
45
- OptFilePath.new(['--plugins-list FILE-PATH', 'List of plugins\' location to use'], exists: true),
45
+ OptSmartList.new(['--plugins-list LIST', 'List of plugins to enumerate']),
46
46
  OptChoice.new(
47
47
  ['--plugins-detection MODE',
48
48
  'Use the supplied mode to enumerate Plugins, instead of the global (--detection-mode) mode.'],
@@ -65,7 +65,7 @@ module WPScan
65
65
  # @return [ Array<OptParseValidator::OptBase> ]
66
66
  def cli_themes_opts
67
67
  [
68
- OptFilePath.new(['--themes-list FILE-PATH', 'List of themes\' location to use'], exists: true),
68
+ OptSmartList.new(['--themes-list LIST', 'List of themes to enumerate']),
69
69
  OptChoice.new(
70
70
  ['--themes-detection MODE',
71
71
  'Use the supplied mode to enumerate Themes, instead of the global (--detection-mode) mode.'],
@@ -129,10 +129,9 @@ module WPScan
129
129
  # @return [ Array<OptParseValidator::OptBase> ]
130
130
  def cli_users_opts
131
131
  [
132
- OptFilePath.new(
133
- ['--users-list FILE-PATH',
134
- 'List of users to check during the users enumeration from the Login Error Messages'],
135
- exists: true
132
+ OptSmartList.new(
133
+ ['--users-list LIST',
134
+ 'List of users to check during the users enumeration from the Login Error Messages']
136
135
  ),
137
136
  OptChoice.new(
138
137
  ['--users-detection MODE',
@@ -63,7 +63,7 @@ module WPScan
63
63
  # @return [ Array<String> ] The plugins list associated to the cli options
64
64
  def plugins_list_from_opts(opts)
65
65
  # List file provided by the user via the cli
66
- return File.open(opts[:plugins_list]).map(&:chomp) if opts[:plugins_list]
66
+ return opts[:plugins_list] if opts[:plugins_list]
67
67
 
68
68
  if opts[:enumerate][:all_plugins]
69
69
  DB::Plugins.all_slugs
@@ -101,7 +101,7 @@ module WPScan
101
101
  # @return [ Array<String> ] The themes list associated to the cli options
102
102
  def themes_list_from_opts(opts)
103
103
  # List file provided by the user via the cli
104
- return File.open(opts[:themes_list]).map(&:chomp) if opts[:themes_list]
104
+ return opts[:themes_list] if opts[:themes_list]
105
105
 
106
106
  if opts[:enumerate][:all_themes]
107
107
  DB::Themes.all_slugs
@@ -38,9 +38,7 @@ module WPScan
38
38
  # usernames from the potential Users found
39
39
  unames = opts[:found].map(&:username)
40
40
 
41
- if opts[:list]
42
- File.open(opts[:list]).each { |uname| unames << uname.chomp }
43
- end
41
+ [*opts[:list]].each { |uname| unames << uname.chomp }
44
42
 
45
43
  unames.uniq
46
44
  end
@@ -5,19 +5,9 @@ module WPScan
5
5
  class UniqueFingerprinting < CMSScanner::Finders::Finder
6
6
  include CMSScanner::Finders::Finder::Fingerprinter
7
7
 
8
- QUERY = 'SELECT md5_hash, path_id, version_id, ' \
9
- 'versions.number AS version,' \
10
- 'paths.value AS path ' \
11
- 'FROM fingerprints ' \
12
- 'LEFT JOIN versions ON version_id = versions.id ' \
13
- 'LEFT JOIN paths on path_id = paths.id ' \
14
- 'WHERE md5_hash IN ' \
15
- '(SELECT md5_hash FROM fingerprints GROUP BY md5_hash HAVING COUNT(*) = 1) ' \
16
- 'ORDER BY version DESC'.freeze
17
-
18
8
  # @return [ WpVersion ]
19
9
  def aggressive(opts = {})
20
- fingerprint(unique_fingerprints, opts) do |version_number, url, md5sum|
10
+ fingerprint(DB::Fingerprints.wp_unique_fingerprints, opts) do |version_number, url, md5sum|
21
11
  hydra.abort
22
12
  progress_bar.finish
23
13
 
@@ -31,30 +21,6 @@ module WPScan
31
21
  nil
32
22
  end
33
23
 
34
- # @return [ Hash ] The unique fingerprints across all versions in the DB
35
- #
36
- # Format returned:
37
- # {
38
- # file_path_1: {
39
- # md5_hash_1: version_1,
40
- # md5_hash_2: version_2
41
- # },
42
- # file_path_2: {
43
- # md5_hash_3: version_1,
44
- # md5_hash_4: version_3
45
- # }
46
- # }
47
- def unique_fingerprints
48
- fingerprints = {}
49
-
50
- repository(:default).adapter.select(QUERY).each do |f|
51
- fingerprints[f.path] ||= {}
52
- fingerprints[f.path][f.md5_hash] = f.version
53
- end
54
-
55
- fingerprints
56
- end
57
-
58
24
  def create_progress_bar(opts = {})
59
25
  super(opts.merge(title: 'Fingerprinting the version -'))
60
26
  end
@@ -6,8 +6,8 @@ module WPScan
6
6
  include CMSScanner::Target::Platform::PHP
7
7
  include CMSScanner::Target::Server::Generic
8
8
 
9
- READMES = %w[readme.txt README.txt Readme.txt ReadMe.txt README.TXT readme.TXT].freeze
10
- CHANGELOGS = %w[changelog.txt Changelog.txt ChangeLog.txt CHANGELOG.txt].freeze
9
+ READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
10
+ CHANGELOGS = %w[changelog.txt CHANGELOG.md changelog.md].freeze
11
11
 
12
12
  attr_reader :uri, :name, :detection_opts, :version_detection_opts, :target, :db_data
13
13
 
@@ -22,9 +22,15 @@ module WPScan
22
22
 
23
23
  @all_numbers = []
24
24
 
25
- DB::Version.all.each { |v| @all_numbers << v.number }
25
+ DB::Fingerprints.wp_fingerprints.each_value do |fp|
26
+ fp.each_value do |versions|
27
+ versions.each do |version|
28
+ @all_numbers << version unless @all_numbers.include?(version)
29
+ end
30
+ end
31
+ end
26
32
 
27
- @all_numbers
33
+ @all_numbers.sort! { |a, b| Gem::Version.new(b) <=> Gem::Version.new(a) }
28
34
  end
29
35
 
30
36
  # @return [ JSON ]
data/lib/wpscan.rb CHANGED
@@ -11,6 +11,7 @@ require 'uri'
11
11
  require 'time'
12
12
  require 'readline'
13
13
  require 'securerandom'
14
+
14
15
  # Custom Libs
15
16
  require 'wpscan/helper'
16
17
  require 'wpscan/db'
data/lib/wpscan/db.rb CHANGED
@@ -1,10 +1,4 @@
1
- require 'dm-core'
2
- require 'dm-migrations'
3
- require 'dm-constraints'
4
- require 'dm-sqlite-adapter'
5
-
6
1
  require 'wpscan/db/wp_item'
7
- require 'wpscan/db/schema'
8
2
  require 'wpscan/db/updater'
9
3
  require 'wpscan/db/wp_items'
10
4
  require 'wpscan/db/plugins'
@@ -12,17 +6,5 @@ require 'wpscan/db/themes'
12
6
  require 'wpscan/db/plugin'
13
7
  require 'wpscan/db/theme'
14
8
  require 'wpscan/db/wp_version'
9
+ require 'wpscan/db/fingerprints'
15
10
  require 'wpscan/db/dynamic_finders'
16
-
17
- module WPScan
18
- # DB
19
- module DB
20
- def self.init_db
21
- db_file ||= File.join(DB_DIR, 'wordpress.db')
22
-
23
- # DataMapper::Logger.new($stdout, :debug)
24
- DataMapper.setup(:default, "sqlite://#{db_file}")
25
- DataMapper.auto_upgrade!
26
- end
27
- end
28
- end
@@ -4,7 +4,7 @@ module WPScan
4
4
  class DynamicFinders
5
5
  # @return [ String ]
6
6
  def self.db_file
7
- @db_file ||= File.join(DB_DIR, 'dynamic_finders.yml')
7
+ @db_file ||= File.join(DB_DIR, 'dynamic_finders_01.yml')
8
8
  end
9
9
 
10
10
  # @return [ Hash ]
@@ -35,15 +35,7 @@ module WPScan
35
35
 
36
36
  # @return [ Hash ]
37
37
  def self.comments
38
- unless @comments
39
- @comments = finder_configs('Comments')
40
-
41
- @comments.each do |slug, config|
42
- @comments[slug]['pattern'] = Regexp.new(config['pattern'], Regexp::IGNORECASE)
43
- end
44
- end
45
-
46
- @comments
38
+ @comments ||= finder_configs('Comments')
47
39
  end
48
40
 
49
41
  # @return [ Hash ]
@@ -0,0 +1,50 @@
1
+ module WPScan
2
+ module DB
3
+ # Fingerprints class
4
+ class Fingerprints
5
+ # @param [ Hash ] data
6
+ #
7
+ # @return [ Hash ] the unique fingerprints in the data argument given
8
+ # Format returned:
9
+ # {
10
+ # file_path_1: {
11
+ # md5_hash_1: version_1,
12
+ # md5_hash_2: version_2
13
+ # },
14
+ # file_path_2: {
15
+ # md5_hash_3: version_1,
16
+ # md5_hash_4: version_3
17
+ # }
18
+ # }
19
+ def self.unique_fingerprints(data)
20
+ unique_fingerprints = {}
21
+
22
+ data.each do |file_path, fingerprints|
23
+ fingerprints.each do |md5sum, versions|
24
+ next unless versions.size == 1
25
+
26
+ unique_fingerprints[file_path] ||= {}
27
+ unique_fingerprints[file_path][md5sum] = versions.first
28
+ end
29
+ end
30
+
31
+ unique_fingerprints
32
+ end
33
+
34
+ # @return [ String ]
35
+ def self.wp_fingerprints_path
36
+ @wp_unique_fingerprints_path ||= File.join(DB_DIR, 'wp_fingerprints.json')
37
+ end
38
+
39
+ # @return [ Hash ]
40
+ def self.wp_fingerprints
41
+ @wp_fingerprints ||= read_json_file(wp_fingerprints_path)
42
+ end
43
+
44
+ # @return [ Hash ]
45
+ def self.wp_unique_fingerprints
46
+ @wp_unique_fingerprints ||= unique_fingerprints(wp_fingerprints)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -7,9 +7,11 @@ module WPScan
7
7
  FILES = %w[
8
8
  plugins.json themes.json wordpresses.json
9
9
  timthumbs-v3.txt user-agents.txt config_backups.txt
10
- dynamic_finders.yml wordpress.db LICENSE
10
+ dynamic_finders_01.yml wp_fingerprints.json LICENSE
11
11
  ].freeze
12
12
 
13
+ OLD_FILES = %w[wordpress.db dynamic_finders.yml].freeze
14
+
13
15
  attr_reader :repo_directory
14
16
 
15
17
  def initialize(repo_directory)
@@ -18,6 +20,16 @@ module WPScan
18
20
  FileUtils.mkdir_p(repo_directory) unless Dir.exist?(repo_directory)
19
21
 
20
22
  raise "#{repo_directory} is not writable" unless Pathname.new(repo_directory).writable?
23
+
24
+ delete_old_files
25
+ end
26
+
27
+ # Removes DB files which are no longer used
28
+ # this doesn't raise errors if they don't exist
29
+ def delete_old_files
30
+ OLD_FILES.each do |old_file|
31
+ FileUtils.remove_file(local_file_path(old_file), true)
32
+ end
21
33
  end
22
34
 
23
35
  # @return [ Time, nil ]
@@ -1,4 +1,4 @@
1
1
  # Version
2
2
  module WPScan
3
- VERSION = '3.0.5'.freeze
3
+ VERSION = '3.0.6'.freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wpscan
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.5
4
+ version: 3.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - WPScanTeam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-06 00:00:00.000000000 Z
11
+ date: 2017-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cms_scanner
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.37.11
19
+ version: 0.0.37.12
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.37.11
26
+ version: 0.0.37.12
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: yajl-ruby
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,62 +52,6 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.1'
55
- - !ruby/object:Gem::Dependency
56
- name: dm-core
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: 1.2.0
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: 1.2.0
69
- - !ruby/object:Gem::Dependency
70
- name: dm-migrations
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: 1.2.0
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: 1.2.0
83
- - !ruby/object:Gem::Dependency
84
- name: dm-constraints
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: 1.2.0
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: 1.2.0
97
- - !ruby/object:Gem::Dependency
98
- name: dm-sqlite-adapter
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: 1.2.0
104
- type: :runtime
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: 1.2.0
111
55
  - !ruby/object:Gem::Dependency
112
56
  name: rake
113
57
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +72,14 @@ dependencies:
128
72
  requirements:
129
73
  - - "~>"
130
74
  - !ruby/object:Gem::Version
131
- version: 3.6.0
75
+ version: 3.7.0
132
76
  type: :development
133
77
  prerelease: false
134
78
  version_requirements: !ruby/object:Gem::Requirement
135
79
  requirements:
136
80
  - - "~>"
137
81
  - !ruby/object:Gem::Version
138
- version: 3.6.0
82
+ version: 3.7.0
139
83
  - !ruby/object:Gem::Dependency
140
84
  name: rspec-its
141
85
  requirement: !ruby/object:Gem::Requirement
@@ -361,9 +305,9 @@ files:
361
305
  - lib/wpscan/controllers.rb
362
306
  - lib/wpscan/db.rb
363
307
  - lib/wpscan/db/dynamic_finders.rb
308
+ - lib/wpscan/db/fingerprints.rb
364
309
  - lib/wpscan/db/plugin.rb
365
310
  - lib/wpscan/db/plugins.rb
366
- - lib/wpscan/db/schema.rb
367
311
  - lib/wpscan/db/theme.rb
368
312
  - lib/wpscan/db/themes.rb
369
313
  - lib/wpscan/db/updater.rb
@@ -1,39 +0,0 @@
1
- module WPScan
2
- module DB
3
- # WP Version
4
- class Version < WpItem
5
- include DataMapper::Resource
6
-
7
- storage_names[:default] = 'versions'
8
-
9
- has n, :fingerprints, constraint: :destroy
10
-
11
- property :id, Serial
12
- property :number, String, required: true, unique: true
13
- end
14
-
15
- # Path
16
- class Path
17
- include DataMapper::Resource
18
-
19
- storage_names[:default] = 'paths'
20
-
21
- has n, :fingerprints, constraint: :destroy
22
-
23
- property :id, Serial
24
- property :value, String, required: true, unique: true
25
- end
26
-
27
- # Fingerprint
28
- class Fingerprint
29
- include DataMapper::Resource
30
-
31
- storage_names[:default] = 'fingerprints'
32
-
33
- belongs_to :version, key: true
34
- belongs_to :path, key: true
35
-
36
- property :md5_hash, String, required: true, length: 32
37
- end
38
- end
39
- end