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/fuzzers/ftp/ftp_pre_post.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
class MetasploitModule < Msf::Auxiliary
7
include Msf::Auxiliary::Scanner
8
include Msf::Exploit::Remote::Tcp
9
10
def initialize
11
super(
12
'Name' => 'Simple FTP Fuzzer',
13
'Description' => %q{
14
This module will connect to a FTP server and perform pre- and post-authentication fuzzing
15
},
16
'Author' => [ 'corelanc0d3r <peter.ve[at]corelan.be>', 'jduck' ],
17
'License' => MSF_LICENSE
18
)
19
20
register_options(
21
[
22
Opt::RPORT(21),
23
OptInt.new('STARTATSTAGE', [ false, "Start at this test stage",1]),
24
OptInt.new('STEPSIZE', [ false, "Increase string size each iteration with this number of chars",10]),
25
OptInt.new('DELAY', [ false, "Delay between connections in seconds",1]),
26
OptInt.new('STARTSIZE', [ false, "Fuzzing string startsize",10]),
27
OptInt.new('ENDSIZE', [ false, "Fuzzing string endsize",20000]),
28
OptInt.new('STOPAFTER', [ false, "Stop after x number of consecutive errors",2]),
29
OptString.new('USER', [ false, "Username",'anonymous']),
30
OptString.new('PASS', [ false, "Password",'[email protected]']),
31
OptBool.new('FASTFUZZ', [ false, "Only fuzz with cyclic pattern",true]),
32
OptBool.new('CONNRESET', [ false, "Break on CONNRESET error",true]),
33
])
34
35
@evilchars = [
36
'A','a','%s','%d','%n','%x','%p','-1','0','0xfffffffe','0xffffffff','A/','//','/..','//..',
37
'A%20','./A','.A',',A','A:','!A','&A','?A','\A','../A/','..?','//A:','\\A','{A','$A','A*',
38
'cmd','[email protected]','#A','A/../','~','~A','~A/','A`/','>A','<A','A%n','A../','.././','A../',
39
'....//','~?*/','.\../','\.//A','-%A','%Y','%H','/1','!','@','%','&','/?(*','*','(',')',
40
'`',',','~/','/.','\$:','/A~%n','=','=:;)}','1.2.','41414141','-1234','999999,','%00','+A',
41
'+123','..\'','??.','..\.\'','.../','1234123+',
42
'%Y%%Y%/','%FC%80%80%80%80%AE%FC%80%80%80%80%AE/','????/','\uff0e/','%%32%65%%32%65/',
43
'+B./','%%32%65%%32%65/','..%c0%af','..%e0%80%af','..%c1%9c'
44
]
45
@commands = [
46
'ABOR','ACCT','ALLO','APPE','AUTH','CWD','CDUP','DELE','FEAT','HELP','HOST','LANG','LIST',
47
'MDTM','MKD','MLST','MODE','NLST','NLST -al','NOOP','OPTS','PASV','PORT','PROT','PWD','REIN',
48
'REST','RETR','RMD','RNFR','RNTO','SIZE','SITE','SITE CHMOD','SITE CHOWN','SITE EXEC','SITE MSG',
49
'SITE PSWD','SITE ZONE','SITE WHO','SMNT','STAT','STOR','STOU','STRU','SYST','TYPE','XCUP',
50
'XCRC','XCWD','XMKD','XPWD','XRMD'
51
]
52
@emax = @evilchars.length
53
54
register_advanced_options(
55
[
56
OptString.new('FtpCommands', [ false, "Commands to fuzz at stages 4 and 5",@commands.join(" ")]),
57
OptBool.new('ExpandCrash', [ false, "Expand any crash strings",false]),
58
])
59
end
60
61
62
def get_pkt
63
buf = sock.get_once(-1, 10)
64
vprint_status("[in ] #{buf.inspect}")
65
buf
66
end
67
68
def send_pkt(pkt, get_resp = false)
69
vprint_status("[out] #{pkt.inspect}")
70
sock.put(pkt)
71
get_pkt if get_resp
72
end
73
74
75
def process_phase(phase_num, phase_name, prepend = '', initial_cmds = [])
76
print_status("[Phase #{phase_num}] #{phase_name} - #{Time.now.localtime}")
77
ecount = 1
78
@evilchars.each do |evilstr|
79
80
if datastore['FASTFUZZ']
81
evilstr = "Cyclic"
82
@emax = 1
83
end
84
85
if (@stopprocess == false)
86
count = datastore['STARTSIZE']
87
print_status(" Character : #{evilstr} (#{ecount}/#{@emax})")
88
ecount += 1
89
while count <= datastore['ENDSIZE']
90
begin
91
connect
92
if datastore['FASTFUZZ']
93
evil = Rex::Text.pattern_create(count)
94
else
95
evil = evilstr * count
96
end
97
print_status(" -> Fuzzing size set to #{count} (#{prepend}#{evilstr})")
98
initial_cmds.each do |cmd|
99
send_pkt(cmd, true)
100
end
101
pkt = prepend + evil + "\r\n"
102
send_pkt(pkt, true)
103
sock.put("QUIT\r\n")
104
select(nil, nil, nil, datastore['DELAY'])
105
disconnect
106
107
count += datastore['STEPSIZE']
108
109
rescue ::Exception => e
110
@error_cnt += 1
111
print_status("Exception #{@error_cnt} of #{@nr_errors}")
112
if (e.class.name == 'Rex::ConnectionRefused') or (e.class.name == 'EOFError') or (e.class.name == 'Errno::ECONNRESET' and datastore['CONNRESET']) or (e.class.name == 'Errno::EPIPE')
113
if datastore['ExpandCrash']
114
print_status("Crash string : #{prepend}#{evil}")
115
else
116
print_status("Crash string : #{prepend}#{evilstr} x #{count}")
117
end
118
if @error_cnt >= @nr_errors
119
print_status("System does not respond - exiting now\n")
120
@stopprocess = true
121
print_error("Error: #{e.class} #{e} #{e.backtrace}\n")
122
return
123
else
124
print_status("Exception triggered, need #{@nr_errors - @error_cnt} more exception(s) before interrupting process")
125
select(nil,nil,nil,3) #wait 3 seconds
126
end
127
end
128
if @error_cnt >= @nr_errors
129
count += datastore['STEPSIZE']
130
@error_cnt = 0
131
end
132
end
133
end
134
end
135
end
136
end
137
138
def ftp_commands
139
if datastore['FtpCommands'].to_s.upcase == "DEFAULT"
140
@commands
141
else
142
datastore['FtpCommands'].split(/[\s,]+/)
143
end
144
end
145
146
def run_host(ip)
147
148
startstage = datastore['STARTATSTAGE']
149
150
@nr_errors = datastore['STOPAFTER']
151
@error_cnt = 0
152
@stopprocess = false
153
154
if datastore['FASTFUZZ']
155
@evilchars = ['']
156
end
157
158
print_status("Connecting to host " + ip + " on port " + datastore['RPORT'].to_s)
159
160
if (startstage == 1)
161
process_phase(1, "Fuzzing without command")
162
startstage += 1
163
end
164
165
if (startstage == 2) and (@stopprocess == false)
166
process_phase(2, "Fuzzing USER", 'USER ')
167
startstage += 1
168
end
169
170
if (startstage == 3) and (@stopprocess == false)
171
process_phase(3, "Fuzzing PASS", 'PASS ',
172
[ "USER " + datastore['USER'] + "\r\n" ])
173
startstage += 1
174
end
175
176
if (startstage == 4)
177
print_status "[Phase 4] Fuzzing commands: #{ftp_commands.join(", ")}"
178
ftp_commands().each do |cmd|
179
if (@stopprocess == false)
180
process_phase(4, "Fuzzing command: #{cmd}", "#{cmd} ",
181
[
182
"USER " + datastore['USER'] + "\r\n",
183
"PASS " + datastore['PASS'] + "\r\n"
184
])
185
end
186
end
187
# Don't progress into stage 5, it must be selected manually.
188
#startstage += 1
189
end
190
191
# Fuzz other commands, all command combinations in one session
192
if (startstage == 5)
193
print_status("[Phase 5] Fuzzing other commands (Part 2, #{Time.now.localtime}): #{ftp_commands.join(", ")}")
194
ftp_commands().each do |cmd|
195
if (@stopprocess == false)
196
ecount = 1
197
count = datastore['STARTSIZE']
198
print_status("Fuzzing command #{cmd} - #{Time.now.localtime}" )
199
200
connect
201
pkt = "USER " + datastore['USER'] + "\r\n"
202
send_pkt(pkt, true)
203
pkt = "PASS " + datastore['PASS'] + "\r\n"
204
send_pkt(pkt, true)
205
206
while count <= datastore['ENDSIZE']
207
print_status(" -> Fuzzing size set to #{count}")
208
begin
209
@evilchars.each do |evilstr|
210
if datastore['FASTFUZZ']
211
evilstr = "Cyclic"
212
evil = Rex::Text.pattern_create(count)
213
@emax = 1
214
ecount = 1
215
else
216
evil = evilstr * count
217
end
218
print_status(" Command : #{cmd}, Character : #{evilstr} (#{ecount}/#{@emax})")
219
ecount += 1
220
pkt = cmd + " " + evil + "\r\n"
221
send_pkt(pkt, true)
222
select(nil, nil, nil, datastore['DELAY'])
223
@error_cnt = 0
224
end
225
rescue ::Exception => e
226
@error_cnt += 1
227
print_status("Exception #{@error_cnt} of #{@nr_errors}")
228
if (e.class.name == 'Rex::ConnectionRefused') or (e.class.name == 'EOFError') or (e.class.name == 'Errno::ECONNRESET' and datastore['CONNRESET']) or (e.class.name == 'Errno::EPIPE')
229
if @error_cnt >= @nr_errors
230
print_status("System does not respond - exiting now\n")
231
@stopprocess = true
232
print_error("Error: #{e.class} #{e} #{e.backtrace}\n")
233
return
234
else
235
print_status("Exception triggered, need #{@nr_errors - @error_cnt} more exception(s) before interrupting process")
236
select(nil,nil,nil,3) #wait 3 seconds
237
end
238
end
239
if @error_cnt >= @nr_errors
240
@error_cnt = 0
241
end
242
end
243
count += datastore['STEPSIZE']
244
end
245
sock.put("QUIT\r\n")
246
select(nil, nil, nil, datastore['DELAY'])
247
disconnect
248
end
249
end
250
end
251
end
252
end
253
254