CVE-2022-39387 (published 2022-11-04T19:15Z)

XWiki OIDC has various tools to manipulate OpenID Connect protocol in XWiki. Prior to version 1.29.1, even if a wiki has an OpenID provider configured through its xwiki.properties, it is possible to provide a third party provider its details through request parameters. One can then bypass the XWiki authentication altogether by specifying its own provider through the oidc.endpoint.* request parameters (or by using an XWiki-based OpenID provider with oidc.xwikiprovider. With the same approach, one could also provide a specific group mapping through oidc.groups.mapping that would make his user automatically part of the XWikiAdminGroup. This issue has been patched, please upgrade to 1.29.1. There is no workaround, an upgrade of the authenticator is required.

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

Weakness information:
cwe_id: CWE-287
cwe_name: Improper Authentication
description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
extended_description: 
            
			
         
         

Fix information:
repo_url: https://github.com/xwiki-contrib/oidc
commit_details: repo_url: https://github.com/xwiki-contrib/oidc
author: Thomas Mortagne
author_date: 2022-01-27 15:16:30+01:00
msg: OIDC-118: Fix bad handling of request parameters
repository: repo_url: https://github.com/xwiki-contrib/oidc
repo_name: xwiki-contrib/oidc
description: Make XWiki an identity provider that can be reused by any application
repo_language: Java
owner: xwiki-contrib
file_changes: filename: OIDCClientConfiguration.java
change_type: ModificationType.MODIFY
diff: @@ -37,11 +37,11 @@ import javax.inject.Singleton;
 import javax.servlet.http.HttpSession;
 
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.SetUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.joda.time.LocalDateTime;
 import org.slf4j.Logger;
 import org.xwiki.component.annotation.Component;
-import org.xwiki.configuration.ConfigurationSource;
 import org.xwiki.container.Container;
 import org.xwiki.container.Request;
 import org.xwiki.container.Session;
@@ -234,6 +234,8 @@ public class OIDCClientConfiguration extends OIDCConfiguration
 
     private static final String XWIKI_GROUP_PREFIX = "XWiki.";
 
+    private static final Set<String> SAFE_PROPERTIES = SetUtils.hashSet(PROP_SKIPPED);
+
     @Inject
     private InstanceIdManager instance;
 
@@ -249,10 +251,6 @@ public class OIDCClientConfiguration extends OIDCConfiguration
     @Inject
     private Logger logger;
 
-    @Inject
-    // TODO: store configuration in custom objects
-    private ConfigurationSource configuration;
-
     private HttpSession getHttpSession()
     {
         Session session = this.container.getSession();
@@ -335,10 +333,12 @@ public class OIDCClientConfiguration extends OIDCConfiguration
     @Override
     protected <T> T getProperty(String key, Class<T> valueClass)
     {
-        // Get property from request
-        String requestValue = getRequestParameter(key);
-        if (requestValue != null) {
-            return this.converter.convert(valueClass, requestValue);
+        if (SAFE_PROPERTIES.contains(key)) {
+            // Get property from request
+            String requestValue = getRequestParameter(key);
+            if (requestValue != null) {
+                return this.converter.convert(valueClass, requestValue);
+            }
         }
 
         // Get property from session
@@ -354,10 +354,12 @@ public class OIDCClientConfiguration extends OIDCConfiguration
     @Override
     protected <T> T getProperty(String key, T def)
     {
-        // Get property from request
-        String requestValue = getRequestParameter(key);
-        if (requestValue != null) {
-            return this.converter.convert(def.getClass(), requestValue);
+        if (SAFE_PROPERTIES.contains(key)) {
+            // Get property from request
+            String requestValue = getRequestParameter(key);
+            if (requestValue != null) {
+                return this.converter.convert(def.getClass(), requestValue);
+            }
         }
 
         // Get property from session
@@ -426,9 +428,19 @@ public class OIDCClientConfiguration extends OIDCConfiguration
             uri = new URI(uriString);
         }
 
-        // If we still don't have any endpoint URI, return null
+        // If we still don't have any endpoint URI, try the request
         if (uri == null) {
-            return null;
+            uriString = getRequestParameter(PROPPREFIX_ENDPOINT + hint);
+            if (uriString == null) {
+                String provider = getRequestParameter(PROP_XWIKIPROVIDER);
+                if (provider == null) {
+                    return null;
+                }
+
+                uri = this.manager.createEndPointURI(provider, hint);
+            } else {
+                uri = new URI(uriString);
+            }
         }
 
         // Find custom headers

programming_language: Java
filename: OIDCClientConfigurationTest.java
change_type: ModificationType.MODIFY
diff: @@ -19,8 +19,10 @@
  */
 package org.xwiki.contrib.oidc.auth.internal;
 
+import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.net.URL;
 import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -28,11 +30,19 @@ import java.util.Map;
 
 import org.junit.jupiter.api.Test;
 import org.xwiki.configuration.ConfigurationSource;
+import org.xwiki.container.Container;
+import org.xwiki.container.servlet.ServletRequest;
+import org.xwiki.contrib.oidc.provider.internal.OIDCManager;
+import org.xwiki.contrib.oidc.provider.internal.endpoint.TokenOIDCEndpoint;
+import org.xwiki.properties.ConverterManager;
 import org.xwiki.test.junit5.mockito.ComponentTest;
 import org.xwiki.test.junit5.mockito.InjectMockComponents;
 import org.xwiki.test.junit5.mockito.MockComponent;
 
+import com.xpn.xwiki.web.XWikiServletRequestStub;
+
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.when;
@@ -51,6 +61,15 @@ class OIDCClientConfigurationTest
     @MockComponent
     private ConfigurationSource sourceConfiguration;
 
+    @MockComponent
+    private Container container;
+
+    @MockComponent
+    private OIDCManager manager;
+
+    @MockComponent
+    private ConverterManager converterManager;
+
     @Test
     void getUserInfoOIDCEndpoint() throws URISyntaxException
     {
@@ -78,6 +97,39 @@ class OIDCClientConfigurationTest
 
         assertEquals(uri, endpoint.getURI());
         assertEquals(headers, endpoint.getHeaders());
+    }
+
+    @Test
+    void getPropertyOrder() throws MalformedURLException, URISyntaxException
+    {
+        String provider = "http://urlprovider";
+        URI urlauthorization = new URI("http://urlauthorization");
+
+        XWikiServletRequestStub requestStub = new XWikiServletRequestStub(new URL("http://url"), null);
+
+        when(this.container.getRequest()).thenReturn(new ServletRequest(requestStub));
+        when(this.sourceConfiguration.getProperty(OIDCClientConfiguration.PROP_SKIPPED, false)).thenReturn(false);
+
+        assertFalse(this.configuration.isSkipped());
+        assertNull(this.configuration.getXWikiProvider());
+        assertNull(this.configuration.getAuthorizationOIDCEndpoint());
+        assertNull(this.configuration.getAuthorizationOIDCEndpoint());
+        assertNull(this.configuration.getTokenOIDCEndpoint());
+
+        requestStub.put(OIDCClientConfiguration.PROP_SKIPPED, "true");
+        when(this.converterManager.convert(Boolean.class, "true")).thenReturn(true);
+
+        assertTrue(this.configuration.isSkipped());
+
+        requestStub.put(OIDCClientConfiguration.PROP_GROUPS_ALLOWED, "true");
+
+        assertNull(this.configuration.getAllowedGroups());
+
+        requestStub.put(OIDCClientConfiguration.PROP_XWIKIPROVIDER, provider.toString());
+        requestStub.put(OIDCClientConfiguration.PROP_ENDPOINT_AUTHORIZATION, urlauthorization.toString());
+        when(this.manager.createEndPointURI(provider, TokenOIDCEndpoint.HINT)).thenReturn(new URI(provider));
 
+        assertEquals(urlauthorization, this.configuration.getAuthorizationOIDCEndpoint().getURI());
+        assertEquals(provider, this.configuration.getTokenOIDCEndpoint().getURI().toString());
     }
 }

programming_language: Java