CVE-2020-5206: Improper Authentication (CWE-287)
value: In Opencast before 7.6 and 8.1, using a remember-me cookie with an arbitrary username can cause Opencast to assume proper authentication for that user even if the remember-me cookie was incorrect given that the attacked endpoint also allows anonymous access. This way, an attacker can, for example, fake a remember-me token, assume the identity of the global system administrator and request non-public content from the search service without ever providing any proper authentication. This problem is fixed in Opencast 7.6 and Opencast 8.1
CWE description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
CVSS v3 base score: 10
Published: 2020-01-30T22:15Z
Repository: https://github.com/opencast/opencast
Language: Java
Files changed: modules/kernel/src/main/java/org/opencastproject/kernel/security/SecurityServiceSpringImpl.java
Commit date: 2020-01-29 12:04:02 +0100
Commit message: Authentication Bypass For Endpoints With Anonymous Access

Using a remember-me cookie with an arbitrary username can cause Opencast
to assume proper authentication for that user even if the remember-me
cookie was incorrect given that the attacked endpoint also allows
anonymous access.

This way, an attacker can, for example, fake a remember-me token, assume
the identity of the global system administrator and request non-public
content from the search service without ever providing any proper
authentication.

The reason for this problem is that using a remember-me cookie will
always cause the user in the request context to be populated, even if
the cookie is invalid by now. This is usually no problem, except in
combination with anonymous access where anonymous authentication is
granted and the request may continue.

In such a case, Opencast's security service would just check that a user
existed in the request context and assume proper authentication of this
user, never checking if it's actually anonymous authentication.

This patch adds this additional check, falling back to the anonymous
user in case of anonymous authentication.

Diff: diff --git a/modules/kernel/src/main/java/org/opencastproject/kernel/security/SecurityServiceSpringImpl.java b/modules/kernel/src/main/java/org/opencastproject/kernel/security/SecurityServiceSpringImpl.java
index d6b9dbc5f3..3b643d4f5e 100644
--- a/modules/kernel/src/main/java/org/opencastproject/kernel/security/SecurityServiceSpringImpl.java
+++ b/modules/kernel/src/main/java/org/opencastproject/kernel/security/SecurityServiceSpringImpl.java
@@ -30,10 +30,11 @@ import org.opencastproject.security.api.User;
 import org.opencastproject.security.api.UserDirectoryService;
 import org.opencastproject.security.util.SecurityUtil;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
 
@@ -92,37 +93,40 @@ public class SecurityServiceSpringImpl implements SecurityService {
     if (org == null)
       throw new IllegalStateException("No organization is set in security context");
 
     User delegatedUser = delegatedUserHolder.get();
 
+    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+    if (auth instanceof AnonymousAuthenticationToken) {
+      return SecurityUtil.createAnonymousUser(org);
+    }
+
     if (delegatedUser != null) {
       return delegatedUser;
     }
 
-    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
     JaxbOrganization jaxbOrganization = JaxbOrganization.fromOrganization(org);
     if (auth != null) {
       Object principal = auth.getPrincipal();
-      if ((principal != null) && (principal instanceof UserDetails)) {
+      if ((principal instanceof UserDetails)) {
         UserDetails userDetails = (UserDetails) principal;
 
         User user = null;
 
         // If user exists, fetch it from the userDirectory
         if (userDirectory != null) {
           user = userDirectory.loadUser(userDetails.getUsername());
           if (user == null) {
-            logger.debug(
-                    "Authenticated user '{}' could not be found in any of the current UserProviders. Continuing anyway...",
-                    userDetails.getUsername());
+            logger.debug("Authenticated user '{}' could not be found in any of the current UserProviders. "
+                + "Continuing anyway...", userDetails.getUsername());
           }
         } else {
           logger.debug("No UserDirectory was found when trying to search for user '{}'", userDetails.getUsername());
         }
 
         // Add the roles (authorities) in the security context
-        Set<JaxbRole> roles = new HashSet<JaxbRole>();
+        Set<JaxbRole> roles = new HashSet<>();
         Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
         if (authorities != null) {
           for (GrantedAuthority ga : authorities) {
             roles.add(new JaxbRole(ga.getAuthority(), jaxbOrganization));
           }

Vulnerable code:     Authentication auth = SecurityContextHolder.getContext().getAuthentication();
      if ((principal != null) && (principal instanceof UserDetails)) {
            logger.debug(
                    "Authenticated user '{}' could not be found in any of the current UserProviders. Continuing anyway...",
                    userDetails.getUsername());
        Set<JaxbRole> roles = new HashSet<JaxbRole>();
Fixed code: import org.springframework.security.authentication.AnonymousAuthenticationToken;
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth instanceof AnonymousAuthenticationToken) {
      return SecurityUtil.createAnonymousUser(org);
    }

      if ((principal instanceof UserDetails)) {
            logger.debug("Authenticated user '{}' could not be found in any of the current UserProviders. "
                + "Continuing anyway...", userDetails.getUsername());
        Set<JaxbRole> roles = new HashSet<>();