Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/metasploit/framework/ftp/client.rb
Views: 11784
require 'metasploit/framework/tcp/client'12module Metasploit3module Framework4module Ftp5module Client6extend ActiveSupport::Concern7include Metasploit::Framework::Tcp::Client89#10# This method establishes an FTP connection to host and port specified by11# the 'rhost' and 'rport' methods. After connecting, the banner12# message is read in and stored in the 'banner' attribute.13#14def connect(global = true)15fd = super(global)1617@ftpbuff = '' unless @ftpbuff1819# Wait for a banner to arrive...20self.banner = recv_ftp_resp(fd)2122# Return the file descriptor to the caller23fd24end2526#27# This method handles establishing datasocket for data channel28#29def data_connect(mode = nil, nsock = self.sock)30if mode31res = send_cmd([ 'TYPE' , mode ], true, nsock)32return nil if not res =~ /^200/33end3435# force datasocket to renegotiate36self.datasocket.shutdown if self.datasocket != nil3738res = send_cmd(['PASV'], true, nsock)39return nil if not res =~ /^227/4041# 227 Entering Passive Mode (127,0,0,1,196,5)42if res =~ /\((\d+)\,(\d+),(\d+),(\d+),(\d+),(\d+)/43# convert port to FTP syntax44datahost = "#{$1}.#{$2}.#{$3}.#{$4}"45dataport = ($5.to_i * 256) + $6.to_i46self.datasocket = Rex::Socket::Tcp.create(47'PeerHost' => datahost,48'PeerPort' => dataport,49'Context' => { 'Msf' => framework, 'MsfExploit' => framework_module }50)51end52self.datasocket53end5455#56# This method handles disconnecting our data channel57#58def data_disconnect59self.datasocket.shutdown60self.datasocket = nil61end6263#64# Connect and login to the remote FTP server using the credentials65# that have been supplied in the exploit options.66#67def connect_login(user,pass,global = true)68ftpsock = connect(global)6970if !(user and pass)71return false72end7374res = send_user(user, ftpsock)7576if (res !~ /^(331|2)/)77return false78end7980if (pass)81res = send_pass(pass, ftpsock)82if (res !~ /^2/)83return false84end85end8687return true88end8990#91# This method logs in as the supplied user by transmitting the FTP92# 'USER <user>' command.93#94def send_user(user, nsock = self.sock)95raw_send("USER #{user}\r\n", nsock)96recv_ftp_resp(nsock)97end9899#100# This method completes user authentication by sending the supplied101# password using the FTP 'PASS <pass>' command.102#103def send_pass(pass, nsock = self.sock)104raw_send("PASS #{pass}\r\n", nsock)105recv_ftp_resp(nsock)106end107108#109# This method sends a QUIT command.110#111def send_quit(nsock = self.sock)112raw_send("QUIT\r\n", nsock)113recv_ftp_resp(nsock)114end115116#117# This method sends one command with zero or more parameters118#119def send_cmd(args, recv = true, nsock = self.sock)120cmd = args.join(" ") + "\r\n"121ret = raw_send(cmd, nsock)122if (recv)123return recv_ftp_resp(nsock)124end125return ret126end127128#129# This method transmits the command in args and receives / uploads DATA via data channel130# For commands not needing data, it will fall through to the original send_cmd131#132# For commands that send data only, the return will be the server response.133# For commands returning both data and a server response, an array will be returned.134#135# NOTE: This function always waits for a response from the server.136#137def send_cmd_data(args, data, mode = 'a', nsock = self.sock)138type = nil139# implement some aliases for various commands140if (args[0] =~ /^DIR$/i || args[0] =~ /^LS$/i)141# TODO || args[0] =~ /^MDIR$/i || args[0] =~ /^MLS$/i142args[0] = "LIST"143type = "get"144elsif (args[0] =~ /^GET$/i)145args[0] = "RETR"146type = "get"147elsif (args[0] =~ /^PUT$/i)148args[0] = "STOR"149type = "put"150end151152# fall back if it's not a supported data command153if not type154return send_cmd(args, true, nsock)155end156157# Set the transfer mode and connect to the remove server158return nil if not data_connect(mode)159160# Our pending command should have got a connection now.161res = send_cmd(args, true, nsock)162# make sure could open port163return nil unless res =~ /^(150|125) /164165# dispatch to the proper method166if (type == "get")167# failed listings just disconnect..168begin169data = self.datasocket.get_once(-1, ftp_timeout)170rescue ::EOFError171data = nil172end173else174sent = self.datasocket.put(data)175end176177# close data channel so command channel updates178data_disconnect179180# get status of transfer181ret = nil182if (type == "get")183ret = recv_ftp_resp(nsock)184ret = [ ret, data ]185else186ret = recv_ftp_resp(nsock)187end188189ret190end191192#193# This method transmits a FTP command and waits for a response. If one is194# received, it is returned to the caller.195#196def raw_send_recv(cmd, nsock = self.sock)197nsock.put(cmd)198nsock.get_once(-1, ftp_timeout)199end200201#202# This method reads an FTP response based on FTP continuation stuff203#204def recv_ftp_resp(nsock = self.sock)205found_end = false206resp = ""207left = ""208if !@ftpbuff.empty?209left << @ftpbuff210@ftpbuff = ""211end212while true213data = nsock.get_once(-1, ftp_timeout)214if not data215@ftpbuff << resp216@ftpbuff << left217return data218end219220got = left + data221left = ""222223# handle the end w/o newline case224enlidx = got.rindex(0x0a.chr)225if enlidx != (got.length-1)226if not enlidx227left << got228next229else230left << got.slice!((enlidx+1)..got.length)231end232end233234# split into lines235rarr = got.split(/\r?\n/)236rarr.each do |ln|237if not found_end238resp << ln239resp << "\r\n"240if ln.length > 3 and ln[3,1] == ' '241found_end = true242end243else244left << ln245left << "\r\n"246end247end248if found_end249@ftpbuff << left250return resp251end252end253end254255#256# This method transmits a FTP command and does not wait for a response257#258def raw_send(cmd, nsock = self.sock)259nsock.put(cmd)260end261262def ftp_timeout263raise NotImplementedError264end265266267268protected269270#271# This attribute holds the banner that was read in after a successful call272# to connect or connect_login.273#274attr_accessor :banner, :datasocket275276277end278end279end280end281282283