SAP Cloud SDK for Python CVE-2026-89111 — OAuth Credential SSRF Sentinel Hunts

Threat Overview

Confirmed vendor disclosure (SAP GitHub advisory, October 13, 2026): CVE-2026-89111 is an OAuth2 server-side request forgery in sap-cloud-sdk for Python used by multi-tenant SAP Business Technology Platform applications. When an attacker controls the tenant_subdomain parameter, affected SDK token-URL construction can redirect an OAuth2 token request to an attacker-controlled server and transmit application client credentials. This is credential exfiltration through a token request, not a confirmed remote code execution flaw. SAP lists versions <0.45.2 as affected and 0.45.2 as patched; an allowlist of registered tenant subdomains is a vendor workaround.

Version qualification / researcher finding: The Hacker News (October 13) tested later releases and found missing validation in some Destination/Document Management Service code paths in 0.53.0–0.55.0; validation was restored in 0.55.1. SAP’s published advisory did not explicitly include those later releases as affected. Treat that later-version risk as independently reported and not yet reconciled with SAP’s CVE affected-version list.

MSSP relevance: Applications with a privileged OAuth client can leak secrets through ordinary-looking HTTPS egress. Sentinel network telemetry usually sees domains and process names, not HTTPS request bodies or client-secret values; application-level token request logs and accurate SDK dependency inventory are important.

References

Impacted Systems

  • Vendor/package: SAP Cloud SDK for Python, PyPI package sap-cloud-sdk; relevant OAuth2 token provider, Agent Gateway, Destination and Document Management Service integrations.
  • Deployment: multi-tenant applications on SAP Business Technology Platform (BTP) using the Python SDK; host OS/container runtime is not itself the vulnerability boundary.
  • Confirmed affected: package versions below 0.45.2 (SAP advisory). Vendor-listed fixed: 0.45.2.
  • Independently reported additional risk: versions 0.53.0–0.55.0 lacked tenant validation in certain SDK paths; 0.55.1+ through 0.58.4 passed the reported tests. SAP had not amended the CVE range to cover those later releases at reporting time.
  • Prerequisites: attacker controls an application-provided tenant_subdomain value; affected app uses it to request an OAuth2 token; egress to an attacker-controlled host is permitted. No particular public port or exploit URL has been verified.
  • Unaffected: applications not using the affected SDK tenant-subdomain token flow, or SDK code paths with validated tenant values. Confirm dependency and call-path details rather than assuming all SAP BTP workloads are affected.

Why this matters

A newly disclosed critical cloud application credential-exfiltration risk with broad downstream authorization impact for tenants using vulnerable SDK flows.

Exploitation Status

No publicly confirmed exploitation as of October 14, 2026 in reviewed SAP/THN reporting. Vendor confirms vulnerability and CVSS 10.0; researcher testing identifies possible later-version validation gaps. No published campaign IOCs or known attacker infrastructure.

What this hunt looks for

Proposed review of Python runtime egress to newly seen domains, proxy token-endpoint requests, tenant-subdomain application messages, SDK package changes, and Python process egress anomalies where those tables and fields are ingested into Sentinel. No customer workspace was queried during preparation.

Required logs

Microsoft Sentinel data sources referenced by these hunts: CommonSecurityLog, DeviceFileEvents, DeviceNetworkEvents, DeviceProcessEvents, Syslog. Availability must be validated per workspace; unsupported tables cannot provide visibility.

KQL Hunting Content

Hunt 1: First pass — Python processes contacting rare external token destinations

Sentinel table: DeviceNetworkEvents. Identify unexpected external connections initiated by Python application runtimes. Without SAP app inventory and expected OAuth hosts, results are hypotheses only.

let known=DeviceNetworkEvents
| where TimeGenerated between (ago(30d)..ago(2d))
| where InitiatingProcessFileName in~ ('python','python3','python.exe','python3.exe','gunicorn','gunicorn.exe','uvicorn','uvicorn.exe')
| where isnotempty(RemoteUrl)
| summarize by DeviceName, RemoteUrl;
DeviceNetworkEvents
| where TimeGenerated >= ago(2d)
| where InitiatingProcessFileName in~ ('python','python3','python.exe','python3.exe','gunicorn','gunicorn.exe','uvicorn','uvicorn.exe')
| where isnotempty(RemoteUrl) and RemotePort in (80,443,8443)
| join kind=leftanti known on DeviceName, RemoteUrl
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort, ActionType
| order by TimeGenerated desc

