CVE-2024-3569 (published 2024-04-10T17:15Z)

A Denial of Service (DoS) vulnerability exists in the mintplex-labs/anything-llm repository when the application is running in 'just me' mode with a password. An attacker can exploit this vulnerability by making a request to the endpoint using the [validatedRequest] middleware with a specially crafted 'Authorization:' header. This vulnerability leads to uncontrolled resource consumption, causing a DoS condition.


Weakness information:
cwe_id: CWE-400
cwe_name: Uncontrolled Resource Consumption
description: The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
extended_description: 
            Limited resources include memory, file system storage, database connection pool entries, and CPU. If an attacker can trigger the allocation of these limited resources, but the number or size of the resources is not controlled, then the attacker could cause a denial of service that consumes all available resources. This would prevent valid users from accessing the product, and it could potentially have an impact on the surrounding environment. For example, a memory exhaustion attack against an application could slow down the application as well as its host operating system.
            There are at least three distinct scenarios which can commonly lead to resource exhaustion:
               
                  Lack of throttling for the number of allocated resources
                  Losing all references to a resource before reaching the shutdown stage
                  Not closing/returning a resource after processing
               
            Resource exhaustion problems are often result due to an incorrect implementation of the following situations:
               
                  Error conditions and other exceptional circumstances.
                  Confusion over which part of the program is responsible for releasing the resource.
               
         
         

Fix information:
repo_url: https://github.com/mintplex-labs/anything-llm
commit_details: repo_url: https://github.com/mintplex-labs/anything-llm
author: Timothy Carambat
author_date: 2024-03-26 16:47:25-07:00
msg: handle expired token being null and prevent constant rerender (#956)

* handle expired token being null and prevent constant rerender

* reset defaults
repository: repo_url: https://github.com/mintplex-labs/anything-llm
repo_name: Mintplex-Labs/anything-llm
description: The all-in-one Desktop & Docker AI application with full RAG and AI Agent capabilities.
repo_language: JavaScript
owner: mintplex-labs
file_changes: filename: index.jsx
change_type: ModificationType.MODIFY
diff: @@ -34,7 +34,7 @@ export default function PasswordModal({ mode = "single" }) {
   );
 }
 
-export function usePasswordModal() {
+export function usePasswordModal(notry = false) {
   const [auth, setAuth] = useState({
     loading: true,
     requiresAuth: false,
@@ -47,7 +47,7 @@ export function usePasswordModal() {
 
       // If the last validity check is still valid
       // we can skip the loading.
-      if (!System.needsAuthCheck()) {
+      if (!System.needsAuthCheck() && notry === false) {
         setAuth({
           loading: false,
           requiresAuth: false,
@@ -60,7 +60,7 @@ export function usePasswordModal() {
       if (settings?.MultiUserMode) {
         const currentToken = window.localStorage.getItem(AUTH_TOKEN);
         if (!!currentToken) {
-          const valid = await System.checkAuth(currentToken);
+          const valid = notry ? false : await System.checkAuth(currentToken);
           if (!valid) {
             setAuth({
               loading: false,
@@ -102,7 +102,7 @@ export function usePasswordModal() {
 
         const currentToken = window.localStorage.getItem(AUTH_TOKEN);
         if (!!currentToken) {
-          const valid = await System.checkAuth(currentToken);
+          const valid = notry ? false : await System.checkAuth(currentToken);
           if (!valid) {
             setAuth({
               loading: false,
@@ -110,6 +110,8 @@ export function usePasswordModal() {
               mode: "single",
             });
             window.localStorage.removeItem(AUTH_TOKEN);
+            window.localStorage.removeItem(AUTH_USER);
+            window.localStorage.removeItem(AUTH_TIMESTAMP);
             return;
           } else {
             setAuth({

programming_language: JavaScript
filename: index.jsx
change_type: ModificationType.MODIFY
diff: @@ -136,6 +136,6 @@ export default function PrivateRoute({ Component }) {
       <Component />
     </UserMenu>
   ) : (
-    <Navigate to={paths.login()} />
+    <Navigate to={paths.login(true)} />
   );
 }

programming_language: TypeScript
filename: index.jsx
change_type: ModificationType.MODIFY
diff: @@ -3,9 +3,11 @@ import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
 import { FullScreenLoader } from "@/components/Preloader";
 import { Navigate } from "react-router-dom";
 import paths from "@/utils/paths";
+import useQuery from "@/hooks/useQuery";
 
 export default function Login() {
-  const { loading, requiresAuth, mode } = usePasswordModal();
+  const query = useQuery();
+  const { loading, requiresAuth, mode } = usePasswordModal(!!query.get("nt"));
   if (loading) return <FullScreenLoader />;
   if (requiresAuth === false) return <Navigate to={paths.home()} />;
 

programming_language: TypeScript
filename: paths.js
change_type: ModificationType.MODIFY
diff: @@ -4,8 +4,8 @@ export default {
   home: () => {
     return "/";
   },
-  login: () => {
-    return "/login";
+  login: (noTry = false) => {
+    return `/login${noTry ? "?nt=1" : ""}`;
   },
   onboarding: {
     home: () => {

programming_language: TypeScript
filename: validatedRequest.js
change_type: ModificationType.MODIFY
diff: @@ -38,9 +38,17 @@ async function validatedRequest(request, response, next) {
 
   const bcrypt = require("bcrypt");
   const { p } = decodeJWT(token);
+
+  if (p === null) {
+    response.status(401).json({
+      error: "Token expired or failed validation.",
+    });
+    return;
+  }
+
   if (!bcrypt.compareSync(p, bcrypt.hashSync(process.env.AUTH_TOKEN, 10))) {
     response.status(401).json({
-      error: "Invalid auth token found.",
+      error: "Invalid auth credentials.",
     });
     return;
   }

programming_language: JavaScript