CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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