CVE-2022-36092: Improper Authentication (CWE-287)
value: XWiki Platform Old Core is a core package for XWiki Platform, a generic wiki platform. Prior to versions 14.2 and 13.10.4, all rights checks that would normally prevent a user from viewing a document on a wiki can be bypassed using the login action and directly specified templates. This exposes title, content and comments of any document and properties of objects, though class and property name must be known. This is also exploitable on private wikis. This has been patched in versions 14.2 and 13.10.4 by properly checking view rights before loading documents and disallowing non-default templates in the login, registration and skin action. As a workaround, it would be possible to protect all templates individually by adding code to check access rights first.
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: 7.5
Published: 2022-09-08T18:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: Java
Files changed: xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/XWiki.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginAction.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginErrorAction.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginSubmitAction.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/RegisterAction.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/SkinAction.java
Commit date: 2022-03-24 18:22:39 +0100
Commit message: XWIKI-19549: Disallow template override for login, register and skin

* Also do not put a document without view rights into the context.

Diff: diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/XWiki.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/XWiki.java
index fe0defd8b5a5..cfc0f672a199 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/XWiki.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/XWiki.java
@@ -5915,35 +5915,45 @@ && getRightService().hasAccessLevel("view", XWikiRightService.GUEST_USER_FULLNAM
                 throw new XWikiException(XWikiException.MODULE_XWIKI_USER, XWikiException.ERROR_XWIKI_USER_INACTIVE,
                     "User {0} account is inactive", null, args);
             }
         }
 
