Class: Rex::Proto::Ftp::Client
- Inherits:
-
Object
- Object
- Rex::Proto::Ftp::Client
- Defined in:
- lib/rex/proto/ftp/client.rb
Overview
Acts as a client to an FTP server See the RFC: www.w3.org/Protocols/rfc959/
Instance Method Summary collapse
-
#cd(path) ⇒ Object
Function implementing ‘cd’ or change directory command.
-
#connect ⇒ Object
This method estabilishes a connection to the host on the port defined in opts{}, if the connection is successful, the method returns a socket which can be used to communicate with the client.
-
#connect_login ⇒ Object
This method uses the senduser and sendpass methods defined below in order to login to the ftp server.
-
#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.
-
#download(filename) ⇒ Object
Function implementing download command.
-
#initialize(host, port = 21, ssl = nil, ssl_version = nil, proxies = nil, username = '', password = '', verbose = false) ⇒ Client
constructor
Creates a new client instance.
-
#ls ⇒ Object
Function implementing ‘ls’ or list files command.
-
#pwd ⇒ Object
Function implementing ‘pwd’ or present working directory command.
-
#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.
-
#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.
-
#upload ⇒ Object
Function implementing upload command.
Constructor Details
#initialize(host, port = 21, ssl = nil, ssl_version = nil, proxies = nil, username = '', password = '', verbose = false) ⇒ Client
Creates a new client instance.
18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/rex/proto/ftp/client.rb', line 18 def initialize(host, port = 21, ssl = nil, ssl_version = nil, proxies = nil, username = '', password = '', verbose = false) self.hostname = host self.port = port.to_i self.context = context self.ssl = ssl self.ssl_version = ssl_version self.proxies = proxies self.username = username self.password = password self.verbose = verbose end |
Instance Method Details
#cd(path) ⇒ Object
Function implementing ‘cd’ or change directory command
314 315 316 |
# File 'lib/rex/proto/ftp/client.rb', line 314 def cd(path) send_cmd(["cwd " + path]) end |
#connect ⇒ Object
This method estabilishes a connection to the host on the port defined in opts{}, if the connection is successful, the method returns a socket which can be used to communicate with the client
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/rex/proto/ftp/client.rb', line 35 def connect nsock = Rex::Socket::Tcp.create( 'PeerHost' => self.hostname, 'PeerPort' => self.port, 'LocalHost' => "0.0.0.0", 'LocalPort' => 0.to_i, 'SSL' => self.ssl, 'SSLVersion' => self.ssl_version, 'Proxies' => self.proxies ) self.sock = nsock self. = recv_ftp_resp(nsock) print_status("Connected to target FTP server.") if self.verbose nsock end |
#connect_login ⇒ Object
This method uses the senduser and sendpass methods defined below in order to login to the ftp server
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 |
# File 'lib/rex/proto/ftp/client.rb', line 127 def connect_login ftpsock = connect if !(self.user && self.pass) print_error("No username and password were supplied, unable to login") return false end print_status("Authenticating as #{user} with password #{pass}...") if self.verbose res = send_user(user, ftpsock) if res !~ /^(331|2)/ print_error("The server rejected our username") if self.verbose return false end if pass print_status("Sending password...") if self.verbose res = send_pass(pass, ftpsock) if res !~ /^2/ print_error("The server rejected our password") if self.verbose return false end end true end |
#data_connect(mode = nil, nsock = self.sock) ⇒ Object
This method handles establishing datasocket for data channel
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/rex/proto/ftp/client.rb', line 176 def data_connect(mode = nil, nsock = self.sock) 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 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 ) end self.datasocket end |
#data_disconnect ⇒ Object
This method handles disconnecting our data channel
204 205 206 207 |
# File 'lib/rex/proto/ftp/client.rb', line 204 def data_disconnect self.datasocket.shutdown if self.datasocket self.datasocket = nil end |
#download(filename) ⇒ Object
Function implementing download command
321 322 323 324 325 326 327 328 329 |
# File 'lib/rex/proto/ftp/client.rb', line 321 def download(filename) datasocket = data_connect send_cmd(["retr", filename]) output = datasocket.get file = File.open(filename, "wb") file.write(output) file.close data_disconnect end |
#ls ⇒ Object
Function implementing ‘ls’ or list files command
296 297 298 299 300 301 302 |
# File 'lib/rex/proto/ftp/client.rb', line 296 def ls datasocket = data_connect send_cmd(["list"]) output = datasocket.get data_disconnect output end |
#pwd ⇒ Object
Function implementing ‘pwd’ or present working directory command
307 308 309 |
# File 'lib/rex/proto/ftp/client.rb', line 307 def pwd send_cmd(["pwd"]) end |
#raw_send(cmd, nsock = self.sock) ⇒ Object
This method transmits a FTP command and does not wait for a response
109 110 111 112 |
# File 'lib/rex/proto/ftp/client.rb', line 109 def raw_send(cmd, nsock = self.sock) print_status("FTP send: #{cmd.inspect}") if self.verbose 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.
118 119 120 121 |
# File 'lib/rex/proto/ftp/client.rb', line 118 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
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 98 99 100 101 102 103 104 |
# File 'lib/rex/proto/ftp/client.rb', line 54 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 !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 !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 !found_end resp << ln resp << "\r\n" if ln.length > 3 && 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 self.verbose return resp end end end |
#send_cmd(args, recv = true, nsock = self.sock) ⇒ Object
This method sends one command with zero or more parameters
212 213 214 215 216 217 218 219 |
# File 'lib/rex/proto/ftp/client.rb', line 212 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 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.
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 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 |
# File 'lib/rex/proto/ftp/client.rb', line 238 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 !type return send_cmd(args, true, nsock) end # Set the transfer mode and connect to the remove server return nil if !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 = self.datasocket.get_once(-1, ftp_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.
168 169 170 171 |
# File 'lib/rex/proto/ftp/client.rb', line 168 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.
224 225 226 227 |
# File 'lib/rex/proto/ftp/client.rb', line 224 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.
159 160 161 162 |
# File 'lib/rex/proto/ftp/client.rb', line 159 def send_user(user, nsock = self.sock) raw_send("USER #{user}\r\n", nsock) recv_ftp_resp(nsock) end |
#upload ⇒ Object
Function implementing upload command
334 335 336 337 338 339 340 341 342 |
# File 'lib/rex/proto/ftp/client.rb', line 334 def upload datasocket = data_connect file = File.open(filename, "rb") data = file.read file.close send_cmd(["stor", filename]) datasocket.write(data) data_disconnect end |