Module: Metasploit::Framework::Ftp::Client

Extended by:
ActiveSupport::Concern
Includes:
Tcp::Client
Included in:
LoginScanner::FTP
Defined in:
lib/metasploit/framework/ftp/client.rb

Instance Attribute Summary collapse

Attributes included from Tcp::Client

#max_send_size, #send_delay, #sock

Instance Method Summary collapse

Methods included from Tcp::Client

#chost, #cport, #disconnect, #proxies, #rhost, #rport, #set_tcp_evasions, #ssl, #ssl_version

Instance Attribute Details

This attribute holds the banner that was read in after a successful call to connect or connect_login.



285
286
287
# File 'lib/metasploit/framework/ftp/client.rb', line 285

def banner
  @banner
end

#datasocket ⇒ Object (protected)

This attribute holds the banner that was read in after a successful call to connect or connect_login.



285
286
287
# File 'lib/metasploit/framework/ftp/client.rb', line 285

def datasocket
  @datasocket
end

Instance Method Details

#connect(global = true) ⇒ Object

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.



15
16
17
18
19
20
21
22
23
24
25
# File 'lib/metasploit/framework/ftp/client.rb', line 15

def connect(global = true)
  fd = super(global)

  @ftpbuff = '' unless @ftpbuff

  # Wait for a banner to arrive...
  self.banner = recv_ftp_resp(fd)

  # Return the file descriptor to the caller
  fd
end

#connect_login(user, pass, global = true) ⇒ Object

Connect and login to the remote FTP server using the credentials that have been supplied in the exploit options.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/metasploit/framework/ftp/client.rb', line 78

def (user,pass,global = true)
  ftpsock = connect(global)

  if !(user and pass)
    return false
  end

  res = send_user(user, ftpsock)

  if (res !~ /^(331|2)/)
    return false
  end

  if (pass)
    res = send_pass(pass, ftpsock)
    if (res !~ /^2/)
      return false
    end
  end

  return true
end

#data_connect(mode = nil, nsock = self.sock) ⇒ Object

This method handles establishing datasocket for data channel



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/metasploit/framework/ftp/client.rb', line 30

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
  if datasocket
    old_datasocket = datasocket
    self.datasocket = nil
    old_datasocket.shutdown
    old_datasocket.close
  end

  res = send_cmd(['PASV'], true, nsock)
  return nil if not res =~ /^227/

  # 227 Entering Passive Mode (127,0,0,1,196,5)
  match = res.match(/\((\d{1,3}),(\d{1,3}),(\d{1,3}),(\d{1,3}),(\d{1,3}),(\d{1,3})\)/)
  return nil unless match

  values = match.captures.map(&:to_i)
  return nil unless values.all? { |value| value.between?(0, 255) }

  dataport = (values[4] * 256) + values[5]
  return nil if dataport.zero?

  # Prefer the original control connection host over the host advertised in the PASV response.
  self.datasocket = Rex::Socket::Tcp.create(
    'PeerHost' => nsock.peerhost,
    'PeerPort' => dataport,
    'Context'  => { 'Msf' => framework, 'MsfExploit' => framework_module }
  )
  self.datasocket
end

#data_disconnect ⇒ Object

This method handles disconnecting our data channel



69
70
71
72
# File 'lib/metasploit/framework/ftp/client.rb', line 69

def data_disconnect
  self.datasocket.shutdown
  self.datasocket = nil
end

#ftp_timeout ⇒ Object

Raises:

  • (NotImplementedError)


273
274
275
# File 'lib/metasploit/framework/ftp/client.rb', line 273

def ftp_timeout
  raise NotImplementedError
end

#raw_send(cmd, nsock = self.sock) ⇒ Object

This method transmits a FTP command and does not wait for a response



269
270
271
# File 'lib/metasploit/framework/ftp/client.rb', line 269

def raw_send(cmd, nsock = self.sock)
  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.



207
208
209
210
# File 'lib/metasploit/framework/ftp/client.rb', line 207

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



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
# File 'lib/metasploit/framework/ftp/client.rb', line 215

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
      return resp
    end
  end
end

#send_cmd(args, recv = true, nsock = self.sock) ⇒ Object

This method sends one command with zero or more parameters



130
131
132
133
134
135
136
137
# File 'lib/metasploit/framework/ftp/client.rb', line 130

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.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/metasploit/framework/ftp/client.rb', line 148

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 = 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 ' command.



114
115
116
117
# File 'lib/metasploit/framework/ftp/client.rb', line 114

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.



122
123
124
125
# File 'lib/metasploit/framework/ftp/client.rb', line 122

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 ' command.



105
106
107
108
# File 'lib/metasploit/framework/ftp/client.rb', line 105

def send_user(user, nsock = self.sock)
  raw_send("USER #{user}\r\n", nsock)
  recv_ftp_resp(nsock)
end