-        context.put("doc", doc);
-        context.put("cdoc", doc);
-        vcontext.put("doc", doc.newDocument(context));
-        vcontext.put("cdoc", vcontext.get("doc"));
-        XWikiDocument tdoc;
-
-        // If the parameter language exists and is empty, it means we want to force loading the regular document
-        // not a translation. This should be handled later by doing a better separation between locale used in the UI
-        // and for loading the documents.
-        if ("".equals(context.getRequest().getParameter("language"))) {
-            tdoc = doc;
+        if (!"skin".equals(context.getAction()) && !this.getRightService().hasAccessLevel("view",
+            user.getFullName(), doc.getFullName(), context)) {
+            // If for some reason (e.g., login action) the user has rights for the action but no view right on the
+            // document, do not load the document into the context.
+            setPhonyDocument(reference, context, vcontext);
+            doc = context.getDoc();
+            context.put("tdoc", doc);
+            context.put("cdoc", doc);
         } else {
-            tdoc = doc.getTranslatedDocument(context);
-        }
+            context.put("doc", doc);
+            context.put("cdoc", doc);
+            vcontext.put("doc", doc.newDocument(context));
+            vcontext.put("cdoc", vcontext.get("doc"));
+            XWikiDocument tdoc;
+
+            // If the parameter language exists and is empty, it means we want to force loading the regular document
+            // not a translation. This should be handled later by doing a better separation between locale used in the UI
+            // and for loading the documents.
+            if ("".equals(context.getRequest().getParameter("language"))) {
+                tdoc = doc;
+            } else {
+                tdoc = doc.getTranslatedDocument(context);
+            }
 
-        try {
-            String rev = (String) context.get("rev");
-            if (StringUtils.isNotEmpty(rev)) {
-                tdoc = getDocument(tdoc, rev, context);
+            try {
+                String rev = (String) context.get("rev");
+                if (StringUtils.isNotEmpty(rev)) {
+                    tdoc = getDocument(tdoc, rev, context);
+                }
+            } catch (Exception ex) {
+                // Invalid version, just use the most recent one
             }
-        } catch (Exception ex) {
-            // Invalid version, just use the most recent one
+            context.put("tdoc", tdoc);
+            vcontext.put("tdoc", tdoc.newDocument(context));
         }
-        context.put("tdoc", tdoc);
-        vcontext.put("tdoc", tdoc.newDocument(context));
 
         return true;
     }
 
     /**
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginAction.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginAction.java
index 8651f9fc47b6..4a3a3b04cedf 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginAction.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginAction.java
@@ -35,24 +35,38 @@
 @Component
 @Named("login")
 @Singleton
 public class LoginAction extends XWikiAction
 {
+    private static final String LOGIN = "login";
+
     /**
      * Default constructor.
      */
     public LoginAction()
     {
         this.waitForXWikiInitialization = false;
     }
 
+    @Override
+    public boolean action(XWikiContext context) throws XWikiException
+    {
+        // Disallow template override with xpage parameter.
+        if (!LOGIN.equals(Utils.getPage(context.getRequest(), LOGIN))) {
+            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
+                String.format("Template may not be overriden with 'xpage' in [%s] action.", LOGIN));
+        }
+
+        return super.action(context);
+    }
+
     @Override
     public String render(XWikiContext context) throws XWikiException
     {
         // if the request does not come from an explicit login link (such as the login button)
         // then we consider it's coming from a redirect because a guest user tries to access a protected resource
         if (!"1".equals(context.getRequest().getParameter("loginLink"))) {
             context.getResponse().setStatus(401);
         }
-        return "login";
+        return LOGIN;
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginErrorAction.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginErrorAction.java
index 3c13976742e7..58a4cde5f592 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginErrorAction.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginErrorAction.java
@@ -35,11 +35,25 @@
 @Component
 @Named("loginerror")
 @Singleton
 public class LoginErrorAction extends XWikiAction
 {
+    private static final String LOGIN = "login";
+
+    @Override
+    public boolean action(XWikiContext context) throws XWikiException
+    {
+        // Disallow template override with xpage parameter.
+        if (!LOGIN.equals(Utils.getPage(context.getRequest(), LOGIN))) {
+            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
+                String.format("Template may not be overriden with 'xpage' in [%s] action.", LOGIN));
+        }
+
+        return super.action(context);
+    }
+
     @Override
     public String render(XWikiContext context) throws XWikiException
     {
-        return "login";
+        return LOGIN;
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginSubmitAction.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginSubmitAction.java
index e30917397698..7dabbea66396 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginSubmitAction.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/LoginSubmitAction.java
@@ -40,15 +40,29 @@
 @Component
 @Named("loginsubmit")
 @Singleton
 public class LoginSubmitAction extends XWikiAction
 {
+    private static final String LOGIN = "login";
+
+    @Override
+    public boolean action(XWikiContext context) throws XWikiException
+    {
+        // Disallow template override with xpage parameter.
+        if (!LOGIN.equals(Utils.getPage(context.getRequest(), LOGIN))) {
+            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
+                String.format("Template may not be overriden with 'xpage' in [%s] action.", LOGIN));
+        }
+
+        return super.action(context);
+    }
+
     @Override
     public String render(XWikiContext context) throws XWikiException
     {
         String msg = (String) context.get("message");
         if (StringUtils.isNotBlank(msg)) {
             context.getResponse().setStatus(HttpServletResponse.SC_FORBIDDEN);
         }
-        return "login";
+        return LOGIN;
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/RegisterAction.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/RegisterAction.java
index cc24592f99de..907df84aad10 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/RegisterAction.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/RegisterAction.java
@@ -60,10 +60,16 @@ public boolean action(XWikiContext context) throws XWikiException
     {
         XWiki xwiki = context.getWiki();
         XWikiRequest request = context.getRequest();
         XWikiResponse response = context.getResponse();
 
+        // Disallow template override with xpage parameter.
+        if (!REGISTER.equals(Utils.getPage(context.getRequest(), REGISTER))) {
+            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
+                String.format("Template may not be overriden with 'xpage' in [%s] action.", REGISTER));
+        }
+
         String register = request.getParameter(REGISTER);
         if (register != null && register.equals("1")) {
             // CSRF prevention
             if (!csrfTokenCheck(context)) {
                 return false;
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/SkinAction.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/SkinAction.java
index 7656c43d654a..dd694284bd29 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/SkinAction.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/SkinAction.java
@@ -64,10 +64,11 @@
 @Component
 @Named("skin")
 @Singleton
 public class SkinAction extends XWikiAction
 {
+
     /** Logging helper. */
     private static final Logger LOGGER = LoggerFactory.getLogger(SkinAction.class);
 
     /** Path delimiter. */
     private static final String DELIMITER = "/";
@@ -81,18 +82,32 @@ public class SkinAction extends XWikiAction
     /**
      * The encoding to use when reading text resources from the filesystem and when sending css/javascript responses.
      */
     private static final String ENCODING = "UTF-8";
 
+    private static final String DOCDOESNOTEXIST = "docdoesnotexist";
+
+    @Override
+    public boolean action(XWikiContext context) throws XWikiException
+    {
+        // Disallow template override with xpage parameter.
+        if (!DOCDOESNOTEXIST.equals(Utils.getPage(context.getRequest(), DOCDOESNOTEXIST))) {
+            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
+                "Template may not be overriden with 'xpage' in [skin] action.");
+        }
+
+        return super.action(context);
+    }
+
     @Override
     public String render(XWikiContext context) throws XWikiException
     {
         try {
             return render(context.getRequest().getPathInfo(), context);
         } catch (IOException e) {
             context.getResponse().setStatus(404);
-            return "docdoesnotexist";
+            return DOCDOESNOTEXIST;
         }
     }
 
     public String render(String path, XWikiContext context) throws XWikiException, IOException
     {
@@ -175,11 +190,11 @@ public String render(String path, XWikiContext context) throws XWikiException, I
             }
             idx = path.lastIndexOf(DELIMITER, idx - 1);
         }
         if (!found) {
             context.getResponse().setStatus(404);
-            return "docdoesnotexist";
+            return DOCDOESNOTEXIST;
         }
         return null;
     }
 
     /**

Vulnerable code:         context.put("doc", doc);
        context.put("cdoc", doc);
        vcontext.put("doc", doc.newDocument(context));
        vcontext.put("cdoc", vcontext.get("doc"));
        XWikiDocument tdoc;

        // If the parameter language exists and is empty, it means we want to force loading the regular document
        // not a translation. This should be handled later by doing a better separation between locale used in the UI
        // and for loading the documents.
        if ("".equals(context.getRequest().getParameter("language"))) {
            tdoc = doc;
            tdoc = doc.getTranslatedDocument(context);
        }
        try {
            String rev = (String) context.get("rev");
            if (StringUtils.isNotEmpty(rev)) {
                tdoc = getDocument(tdoc, rev, context);
        } catch (Exception ex) {
            // Invalid version, just use the most recent one
        context.put("tdoc", tdoc);
        vcontext.put("tdoc", tdoc.newDocument(context));
        return "login";
        return "login";
        return "login";
            return "docdoesnotexist";
            return "docdoesnotexist";
Fixed code:         if (!"skin".equals(context.getAction()) && !this.getRightService().hasAccessLevel("view",
            user.getFullName(), doc.getFullName(), context)) {
            // If for some reason (e.g., login action) the user has rights for the action but no view right on the
            // document, do not load the document into the context.
            setPhonyDocument(reference, context, vcontext);
            doc = context.getDoc();
            context.put("tdoc", doc);
            context.put("cdoc", doc);
            context.put("doc", doc);
            context.put("cdoc", doc);
            vcontext.put("doc", doc.newDocument(context));
            vcontext.put("cdoc", vcontext.get("doc"));
            XWikiDocument tdoc;

            // If the parameter language exists and is empty, it means we want to force loading the regular document
            // not a translation. This should be handled later by doing a better separation between locale used in the UI
            // and for loading the documents.
            if ("".equals(context.getRequest().getParameter("language"))) {
                tdoc = doc;
            } else {
                tdoc = doc.getTranslatedDocument(context);
            }
            try {
                String rev = (String) context.get("rev");
                if (StringUtils.isNotEmpty(rev)) {
                    tdoc = getDocument(tdoc, rev, context);
                }
            } catch (Exception ex) {
                // Invalid version, just use the most recent one
            context.put("tdoc", tdoc);
            vcontext.put("tdoc", tdoc.newDocument(context));
    private static final String LOGIN = "login";

    @Override
    public boolean action(XWikiContext context) throws XWikiException
    {
        // Disallow template override with xpage parameter.
        if (!LOGIN.equals(Utils.getPage(context.getRequest(), LOGIN))) {
            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
                String.format("Template may not be overriden with 'xpage' in [%s] action.", LOGIN));
        }

        return super.action(context);
    }

        return LOGIN;
    private static final String LOGIN = "login";

    @Override
    public boolean action(XWikiContext context) throws XWikiException
    {
        // Disallow template override with xpage parameter.
        if (!LOGIN.equals(Utils.getPage(context.getRequest(), LOGIN))) {
            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
                String.format("Template may not be overriden with 'xpage' in [%s] action.", LOGIN));
        }

        return super.action(context);
    }

        return LOGIN;
    private static final String LOGIN = "login";

    @Override
    public boolean action(XWikiContext context) throws XWikiException
    {
        // Disallow template override with xpage parameter.
        if (!LOGIN.equals(Utils.getPage(context.getRequest(), LOGIN))) {
            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
                String.format("Template may not be overriden with 'xpage' in [%s] action.", LOGIN));
        }

        return super.action(context);
    }

        return LOGIN;
        // Disallow template override with xpage parameter.
        if (!REGISTER.equals(Utils.getPage(context.getRequest(), REGISTER))) {
            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
                String.format("Template may not be overriden with 'xpage' in [%s] action.", REGISTER));
        }


    private static final String DOCDOESNOTEXIST = "docdoesnotexist";

    @Override
    public boolean action(XWikiContext context) throws XWikiException
    {
        // Disallow template override with xpage parameter.
        if (!DOCDOESNOTEXIST.equals(Utils.getPage(context.getRequest(), DOCDOESNOTEXIST))) {
            throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
                "Template may not be overriden with 'xpage' in [skin] action.");
        }

        return super.action(context);
    }

            return DOCDOESNOTEXIST;
            return DOCDOESNOTEXIST;