Module: Msf::Exploit::Remote::Ftp
- Includes:
- 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
-
#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 Tcp
#chost, #cleanup, #connect_timeout, #cport, #disconnect, #handler, #lhost, #lport, #peer, #print_prefix, #proxies, #rhost, #rport, #set_tcp_evasions, #shutdown, #ssl, #ssl_cipher, #ssl_verify_mode, #ssl_version
Instance Attribute Details
#banner ⇒ Object (protected)
This attribute holds the banner that was read in after a successful call to connect or connect_login.
381 382 383 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 381 def @banner end |
#datasocket ⇒ Object (protected)
This attribute holds the banner that was read in after a successful call to connect or connect_login.
381 382 383 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 381 def datasocket @datasocket end |
Instance Method Details
#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.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 49 def connect(global = true, verbose = nil) verbose ||= datastore['FTPDEBUG'] verbose ||= datastore['VERBOSE'] print_status("Connecting to FTP server #{rhost}:#{rport}...") if verbose fd = super(global) # Wait for a banner to arrive... self. = recv_ftp_resp(fd) print_status("Connected to target FTP server.") if verbose # 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.
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 160 161 162 163 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 135 def connect_login(global = true, verbose = nil) verbose ||= datastore['FTPDEBUG'] verbose ||= datastore['VERBOSE'] 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
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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 69 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
120 121 122 123 124 125 126 127 128 129 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 120 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
369 370 371 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 369 def ftp_data_timeout (datastore['FTPDataTimeout'] || 1).to_i end |
#ftp_timeout ⇒ Object
Returns the number of seconds to wait for a FTP reply
362 363 364 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 362 def ftp_timeout (datastore['FTPTimeout'] || 10).to_i end |
#initialize(info = {}) ⇒ Object
Creates an instance of an FTP exploit module.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 18 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', '[email protected]'], 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.
355 356 357 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 355 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
334 335 336 337 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 334 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.
271 272 273 274 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 271 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
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 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 279 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] == ' ' 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
194 195 196 197 198 199 200 201 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 194 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.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 212 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.
178 179 180 181 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 178 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.
186 187 188 189 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 186 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.
169 170 171 172 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 169 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.
348 349 350 |
# File 'lib/msf/core/exploit/remote/ftp.rb', line 348 def user datastore['FTPUSER'] end |