SAP Security Note 3758910 | CVSS 3.0 base score 7.3 (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L) | Component MFG-MII | SAP Security Patch Day, 11 August 2026
At a glance
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:LSummary
A servlet in SAP Manufacturing Integration and Intelligence that implements the backend for energy tariff and cost catalog maintenance performed no authentication and no authorization check before executing the operation named in the incoming request. Any party able to reach the SAP NetWeaver AS Java instance over the network could read, create, modify or delete the cost catalog patterns and energy tariff definitions held by the application. SAP fixed the issue in an August 2026 Support Package patch; there is no workaround and no configuration setting that mitigates it on an unpatched system.
Affected products and versions
SAP Note 3758910 lists software component XMII in releases 15.4 and 15.5. The corrective patches named in the note are:
| Software component version | Support Package | Patch level |
|---|---|---|
| XMII 15.4 | SP001 | 000057 |
| XMII 15.5 | SP000 | 000045 |
| XMII 15.5 | SP001 | 000038 |
| XMII 15.5 | SP002 | 000010 |
The affected code sits in the MII runtime software component XMII, delivered in the software component archive XMII02, inside the /XMII web application. The administration software component MII_ADMIN (archive MIIADMIN02) does not contain the affected classes.
The builds compared for this description are XMII 15.5 SP2 patch 0 (keycounter 1000.15.5.2.0.20250508041720, pr_updateversion MII1552V.05080417) against XMII 15.5 SP2 patch 13 (keycounter 1000.15.5.2.13.20260731081708, pr_updateversion MII1552P.07310816). The note names patch level 10 as the corrective level for that Support Package, so the fixed build examined carries this correction plus three later patch levels.
Technical detail
Entry point. The vulnerable class is com.sap.xmii.servlet.CostServlet, declared in the web module descriptor of the /XMII application and mapped to the URL space /EMACost/* (web.xml lines 678-681 post-patch). It has one entry method, service(HttpServletRequest, HttpServletResponse), which reads a single request parameter naming the operation to run and dispatches through eighteen if / else if branches to eighteen handler methods. Together those handlers give complete create, read, update and delete coverage of cost catalog patterns and of energy tariff definitions with their date-effective cost rules and category relations. The selector is read via WebUtil.getParameter, which walks request.getParameterNames() and matches case-insensitively (WebUtil.java:206-218), so parameters arrive equally well on the query string or in a form body.
Input under attacker control. Each branch reads its own parameters straight from the request with no validation: numeric identifiers for catalog patterns, categories, tariffs and cost dates; tariff names and descriptions; unit-of-measure fields; and, on the create and update paths, whole JSON documents parsed with Gson and deserialized into the CostDate and CostRule persistence objects. The patch adds no input validation. The handlers, the manager layer and the DAO layer are byte-identical before and after the patch, and the entire com/sap/xmii/cost package is unchanged; the defect being fixed is exclusively the absence of an access control decision.
Path to the sink. Handlers call com.sap.xmii.cost.manager.* (CostCatManager, CostDefinitionManager, CostRuleManager, TariffCatRelationManager), which call com.sap.xmii.cost.dao.* for JDBC work against XMII_CAT_INFO, XMII_COST_DEF, XMII_COST_DATE, XMII_COST_DET, XMII_COST_RULE and XMII_TARIFF_CAT. The delete path is representative:
final CostDefinition cdf = getCostDefById(cdfId);
if (cdf != null && checkCostDefUsedByPIC(cdf.getRuleNameUp())) {
throw new IllegalArgumentException("Cannot delete tariff that was already bound to PIC!");
}
final long count = CostDefinitionDAO.deleteCostDefById(conn, cdfId);
*(CostDefinitionManager.java, lines 126-141, post-patch tree)*
That guard is a business rule about process integration, not a permission check. The DAO underneath uses parameterized statements throughout (CostDefinitionDAO.java:98-105), so no injection issue arises; the row is simply deleted on behalf of whoever asked. Searching the whole com/sap/xmii/cost package in the pre-patch tree returns no reference to a session, a user or a permission API.
The missing control. Two independent layers that the rest of the application relies on were both absent for this servlet.
*Authentication.* The /XMII application authenticates through the servlet filter com.sap.xmii.system.SecurityFilter, declared as “XMII Filter” (web.xml lines 15-18). Its doFilter calls SessionHandler.isAuthenticated(request) and, on a negative result, drives UME login or answers 401. In the pre-patch descriptor that filter is bound to thirty servlets by name and to a set of URL patterns, but to neither CostServlet nor any pattern covering /EMACost/*; the URL patterns present are /Samples/*, /CMSLogicEditor/*, /CM/*, *.jsp, *.xml, *.json, *.js, *.css and *.html. The descriptor also contains no <security-constraint> and no <login-config> (grep -c returns 0 on both descriptors), so no container-managed authentication stood in front of it either. This supports the unauthenticated wording in the SAP note and the PR:N metric.
*Authorization.* Pre-patch, service() never obtained the caller’s User object and never called a permission API. All eighteen branches, including the destructive ones, ran unconditionally.
The permission itself was not missing. EnergyTariff, with actions read, write and delete, already existed as a named constant in com.sap.xmii.Illuminator.gui.common.Permissions (EMA_TARIFF = "EnergyTariff", line 136, identical in both trees) and was already evaluated in two places: the Energy Configurations UI page gates its own rendering on it, and ProductionEventServlet.handleGetEnergyTariffPermissions (lines 817-837) evaluates all three actions for the logged-in user, only to assemble a list returned to the browser so the client can show or hide buttons. The authorization concept and its administrable permission pre-dated the fix; the Cost servlet never consulted them, and client-side button logic was the only thing between a user and the data. Because the absent call sites are compiled into the servlet and the layers beneath hold no check of their own, no role assignment, system property or init parameter could switch enforcement on. Hence SAP’s statement that no workaround exists.
What the patch changes
The fix has two parts. The deployment descriptor now binds the servlet to the authentication filter:
<filter-mapping>
<filter-name>XMII Filter</filter-name>
<servlet-name>CostServlet</servlet-name>
</filter-mapping>
*(web.xml lines 179-182, post-patch; absent from the pre-patch descriptor)*
And the servlet now resolves the session user and gates each branch:
final User user = SessionHandler.getUser(req);
...
else if (sMode.equalsIgnoreCase(<delete operation>)) {
UserSecurityHelper.checkShiftTarrifDeleteServiceUser(user);
this.handleDeleteCatPatternById(req, res, os);
}
*(CostServlet.java, line 72 and the dispatch chain that follows, post-patch tree)*
Three methods were appended to the pre-existing class com.sap.xmii.xacute.common.UserSecurityHelper (lines 209-225, post-patch), matched to the effect of each operation: twelve read, list and check branches call the read guard, four create and update branches call the read-plus-write guard, two delete branches call the delete guard. The read guard requires EnergyTariff/read; the read-plus-write guard requires read and write together; the delete guard requires delete. Each resolves to SystemPermissionManager.hasPermission("EnergyTariff", <action>, user) (SystemPermissionManager.java:23-25), delegating to the wrapped UME user, and throws UserSecurityException("USER_DOES_NOT_HAVE_SERVICE", ...) when the permission is absent. That exception extends LHException, which extends RuntimeException and, confirmed at bytecode level, implements no further interface, so it is caught by the servlet’s existing broad catch (Throwable), logged, and turned into an HTTP 500 before the handler runs. Where no xMII session exists, SessionHandler.getUser returns null (its getSession uses request.getSession(false) and never creates one) and the permission call raises a NullPointerException that lands in the same catch, so the sessionless case is blocked as well.
These changes were checked at bytecode level as well: javap on the pre-patch CostServlet.class shows zero references to UserSecurityHelper, and on the post-patch class eighteen invokestatic calls to the three guards plus one to SessionHandler.getUser.
The same patch adds the equivalent binding and guards for ShiftServlet, which is the subject of a separate note; see the references below.
Verification for defenders
1. Confirm the patch level. This is the primary check, since no switch changes behaviour without it. In the AS Java system information (SAP NetWeaver Administrator, or the component information page), read the version of software component XMII and compare against the table above.
2. Confirm the servlet is behind the authentication filter. In the deployed /XMII application, inspect WEB-INF/web.xml and confirm a <filter-mapping> binding filter name XMII Filter to servlet name CostServlet. On an unpatched system that mapping is absent. It is the clearest positive marker that the corrected web module is the one actually running.
3. Confirm the permission gate functionally. With an already-authenticated test account that deliberately holds no EnergyTariff permission, open the Energy Configurations screen and attempt to list tariffs. On a patched system the backend call fails with a USER_DOES_NOT_HAVE_SERVICE error instead of returning data. The denial is the positive confirmation, and the test is an ordinary use of the product rather than a crafted request.
4. Review who holds the permission. After patching, this servlet is governed by EnergyTariff with actions read, write and delete. Review its assignment in the xMII permission editor and confirm only genuine tariff maintainers hold it. Grant on that permission name; the service name printed in the denial message is not a reliable guide to the permission to assign, so do not search on it.
5. Plan for functional fallout. SAP states that after the fix, users of the affected configuration screen require additional roles and authorizations to work without errors. Identify the tariff maintenance population before patching so the denials that follow are anticipated rather than treated as an outage. Note that even the read operations now require EnergyTariff/read.
Detection
Post-patch, denied calls are visible. CostServlet logs through the COST log category, resolving to /Applications/XMII/COST (LogCategory.java lines 48 and 81). A denial produces an error entry keyed Catalog Service Error carrying the UserSecurityException and the USER_DOES_NOT_HAVE_SERVICE text, which names the denied user, followed by a Cost servlet Error entry, with HTTP 500 returned to the client. A burst of such denials from known internal accounts immediately after patching is the expected consequence of the fix. Denials naming accounts with no business in tariff maintenance, or from unexpected network ranges, warrant investigation.
Retrospective hunting is weak. The pre-patch servlet logged nothing security-relevant, returned ordinary HTTP 200 responses on success, and the cost and tariff managers write no audit trail. The only retrospective evidence is generic ICM and HTTP access logging showing requests against the /EMACost/* URL space, correlated with source addresses and with the hours when tariff maintenance would legitimately occur. Where those logs have rolled over, the alternative is a data-side review: compare the current cost and tariff tables, and their date-effective rules, against the last known good backup from before the exposure window. Exposure is much reduced where the AS Java instance was never reachable from outside the plant network, which is the normal MII posture, but that reduction is a function of network placement alone.
References
- SAP Security Note 3758910, “[CVE-2026-44764] Missing Authorization Check in SAP Manufacturing Integration and Intelligence”: https://me.sap.com/notes/3758910
- CVE-2026-44764
- Companion issue fixed by the same code change but tracked separately: SAP Security Note 3758657, CVE-2026-44765, covering the equivalent gap on the shift scheduling servlet: https://me.sap.com/notes/3758657
- SAP Security Patch Day, August 2026
SAP records this issue as externally reported and credits the reporter in the note. This document describes the published vulnerability and the shipped fix; it makes no claim of discovery.
This is one of six SAP MII vulnerabilities corrected on the August 2026 patch day. The full cycle, including the other 19 notes, is covered in the SAP Security Patch Day August 2026 advisory.
Custom code carries the same defect classes
Missing authorization checks, injection into dynamically assembled calls and unvalidated file paths are the recurring findings in customer ABAP as well as in vendor code. The RedRays ABAP Code Scanner reads your own objects and reports them at the line that causes them.
This advisory summarises publicly relevant facts about an SAP Security Note that SAP has already released, together with an analysis of the shipped correction. It is not a substitute for the note itself; customers should always refer to the original note in the SAP Support Portal for authoritative guidance on affected releases and required patch levels. No exploit code is published. RedRays is an independent SAP security vendor and is not affiliated with SAP SE.
