Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/http/brute_dirs.rb
21546 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'enumerable'
7
8
class MetasploitModule < Msf::Auxiliary
9
include Msf::Exploit::Remote::HttpClient
10
include Msf::Auxiliary::WmapScanDir
11
include Msf::Auxiliary::Scanner
12
include Msf::Auxiliary::Report
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'HTTP Directory Brute Force Scanner',
19
'Description' => %q{
20
This module identifies the existence of interesting directories by brute forcing the name
21
in a given directory path.
22
},
23
'Author' => [ 'et' ],
24
'License' => BSD_LICENSE,
25
'Notes' => {
26
'Reliability' => UNKNOWN_RELIABILITY,
27
'Stability' => UNKNOWN_STABILITY,
28
'SideEffects' => UNKNOWN_SIDE_EFFECTS
29
}
30
)
31
)
32
33
register_options(
34
[
35
OptString.new('PATH', [ true, "The path to identify directories", '/']),
36
OptString.new('FORMAT', [ true, "The expected directory format (a alpha, d digit, A upperalpha)", 'a,aa,aaa']),
37
OptInt.new('TIMEOUT', [true, 'The socket connect/read timeout in seconds', 20]),
38
OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]),
39
OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]),
40
]
41
)
42
43
register_advanced_options(
44
[
45
OptInt.new('ErrorCode', [ true, "The expected http code for non existent directories", 404]),
46
OptPath.new('HTTP404Sigs', [
47
false, "Path of 404 signatures to use",
48
File.join(Msf::Config.data_directory, "wmap", "wmap_404s.txt")
49
]),
50
OptBool.new('NoDetailMessages', [ false, "Do not display detailed test messages", true ]),
51
OptInt.new('TestThreads', [ true, "Number of test threads", 25])
52
]
53
)
54
end
55
56
def wmap_enabled
57
true
58
end
59
60
def run_host(ip)
61
conn = false
62
63
timeout = datastore['TIMEOUT']
64
65
delay_value = datastore['DELAY'].to_i
66
if delay_value < 0
67
raise Msf::OptionValidateError.new(['DELAY'])
68
end
69
70
jitter_value = datastore['JITTER'].to_i
71
if jitter_value < 0
72
raise Msf::OptionValidateError.new(['JITTER'])
73
end
74
75
tpath = normalize_uri(datastore['PATH'])
76
if tpath[-1, 1] != '/'
77
tpath += '/'
78
end
79
80
vhost = datastore['VHOST'] || datastore['RHOST']
81
82
dm = datastore['NoDetailMessages']
83
84
# You may add more extensions in the extens array
85
extens = ["/"]
86
87
# You may add multiple formats in the array
88
forma = []
89
forma = datastore['FORMAT'].split(',')
90
91
ecode = datastore['ErrorCode'].to_i
92
extens.each do |exte|
93
#
94
# Detect error code
95
#
96
ecode = datastore['ErrorCode'].to_i
97
begin
98
randdir = Rex::Text.rand_text_alpha(5).chomp
99
randdir << exte
100
res = send_request_cgi({
101
'uri' => tpath + randdir,
102
'method' => 'GET',
103
'ctype' => 'text/html'
104
}, timeout)
105
106
return if not res
107
108
tcode = res.code.to_i
109
110
# Look for a string we can signature on as well
111
if (tcode >= 200 and tcode <= 299)
112
emesg = nil
113
File.open(datastore['HTTP404Sigs'], 'rb').each do |str|
114
if (res.body.index(str))
115
emesg = str
116
break
117
end
118
end
119
120
if (not emesg)
121
print_status("Using first 256 bytes of the response as 404 string")
122
emesg = res.body[0, 256]
123
else
124
print_status("Using custom 404 string of '#{emesg}'")
125
end
126
else
127
ecode = tcode
128
print_status("Using code '#{ecode}' as not found.")
129
end
130
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
131
conn = false
132
rescue ::Timeout::Error, ::Errno::EPIPE
133
end
134
135
forma.each do |f|
136
numb = []
137
f.scan(/./) { |c|
138
case c
139
when 'a'
140
numb << ('a'..'z')
141
when 'd'
142
numb << ('0'..'9')
143
when 'A'
144
numb << ('A'..'Z')
145
# These dont actually work
146
# when 'N'
147
# numb << ('A'..'Z')+('0'..'9')
148
# when 'n'
149
# numb << ('a'..'z')+('0'..'9')
150
else
151
print_error("Format string error")
152
return
153
end
154
}
155
156
# exte.scan(/./) { |c|
157
# numb << "#{c}"
158
# }
159
160
Enumerable.cart(*numb).each { |testd|
161
strdir = testd.join
162
163
begin
164
teststr = tpath + strdir
165
teststr << exte
166
167
# Add the delay based on JITTER and DELAY if needs be
168
add_delay_jitter(delay_value, jitter_value)
169
170
vprint_status("Try... #{wmap_base_url}#{teststr} (#{vhost})")
171
172
res = send_request_cgi({
173
'uri' => teststr,
174
'method' => 'GET',
175
'ctype' => 'text/plain'
176
}, timeout)
177
178
if (not res or ((res.code.to_i == ecode) or (emesg and res.body.index(emesg))))
179
if dm == false
180
print_status("NOT Found #{wmap_base_url}#{teststr} #{res.code.to_i}")
181
# blah
182
end
183
else
184
if res.code.to_i == 400 and ecode != 400
185
print_error("Server returned an error code. #{wmap_base_url}#{teststr} #{res.code.to_i}")
186
else
187
print_good("Found #{wmap_base_url}#{teststr} #{res.code.to_i}")
188
189
report_web_vuln({
190
:host => rhost,
191
:port => rport,
192
:vhost => vhost,
193
:ssl => ssl,
194
:path => "#{teststr}",
195
:method => 'GET',
196
:pname => "",
197
:proof => "Res code: #{res.code.to_s}",
198
:risk => 0,
199
:confidence => 100,
200
:category => 'directory',
201
:description => 'Directory found.',
202
:name => 'directory'
203
})
204
205
end
206
end
207
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
208
rescue ::Timeout::Error, ::Errno::EPIPE
209
end
210
}
211
end
212
end
213
end
214
end
215
216