Module: Msf::Exploit::Remote::Ftp
- Includes:
- Auxiliary::Report, Tcp
- Defined in:
- lib/msf/core/exploit/remote/ftp.rb
Overview
This module exposes methods that may be useful to exploits that deal with servers that speak the File Transfer Protocol (FTP).
Instance Attribute Summary collapse
-
#banner ⇒ Object
protected
This attribute holds the banner that was read in after a successful call to connect or connect_login.
-
#datasocket ⇒ Object
protected
This attribute holds the banner that was read in after a successful call to connect or connect_login.
Attributes included from Tcp
Instance Method Summary collapse
-
#banner_version ⇒ Object
Extracts a normalized version string from the FTP banner 220 (vsFTPd 2.3.4)x0dx0a -> vsFTPd 2.3.4 220 ProFTPD 1.3.1 Server (Debian) [::ffff:10.0.0.10]x0dx0a -> ProFTPD 1.3.1 Server (Debian).
-
#connect(global = true, verbose = nil) ⇒ Object
(also: #ftp_connect)
This method establishes an FTP connection to host and port specified by the ‘rhost’ and ‘rport’ methods.
-
#connect_login(global = true, verbose = nil) ⇒ Object
Connect and login to the remote FTP server using the credentials that have been supplied in the exploit options.
-
#data_connect(mode = nil, nsock = self.sock) ⇒ Object
This method handles establishing datasocket for data channel.
-
#data_disconnect ⇒ Object
This method handles disconnecting our data channel.
-
#ftp_data_timeout ⇒ Object
Returns the number of seconds to wait to get more FTP data.
-
#ftp_timeout ⇒ Object
Returns the number of seconds to wait for a FTP reply.
-
#initialize(info = {}) ⇒ Object
Creates an instance of an FTP exploit module.
-
#pass ⇒ Object
Returns the user string from the ‘FTPPASS’ option.
-
#raw_send(cmd, nsock = self.sock) ⇒ Object
This method transmits a FTP command and does not wait for a response.
-
#raw_send_recv(cmd, nsock = self.sock) ⇒ Object
This method transmits a FTP command and waits for a response.
-
#recv_ftp_resp(nsock = self.sock) ⇒ Object
This method reads an FTP response based on FTP continuation stuff.
-
#send_cmd(args, recv = true, nsock = self.sock) ⇒ Object
This method sends one command with zero or more parameters.
-
#send_cmd_data(args, data, mode = 'a', nsock = self.sock) ⇒ Object
This method transmits the command in args and receives / uploads DATA via data channel For commands not needing data, it will fall through to the original send_cmd.
-
#send_pass(pass, nsock = self.sock) ⇒ Object
This method completes user authentication by sending the supplied password using the FTP ‘PASS <pass>’ command.
-
#send_quit(nsock = self.sock) ⇒ Object
This method sends a QUIT command.
-
#send_user(user, nsock = self.sock) ⇒ Object
This method logs in as the supplied user by transmitting the FTP ‘USER <user>’ command.
-
#user ⇒ Object
Returns the user string from the ‘FTPUSER’ option.
Methods included from Auxiliary::Report
#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot
Methods included from Metasploit::Framework::Require
optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines
Methods included from Tcp
#chost, #cleanup, #connect_timeout, #cport, #disconnect, #handler, #lhost, #lport, #peer, #print_prefix, #proxies, #replicant, #rhost, #rport, #set_tcp_evasions, #shutdown, #ssl, #ssl_cipher, #ssl_verify_mode, #ssl_version, #sslkeylogfile
Instance Attribute Details
#banner ⇒ Object (protected)
This attribute holds the banner that was read in after a successful call to connect or connect_login.
425 426 427 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 425 def @banner end |
#datasocket ⇒ Object (protected)
This attribute holds the banner that was read in after a successful call to connect or connect_login.
425 426 427 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 425 def datasocket @datasocket end |
Instance Method Details
#banner_version ⇒ Object
Extracts a normalized version string from the FTP banner 220 (vsFTPd 2.3.4)x0dx0a -> vsFTPd 2.3.4 220 ProFTPD 1.3.1 Server (Debian) [::ffff:10.0.0.10]x0dx0a -> ProFTPD 1.3.1 Server (Debian)
102 103 104 105 106 107 108 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 102 def .to_s .sub(/^\d{3}[\s-]/, '') .strip .gsub(/\A\(|\)\z/, '') .gsub(/\s*\[(?:(?:\d{1,3}\.){3}\d{1,3}|[0-9A-Fa-f:]*:[0-9A-Fa-f:.]+)\]/, '') end |
#connect(global = true, verbose = nil) ⇒ Object Also known as: ftp_connect
This method establishes an FTP connection to host and port specified by the ‘rhost’ and ‘rport’ methods. After connecting, the banner message is read in and stored in the ‘banner’ attribute.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 50 def connect(global = true, verbose = nil) verbose = datastore['FTPDEBUG'] || datastore['VERBOSE'] if verbose.nil? print_status("Connecting to FTP server...") if verbose begin fd = super(global) rescue ::Rex::ConnectionRefused report_host(host: rhost) raise end # Wait for a banner to arrive... self. = recv_ftp_resp(fd) print_status('Connected to target FTP server') if verbose # Only record the service and banner when the greeting looks like FTP (RFC 959) if self.&.match?(/^(120|220)[\s-]/) # Cleaned up FTP banner report_service( host: rhost, port: rport, proto: 'tcp', name: 'ftp', info: Rex::Text.to_hex_ascii(), parents: { host: rhost, port: rport, proto: 'tcp', name: 'tcp' } ) # Raw FTP banner report_note( host: rhost, port: rport, proto: 'tcp', sname: 'ftp', type: 'ftp.banner', data: { banner: Rex::Text.to_hex_ascii(self..strip) } ) end # Return the file descriptor to the caller fd end |
#connect_login(global = true, verbose = nil) ⇒ Object
Connect and login to the remote FTP server using the credentials that have been supplied in the exploit options.
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 179 def connect_login(global = true, verbose = nil) verbose = datastore['FTPDEBUG'] || datastore['VERBOSE'] if verbose.nil? ftpsock = ftp_connect(global, verbose) if !(user and pass) print_error("No username and password were supplied, unable to login") return false end print_status("Authenticating as #{user} with password #{pass}...") if verbose res = send_user(user, ftpsock) if (res !~ /^(331|2)/) print_error("The server rejected our username") if verbose return false end if (pass) print_status("Sending password...") if verbose res = send_pass(pass, ftpsock) if (res !~ /^2/) print_error("The server rejected our password") if verbose return false end end return true end |
#data_connect(mode = nil, nsock = self.sock) ⇒ Object
This method handles establishing datasocket for data channel
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 113 def data_connect(mode = nil, nsock = self.sock) pass_mode = datastore['PassiveMode'] if mode res = send_cmd([ 'TYPE' , mode ], true, nsock) return nil if not res =~ /^200/ end # force datasocket to renegotiate self.datasocket.shutdown if self.datasocket != nil # Need to be able to do both extended and normal # passive modes. normal passive mode is default # details of EPSV are in RFC2428 # pass_mode = true is EPSV; false is PASV if pass_mode res = send_cmd(['EPSV'], true, nsock) return nil if not res =~ /^229/ # 229 Entering Passive Mode (|||port|) if res =~ /\(\|\|\|(\d+)\|\)/ # convert port to FTP syntax datahost = "#{rhost}" dataport = $1.to_i self.datasocket = Rex::Socket::Tcp.create( 'PeerHost' => datahost, 'PeerPort' => dataport, 'Context' => { 'Msf' => framework, 'MsfExploit' => self } ) end else res = send_cmd(['PASV'], true, nsock) return nil if not res =~ /^227/ # 227 Entering Passive Mode (127,0,0,1,196,5) if res =~ /\((\d+)\,(\d+),(\d+),(\d+),(\d+),(\d+)/ # convert port to FTP syntax datahost = "#{$1}.#{$2}.#{$3}.#{$4}" dataport = ($5.to_i * 256) + $6.to_i self.datasocket = Rex::Socket::Tcp.create( 'PeerHost' => datahost, 'PeerPort' => dataport, 'Context' => { 'Msf' => framework, 'MsfExploit' => self } ) end end self.datasocket end |
#data_disconnect ⇒ Object
This method handles disconnecting our data channel
164 165 166 167 168 169 170 171 172 173 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 164 def data_disconnect begin if datasocket datasocket.shutdown datasocket.close end rescue IOError end datasocket = nil if datasocket end |
#ftp_data_timeout ⇒ Object
Returns the number of seconds to wait to get more FTP data
413 414 415 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 413 def ftp_data_timeout (datastore['FTPDataTimeout'] || 1).to_i end |
#ftp_timeout ⇒ Object
Returns the number of seconds to wait for a FTP reply
406 407 408 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 406 def ftp_timeout (datastore['FTPTimeout'] || 10).to_i end |
#initialize(info = {}) ⇒ Object
Creates an instance of an FTP exploit module.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 19 def initialize(info = {}) super # Register the options that all FTP exploits may make use of. ( [ Opt::RHOST, Opt::RPORT(21), OptString.new('FTPUSER', [ false, 'The username to authenticate as', 'anonymous'], fallbacks: ['USERNAME']), OptString.new('FTPPASS', [ false, 'The password for the specified username', 'mozilla@example.com'], fallbacks: ['PASSWORD']), ], Msf::Exploit::Remote::Ftp) ( [ OptInt.new('FTPTimeout', [ true, 'The number of seconds to wait for a reply from an FTP command', 16]), OptBool.new('FTPDEBUG', [ false, 'Whether or not to print verbose debug statements', false ]), OptBool.new('PassiveMode', [ false, 'Set true for extended passive (EPSV) ftp mode.', false]) ], Msf::Exploit::Remote::Ftp) register_autofilter_ports([ 21, 2121]) register_autofilter_services(%W{ ftp }) @ftpbuff = "" end |
#pass ⇒ Object
Returns the user string from the ‘FTPPASS’ option.
399 400 401 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 399 def pass datastore['FTPPASS'] end |
#raw_send(cmd, nsock = self.sock) ⇒ Object
This method transmits a FTP command and does not wait for a response
378 379 380 381 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 378 def raw_send(cmd, nsock = self.sock) print_status("FTP send: #{cmd.inspect}") if datastore['FTPDEBUG'] nsock.put(cmd) end |
#raw_send_recv(cmd, nsock = self.sock) ⇒ Object
This method transmits a FTP command and waits for a response. If one is received, it is returned to the caller.
315 316 317 318 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 315 def raw_send_recv(cmd, nsock = self.sock) nsock.put(cmd) nsock.get_once(-1, ftp_timeout) end |
#recv_ftp_resp(nsock = self.sock) ⇒ Object
This method reads an FTP response based on FTP continuation stuff
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 323 def recv_ftp_resp(nsock = self.sock) found_end = false resp = "" left = "" if !@ftpbuff.empty? left << @ftpbuff @ftpbuff = "" end while true data = nsock.get_once(-1, ftp_timeout) if not data @ftpbuff << resp @ftpbuff << left return data end got = left + data left = "" # handle the end w/o newline case enlidx = got.rindex(0x0a.chr) if enlidx != (got.length-1) if not enlidx left << got next else left << got.slice!((enlidx+1)..got.length) end end # split into lines rarr = got.split(/\r?\n/) rarr.each do |ln| if not found_end resp << ln resp << "\r\n" if ln.length > 3 and ln[3,1] == ' ' and ln[0,3] =~ /\A\d{3}\z/ found_end = true end else left << ln left << "\r\n" end end if found_end @ftpbuff << left print_status("FTP recv: #{resp.inspect}") if datastore['FTPDEBUG'] return resp end end end |
#send_cmd(args, recv = true, nsock = self.sock) ⇒ Object
This method sends one command with zero or more parameters
238 239 240 241 242 243 244 245 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 238 def send_cmd(args, recv = true, nsock = self.sock) cmd = args.join(" ") + "\r\n" ret = raw_send(cmd, nsock) if (recv) return recv_ftp_resp(nsock) end return ret end |
#send_cmd_data(args, data, mode = 'a', nsock = self.sock) ⇒ Object
This method transmits the command in args and receives / uploads DATA via data channel For commands not needing data, it will fall through to the original send_cmd
For commands that send data only, the return will be the server response. For commands returning both data and a server response, an array will be returned.
NOTE: This function always waits for a response from the server.
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 256 def send_cmd_data(args, data, mode = 'a', nsock = self.sock) type = nil # implement some aliases for various commands if (args[0] =~ /^DIR$/i || args[0] =~ /^LS$/i) # TODO || args[0] =~ /^MDIR$/i || args[0] =~ /^MLS$/i args[0] = "LIST" type = "get" elsif (args[0] =~ /^GET$/i) args[0] = "RETR" type = "get" elsif (args[0] =~ /^PUT$/i) args[0] = "STOR" type = "put" end # fall back if it's not a supported data command if not type return send_cmd(args, true, nsock) end # Set the transfer mode and connect to the remove server return nil if not data_connect(mode) # Our pending command should have got a connection now. res = send_cmd(args, true, nsock) # make sure could open port return nil unless res =~ /^(150|125) / # dispatch to the proper method if (type == "get") # failed listings just disconnect.. begin data = datasocket.get(ftp_timeout, ftp_data_timeout) rescue ::EOFError data = nil end else sent = self.datasocket.put(data) end # close data channel so command channel updates data_disconnect # get status of transfer ret = nil if (type == "get") ret = recv_ftp_resp(nsock) ret = [ ret, data ] else ret = recv_ftp_resp(nsock) end ret end |
#send_pass(pass, nsock = self.sock) ⇒ Object
This method completes user authentication by sending the supplied password using the FTP ‘PASS <pass>’ command.
222 223 224 225 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 222 def send_pass(pass, nsock = self.sock) raw_send("PASS #{pass}\r\n", nsock) recv_ftp_resp(nsock) end |
#send_quit(nsock = self.sock) ⇒ Object
This method sends a QUIT command.
230 231 232 233 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 230 def send_quit(nsock = self.sock) raw_send("QUIT\r\n", nsock) recv_ftp_resp(nsock) end |
#send_user(user, nsock = self.sock) ⇒ Object
This method logs in as the supplied user by transmitting the FTP ‘USER <user>’ command.
213 214 215 216 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 213 def send_user(user, nsock = self.sock) raw_send("USER #{user}\r\n", nsock) recv_ftp_resp(nsock) end |
#user ⇒ Object
Returns the user string from the ‘FTPUSER’ option.
392 393 394 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 392 def user datastore['FTPUSER'] end |