d45cdd61aa
Since Ruby 2.1, the respond_to? method is more strict because it does not check protected methods. So when you use send(), clearly you're ignoring this type of access control. The patch is meant to preserve this behavior to avoid potential breakage. Resolve #4507
241 lines
5.7 KiB
Ruby
241 lines
5.7 KiB
Ruby
# -*- coding: binary -*-
|
|
module Msf
|
|
|
|
require 'msf/core/exploit/tcp'
|
|
|
|
###
|
|
#
|
|
# This module exposes methods that may be useful to exploits that deal with
|
|
# clients that speak the File Transfer Protocol (FTP).
|
|
#
|
|
###
|
|
module Exploit::Remote::FtpServer
|
|
|
|
include Exploit::Remote::TcpServer
|
|
|
|
#
|
|
# Creates an instance of an FTP exploit module.
|
|
#
|
|
def initialize(info = {})
|
|
super
|
|
|
|
# Register the options that all FTP exploits may make use of.
|
|
register_options(
|
|
[
|
|
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 21 ]),
|
|
OptPort.new('PASVPORT', [ false, "The local PASV data port to listen on (0 is random)", 0 ])
|
|
], Msf::Exploit::Remote::FtpServer)
|
|
end
|
|
|
|
# (see Msf::Exploit#setup)
|
|
def setup
|
|
super
|
|
@state = {}
|
|
end
|
|
|
|
# (see TcpServer#on_client_connect)
|
|
def on_client_connect(c)
|
|
@state[c] = {
|
|
:name => "#{c.peerhost}:#{c.peerport}",
|
|
:ip => c.peerhost,
|
|
:port => c.peerport,
|
|
:user => nil,
|
|
:pass => nil,
|
|
:cwd => '/'
|
|
}
|
|
|
|
active_data_port_for_client(c, 20)
|
|
|
|
c.put "220 FTP Server Ready\r\n"
|
|
end
|
|
|
|
# Dispatches client requests to command handlers.
|
|
#
|
|
# Handlers should be named +on_client_command_*+, ending with a
|
|
# downcased FTP verb, e.g. +on_client_command_user+. If no handler
|
|
# exists for the given command, returns a generic default response.
|
|
#
|
|
# @example Handle SYST requests
|
|
# class Metasploit4 < Msf::Exploit
|
|
# include Msf::Exploit::Remote::FtpServer
|
|
# ...
|
|
# def on_client_command_syst(cmd_conn, arg)
|
|
# print_status("Responding to SYST request")
|
|
# buf = build_exploit_buffer(cmd_conn)
|
|
# cmd_conn.put("215 Unix Type: #{buf}\r\n")
|
|
# end
|
|
# end
|
|
#
|
|
# @param (see TcpServer#on_client_data)
|
|
# @return (see TcpServer#on_client_data)
|
|
def on_client_data(c)
|
|
data = c.get_once
|
|
return if not data
|
|
|
|
cmd,arg = data.strip.split(/\s+/, 2)
|
|
arg ||= ""
|
|
|
|
return if not cmd
|
|
|
|
# Allow per-command overrides
|
|
if self.respond_to?("on_client_command_#{cmd.downcase}", true)
|
|
return self.send("on_client_command_#{cmd.downcase}", c, arg)
|
|
end
|
|
|
|
case cmd.upcase
|
|
when 'USER'
|
|
@state[c][:user] = arg
|
|
c.put "331 User name okay, need password...\r\n"
|
|
return
|
|
|
|
when 'PASS'
|
|
@state[c][:pass] = arg
|
|
print_status("#{@state[c][:name]} LOGIN #{@state[c][:user]} / #{@state[c][:pass]}")
|
|
c.put "230 Login OK\r\n"
|
|
return
|
|
|
|
when 'QUIT'
|
|
c.put "221 Logout\r\n"
|
|
return
|
|
|
|
when 'SYST'
|
|
c.put "215 UNIX Type: L8\r\n"
|
|
return
|
|
|
|
when 'TYPE'
|
|
c.put "200 Type is set\r\n"
|
|
return
|
|
|
|
when 'CWD'
|
|
c.put "250 CWD command successful.\r\n"
|
|
return
|
|
|
|
when 'PWD'
|
|
c.put "257 \"#{@state[c][:cwd]}\" is current directory.\r\n"
|
|
return
|
|
|
|
when 'SIZE'
|
|
c.put "213 1\r\n"
|
|
return
|
|
|
|
when 'MDTM'
|
|
c.put "213 #{Time.now.strftime("%Y%m%d%H%M%S")}\r\n"
|
|
return
|
|
|
|
when 'PORT'
|
|
port = arg.split(',')[4,2]
|
|
if(not port and port.length == 2)
|
|
c.put("500 Illegal PORT command.\r\n")
|
|
return
|
|
end
|
|
|
|
port = port.map{|x| x.to_i}.pack('C*').unpack('n')[0]
|
|
active_data_port_for_client(c, port)
|
|
|
|
c.put "200 PORT command successful.\r\n"
|
|
return
|
|
|
|
when 'PASV'
|
|
daddr = Rex::Socket.source_address(c.peerhost)
|
|
dport = passive_data_port_for_client(c)
|
|
@state[c][:daddr] = daddr
|
|
@state[c][:dport] = dport
|
|
pasv = (daddr.split('.') + [dport].pack('n').unpack('CC')).join(',')
|
|
c.put "227 Entering Passive Mode (#{pasv})\r\n"
|
|
return
|
|
|
|
when /^(STOR|MKD|REM|DEL|RMD)$/
|
|
c.put "500 Access denied\r\n"
|
|
return
|
|
|
|
else
|
|
# Allow per-command overrides
|
|
if(self.respond_to?("on_client_unknown_command"))
|
|
return self.send("on_client_unknown_command", c, cmd.upcase, arg)
|
|
end
|
|
|
|
print_status("#{@state[c][:name]} UNKNOWN '#{cmd} #{arg}'")
|
|
c.put("500 '#{cmd} #{arg}': command not understood.\r\n")
|
|
return
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
def on_client_close(c)
|
|
@state.delete(c)
|
|
end
|
|
|
|
def passive_data_port_for_client(c)
|
|
@state[c][:mode] = :passive
|
|
|
|
if(not @state[c][:passive_sock])
|
|
s = Rex::Socket::TcpServer.create(
|
|
'LocalHost' => '0.0.0.0',
|
|
'LocalPort' => datastore['PASVPORT'].to_i,
|
|
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
|
|
)
|
|
|
|
dport = s.getsockname[2]
|
|
|
|
@state[c][:passive_sock] = s
|
|
@state[c][:passive_port] = dport
|
|
add_socket(s)
|
|
end
|
|
|
|
@state[c][:passive_port]
|
|
end
|
|
|
|
def active_data_port_for_client(c,port)
|
|
@state[c][:mode] = :active
|
|
|
|
connector = Proc.new {
|
|
host = c.peerhost.dup
|
|
sock = Rex::Socket::Tcp.create(
|
|
'PeerHost' => host,
|
|
'PeerPort' => port,
|
|
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
|
|
)
|
|
add_socket(sock)
|
|
sock
|
|
}
|
|
|
|
@state[c][:active_connector] = connector
|
|
@state[c][:active_port] = port
|
|
end
|
|
|
|
|
|
# Create a socket for the protocol data, either PASV or PORT,
|
|
# depending on the client.
|
|
#
|
|
# @see http://tools.ietf.org/html/rfc3659 RFC 3659
|
|
# @see http://tools.ietf.org/html/rfc959 RFC 959
|
|
# @param c [Socket] Control connection socket
|
|
#
|
|
# @return [Socket] A connected socket for the data connection
|
|
# @return [nil] on failure
|
|
def establish_data_connection(c)
|
|
begin
|
|
Timeout.timeout(20) do
|
|
if(@state[c][:mode] == :active)
|
|
return @state[c][:active_connector].call()
|
|
end
|
|
if(@state[c][:mode] == :passive)
|
|
c = @state[c][:passive_sock].accept
|
|
add_socket(c)
|
|
return c
|
|
end
|
|
end
|
|
|
|
rescue ::Exception => e
|
|
print_error("Failed to establish data connection: #{e.class} #{e}")
|
|
end
|
|
|
|
nil
|
|
end
|
|
|
|
end
|
|
end
|
|
|