CVE-2022-25897 (published 2022-09-08T05:15Z)

The package org.eclipse.milo:sdk-server before 0.6.8 are vulnerable to Denial of Service (DoS) when bypassing the limitations for excessive memory consumption by sending multiple CloseSession requests with the deleteSubscription parameter equal to False.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-770
cwe_name: Allocation of Resources Without Limits or Throttling
description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
extended_description: Code frequently has to work with limited resources, so programmers must be careful to ensure that resources are not consumed too quickly, or too easily.  Without use of quotas, resource limits, or other protection mechanisms, it can be easy for an attacker to consume many resources by rapidly making many requests, or causing larger resources to be used than is needed. When too many resources are allocated, or if a single resource is too large, then it can prevent the code from working correctly, possibly leading to a denial of service.
         

Fix information:
repo_url: https://github.com/eclipse/milo
commit_details: repo_url: https://github.com/eclipse/milo
author: Kevin Herron
author_date: 2022-08-24 06:14:36-07:00
msg: Allow max MonitoredItems per session to be configured via OpcUaServerConfigLimits
repository: repo_url: https://github.com/eclipse/milo
repo_name: eclipse/milo
description:  Eclipse Milo™ - an open source implementation of OPC UA (IEC 62541).
repo_language: Java
owner: eclipse
file_changes: filename: OpcUaServerConfigLimits.java
change_type: ModificationType.MODIFY
diff: @@ -112,6 +112,15 @@ public interface OpcUaServerConfigLimits {
         return uint(Integer.MAX_VALUE);
     }
 
+    /**
+     * Get the maximum number of MonitoredItems, per Session, that can be created.
+     *
+     * @return the maximum number of MonitoredItems, per Session, that can be created.
+     */
+    default UInteger getMaxMonitoredItemsPerSession() {
+        return uint(Integer.MAX_VALUE);
+    }
+
     //region ServerCapabilities
 
     default Double getMinSupportedSampleRate() {

programming_language: Java
filename: SubscriptionManager.java
change_type: ModificationType.MODIFY
diff: @@ -122,6 +122,8 @@ public class SubscriptionManager {
     private final Map<UInteger, Subscription> subscriptions = Maps.newConcurrentMap();
     private final List<Subscription> transferred = Lists.newCopyOnWriteArrayList();
 
+    private final AtomicLong monitoredItemCount = new AtomicLong(0L);
+
     private final Session session;
     private final OpcUaServer server;
 
@@ -190,6 +192,7 @@ public class SubscriptionManager {
                     eventItems -> server.getAddressSpaceManager().onEventItemsDeleted(eventItems)
                 );
 
+                monitoredItemCount.getAndUpdate(count -> count - monitoredItems.size());
                 server.getMonitoredItemCount().getAndUpdate(count -> count - monitoredItems.size());
 
                 monitoredItems.clear();
@@ -267,6 +270,7 @@ public class SubscriptionManager {
 
                 results[i] = StatusCode.GOOD;
 
+                monitoredItemCount.getAndUpdate(count -> count - deletedItems.size());
                 server.getMonitoredItemCount().getAndUpdate(count -> count - deletedItems.size());
             } else {
                 results[i] = new StatusCode(StatusCodes.Bad_SubscriptionIdInvalid);
@@ -348,13 +352,20 @@ public class SubscriptionManager {
 
             List<BaseMonitoredItem<?>> monitoredItems = new ArrayList<>();
 
+            long globalMax = server.getConfig()
+                .getLimits().getMaxMonitoredItems().longValue();
+
+            long sessionMax = server.getConfig()
+                .getLimits().getMaxMonitoredItemsPerSession().longValue();
+
             for (int i = 0; i < itemsToCreate.size(); i++) {
                 MonitoredItemCreateRequest createRequest = itemsToCreate.get(i);
 
                 try {
-                    long maxMonitoredItems = server.getConfig().getLimits().getMaxMonitoredItems().longValue();
+                    long globalCount = server.getMonitoredItemCount().incrementAndGet();
+                    long sessionCount = monitoredItemCount.incrementAndGet();
 
-                    if (server.getMonitoredItemCount().incrementAndGet() <= maxMonitoredItems) {
+                    if (globalCount <= globalMax && sessionCount <= sessionMax) {
                         BaseMonitoredItem<?> monitoredItem = createMonitoredItem(
                             createRequest,
                             subscription,
@@ -375,6 +386,7 @@ public class SubscriptionManager {
                         throw new UaException(StatusCodes.Bad_TooManyMonitoredItems);
                     }
                 } catch (UaException e) {
+                    monitoredItemCount.decrementAndGet();
                     server.getMonitoredItemCount().decrementAndGet();
 
                     createResults[i] = new MonitoredItemCreateResult(
@@ -979,6 +991,7 @@ public class SubscriptionManager {
 
                     deleteResults[i] = StatusCode.GOOD;
 
+                    monitoredItemCount.decrementAndGet();
                     server.getMonitoredItemCount().decrementAndGet();
                 }
             }
@@ -1250,6 +1263,7 @@ public class SubscriptionManager {
                     eventItems -> server.getAddressSpaceManager().onEventItemsDeleted(eventItems)
                 );
 
+                monitoredItemCount.getAndUpdate(count -> count - deletedItems.size());
                 server.getMonitoredItemCount().getAndUpdate(count -> count - deletedItems.size());
             }
 
@@ -1293,6 +1307,7 @@ public class SubscriptionManager {
                     eventItems -> server.getAddressSpaceManager().onEventItemsDeleted(eventItems)
                 );
 
+                monitoredItemCount.getAndUpdate(count -> count - monitoredItems.size());
                 server.getMonitoredItemCount().getAndUpdate(count -> count - monitoredItems.size());
 
                 monitoredItems.clear();
@@ -1312,6 +1327,8 @@ public class SubscriptionManager {
 
         if (subscription != null) {
             subscription.setStateListener(null);
+
+            monitoredItemCount.getAndUpdate(count -> count - subscription.getMonitoredItems().size());
         }
 
         return subscription;

programming_language: Java