Hunt 2: HTTP token endpoint egress in proxy/firewall records

Sentinel table: CommonSecurityLog. Find proxy/WAF records exposing full outbound URL paths. HTTPS TLS inspection or explicit proxy URL logging is required to see token paths.

CommonSecurityLog
| where TimeGenerated >= ago(14d)
| extend Url=tostring(column_ifexists('RequestURL','')), Detail=tostring(column_ifexists('Message',''))
| where Url has_any ('/oauth/token','/oauth2/token','/oauth2/v1/token') or Detail has_any ('/oauth/token','/oauth2/token','/oauth2/v1/token')
| project TimeGenerated, DeviceVendor, DeviceProduct, SourceIP, DestinationIP, DestinationHostName, DestinationPort, DeviceAction, Url, Detail
| order by TimeGenerated desc

Hunt 3: SAP tenant-subdomain or token URL application messages

Sentinel table: Syslog. Search ingested application Syslog only; the relevant SAP BTP application may not emit these strings. Never log or return actual client secrets.

Syslog
| where TimeGenerated >= ago(14d)
| where SyslogMessage has_any ('tenant_subdomain','tenant-subdomain','XsuaaAuthProvider','TokenProvider','sap_cloud_sdk')
| project TimeGenerated, Computer, ProcessName, SeverityLevel, SyslogMessage
| order by TimeGenerated desc

Hunt 4: SAP SDK package installation and version change

Sentinel table: DeviceProcessEvents. Deployment context rather than exploitation evidence. Validate version with package inventory; process command lines are not definitive.

DeviceProcessEvents
| where TimeGenerated >= ago(30d)
| where FileName in~ ('pip','pip3','pip.exe','pip3.exe','python','python3','python.exe','python3.exe','poetry','poetry.exe')
| where ProcessCommandLine has_any ('sap-cloud-sdk','sap_cloud_sdk')
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| order by TimeGenerated desc

Hunt 5: SAP SDK library files created or changed

Sentinel table: DeviceFileEvents. Find host file activity in sap_cloud_sdk package paths; not a substitute for dependency manifest or SBOM validation.

DeviceFileEvents
| where TimeGenerated >= ago(30d)
| where FolderPath has 'sap_cloud_sdk' or FileName has 'sap_cloud_sdk'
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, ActionType, FileName, FolderPath, SHA256
| order by TimeGenerated desc

Hunt 6: Python runtime external destination spikes

Sentinel table: DeviceNetworkEvents. Identify unusual egress fan-out from application runtimes that could be consistent with redirected token requests; common SaaS integrations can look similar.

DeviceNetworkEvents
| where TimeGenerated >= ago(14d)
| where InitiatingProcessFileName in~ ('python','python3','python.exe','python3.exe','gunicorn','uvicorn')
| where RemotePort in (80,443,8443) and isnotempty(RemoteIP)
| summarize Connections=count(), UniqueDomains=dcount(RemoteUrl), Domains=make_set(RemoteUrl,15), RemoteIPs=make_set(RemoteIP,15) by DeviceName, InitiatingProcessFileName, bin(TimeGenerated,1h)
| where UniqueDomains >= 10 or Connections >= 100
| order by Connections desc

Hunt 7: Application outbound HTTP requests with unusual tenant values

Sentinel table: CommonSecurityLog. Only meaningful where an application/proxy emits the full request URL including tenant parameter. These strings are generic URL delimiter patterns, not published exploit payloads.

CommonSecurityLog
| where TimeGenerated >= ago(14d)
| extend Url=tostring(column_ifexists('RequestURL',''))
| where Url has 'tenant_subdomain' or Url has 'tenant-subdomain'
| project TimeGenerated, DeviceVendor, DeviceProduct, SourceIP, DestinationIP, DeviceAction, Url
| order by TimeGenerated desc

Detection Notes

Highest signal: outbound OAuth token POST to an unapproved host from an identified SAP SDK application, or logged malformed tenant_subdomain values. Noise: normal Python microservice egress, pip upgrades, token refreshes, shared outbound proxies. Limitations: DeviceNetworkEvents does not reveal HTTPS path or body; CommonSecurityLog RequestURL is often empty for encrypted sessions; Syslog may omit application fields; Python process activity does not establish SAP SDK presence. Do not search for or expose actual client secrets in shared query output. No campaign IOC has been published; exact exploit payloads are not known. Correlate results with app/tenant allowlists and software bill of materials.

Leave a Reply