CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/tftp/tftp_transfer_util.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
7
class MetasploitModule < Msf::Auxiliary
8
include Rex::Proto::TFTP
9
include Msf::Auxiliary::Report
10
11
def initialize
12
super(
13
'Name' => 'TFTP File Transfer Utility',
14
'Description' => %q{
15
This module will transfer a file to or from a remote TFTP server.
16
Note that the target must be able to connect back to the Metasploit system,
17
and NAT traversal for TFTP is often unsupported.
18
19
Two actions are supported: "Upload" and "Download," which behave as one might
20
expect -- use 'set action Actionname' to use either mode of operation.
21
22
If "Download" is selected, at least one of FILENAME or REMOTE_FILENAME
23
must be set. If "Upload" is selected, either FILENAME must be set to a valid path to
24
a source file, or FILEDATA must be populated. FILENAME may be a fully qualified path,
25
or the name of a file in the Msf::Config.local_directory or Msf::Config.data_directory.
26
},
27
'Author' => [ 'todb' ],
28
'References' =>
29
[
30
['URL', 'http://www.faqs.org/rfcs/rfc1350.html'],
31
['URL', 'http://www.networksorcery.com/enp/protocol/tftp.htm']
32
],
33
'Actions' => [
34
[ 'Download', 'Description' => "Download REMOTE_FILENAME as FILENAME from the server."],
35
[ 'Upload', 'Description' => "Upload FILENAME as REMOTE_FILENAME to the server."]
36
],
37
'DefaultAction' => 'Upload',
38
'License' => MSF_LICENSE
39
)
40
register_options([
41
OptString.new( 'FILENAME', [false, "The local filename" ]),
42
OptString.new( 'FILEDATA', [false, "Data to upload in lieu of a real local file." ]),
43
OptString.new( 'REMOTE_FILENAME', [false, "The remote filename"]),
44
OptAddress.new('RHOST', [true, "The remote TFTP server"]),
45
OptPort.new( 'LPORT', [false, "The local port the TFTP client should listen on (default is random)" ]),
46
OptAddressLocal.new('LHOST', [false, "The local address the TFTP client should bind to"]),
47
OptString.new( 'MODE', [false, "The TFTP mode; usual choices are netascii and octet.", "octet"]),
48
Opt::RPORT(69)
49
])
50
end
51
52
def mode
53
datastore['MODE'] || "octet"
54
end
55
56
def remote_file
57
return datastore['REMOTE_FILENAME'] if datastore['REMOTE_FILENAME']
58
return ::File.split(datastore['FILENAME']).last if datastore['FILENAME']
59
end
60
61
def rport
62
datastore['RPORT'] || 69
63
end
64
65
def rhost
66
datastore['RHOST']
67
end
68
69
# Used only to store loot, doesn't actually have any semantic meaning
70
# for the TFTP protocol.
71
def datatype
72
case datastore['MODE']
73
when "netascii"
74
"text/plain"
75
else
76
"application/octet-stream"
77
end
78
end
79
80
def file
81
if action.name == "Upload"
82
fdata = datastore['FILEDATA'].to_s
83
fname = datastore['FILENAME'].to_s
84
if not fdata.empty?
85
fdata_decorated = "DATA:#{datastore['FILEDATA']}"
86
elsif ::File.readable? fname
87
fname
88
else
89
fname_local = ::File.join(Msf::Config.local_directory,fname)
90
fname_data = ::File.join(Msf::Config.data_directory,fname)
91
return fname_local if ::File.file?(fname_local) and ::File.readable?(fname_local)
92
return fname_data if ::File.file?(fname_data) and ::File.readable?(fname_data)
93
return nil # Couldn't find it, giving up.
94
end
95
else # "Download"
96
fname = ::File.split(datastore['FILENAME'] || datastore['REMOTE_FILENAME']).last rescue nil
97
end
98
end
99
100
# Experimental message prepending thinger. Might make it up into the
101
# standard Metasploit lib like vprint_status and friends.
102
def rtarget(ip=nil)
103
if (ip or rhost) and rport
104
[(ip || rhost),rport].map {|x| x.to_s}.join(":") << " "
105
elsif (ip or rhost)
106
"#{rhost} "
107
else
108
""
109
end
110
end
111
112
# This all happens before run(), and should give an idea on how to use
113
# the TFTP client mixin. Essentially, you create an instance of the
114
# Rex::Proto::TFTP::Client class, fill it up with the relevant host and
115
# file data, set it to either :upload or :download, then kick off the
116
# transfer as you like.
117
def setup
118
@lport = datastore['LPORT'] || (1025 + rand(0xffff-1025))
119
@lhost = datastore['LHOST'] || "0.0.0.0"
120
@local_file = file
121
@remote_file = remote_file
122
123
@tftp_client = Rex::Proto::TFTP::Client.new(
124
"LocalHost" => @lhost,
125
"LocalPort" => @lport,
126
"PeerHost" => rhost,
127
"PeerPort" => rport,
128
"LocalFile" => @local_file,
129
"RemoteFile" => @remote_file,
130
"Mode" => mode,
131
"Context" => {'Msf' => self.framework, 'MsfExploit' => self},
132
"Action" => action.name.to_s.downcase.intern
133
)
134
end
135
136
def run
137
case action.name
138
when 'Upload'
139
if file
140
run_upload()
141
else
142
print_error "Need at least a local file name or file data to upload."
143
return
144
end
145
when 'Download'
146
if remote_file
147
run_download()
148
else
149
print_error "Need at least a remote file name to download."
150
return
151
end
152
else
153
print_error "Unknown action: '#{action.name}'"
154
end
155
while not @tftp_client.complete
156
select(nil,nil,nil,1)
157
print_status [rtarget,"TFTP transfer operation complete."].join
158
save_downloaded_file() if action.name == 'Download'
159
break
160
end
161
end
162
163
# Run in case something untoward happened with the connection and the
164
# client object didn't get stopped on its own. This can happen with
165
# transfers that got interrupted or malformed (like sending a 0 byte
166
# file).
167
def cleanup
168
if @tftp_client and @tftp_client.respond_to? :complete
169
while not @tftp_client.complete
170
select(nil,nil,nil,1)
171
vprint_status "Cleaning up the TFTP client ports and threads."
172
@tftp_client.stop
173
end
174
end
175
end
176
177
def run_upload
178
print_status "Sending '#{file}' to #{rhost}:#{rport} as '#{remote_file}'"
179
ret = @tftp_client.send_write_request { |msg| print_tftp_status(msg) }
180
end
181
182
def run_download
183
print_status "Receiving '#{remote_file}' from #{rhost}:#{rport} as '#{file}'"
184
ret = @tftp_client.send_read_request { |msg| print_tftp_status(msg) }
185
end
186
187
def save_downloaded_file
188
print_status "Saving #{remote_file} as '#{file}'"
189
fh = @tftp_client.recv_tempfile
190
data = File.open(fh,"rb") {|f| f.read f.stat.size} rescue nil
191
if data and not data.empty?
192
unless framework.db.active
193
print_status "No database connected, so not actually saving the data:"
194
print_line data
195
end
196
this_service = report_service(
197
:host => rhost,
198
:port => rport,
199
:name => "tftp",
200
:proto => "udp"
201
)
202
store_loot("tftp.file",datatype,rhost,data,file,remote_file,this_service)
203
else
204
print_status [rtarget,"Did not find any data, so nothing to save."].join
205
end
206
fh.unlink rescue nil # Windows often complains about unlinking tempfiles
207
end
208
209
def print_tftp_status(msg)
210
case msg
211
when /Aborting/, /errors.$/
212
print_error [rtarget,msg].join
213
when /^WRQ accepted/, /^Sending/, /complete!$/
214
print_good [rtarget,msg].join
215
else
216
vprint_status [rtarget,msg].join
217
end
218
end
219
end
220
221