CVE-2024-0759: Server-Side Request Forgery (SSRF) (CWE-918)
value: Should an instance of AnythingLLM be hosted on an internal network and the attacked be explicitly granted a permission level of manager or admin, they could link-scrape internally resolving IPs of other services that are on the same network as AnythingLLM.

This would require the attacker also be able to guess these internal IPs as `/*` ranging is not possible, but could be brute forced.

There is a duty of care that other services on the same network would not be fully open and accessible via a simple CuRL with zero authentication as it is not possible to set headers or access via the link collector.
CWE description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
Published: 2024-02-27T06:15Z
Repository: https://github.com/mintplex-labs/anything-llm
Language: JavaScript
Files changed: collector/utils/url/index.js, server/endpoints/system.js
Commit date: 2024-01-19 10:49:40 -0800
Commit message: Prevent private octets from link collection for self-hosted (#626)


Diff: diff --git a/collector/utils/url/index.js b/collector/utils/url/index.js
index 419c02c2..e8cd4a76 100644
--- a/collector/utils/url/index.js
+++ b/collector/utils/url/index.js
@@ -1,11 +1,25 @@
 const VALID_PROTOCOLS = ["https:", "http:"];
+const INVALID_OCTETS = [192, 172, 10, 127];
+
+function isInvalidIp({ hostname }) {
+  const IPRegex = new RegExp(
+    /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/gi
+  );
+  if (!IPRegex.test(hostname)) return false;
+  const [octetOne, ..._rest] = hostname.split(".");
+
+  // If fails to validate to number - abort and return as invalid.
+  if (isNaN(Number(octetOne))) return true;
+  return INVALID_OCTETS.includes(Number(octetOne));
+}
 
 function validURL(url) {
   try {
     const destination = new URL(url);
     if (!VALID_PROTOCOLS.includes(destination.protocol)) return false;
+    if (isInvalidIp(destination)) return false;
     return true;
   } catch {}
   return false;
 }
 
diff --git a/server/endpoints/system.js b/server/endpoints/system.js
index 29f5ab4f..3f11bf5e 100644
--- a/server/endpoints/system.js
+++ b/server/endpoints/system.js
@@ -14,14 +14,11 @@ const {
   makeJWT,
   userFromSession,
   multiUserMode,
   queryParams,
 } = require("../utils/http");
-const {
-  setupLogoUploads,
-  setupPfpUploads,
-} = require("../utils/files/multer");
+const { setupLogoUploads, setupPfpUploads } = require("../utils/files/multer");
 const { v4 } = require("uuid");
 const { SystemSettings } = require("../models/systemSettings");
 const { User } = require("../models/user");
 const { validatedRequest } = require("../utils/middleware/validatedRequest");
 const { handleLogoUploads } = setupLogoUploads();

Vulnerable code: const {
  setupLogoUploads,
  setupPfpUploads,
} = require("../utils/files/multer");
Fixed code: const INVALID_OCTETS = [192, 172, 10, 127];

function isInvalidIp({ hostname }) {
  const IPRegex = new RegExp(
    /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/gi
  );
  if (!IPRegex.test(hostname)) return false;
  const [octetOne, ..._rest] = hostname.split(".");

  // If fails to validate to number - abort and return as invalid.
  if (isNaN(Number(octetOne))) return true;
  return INVALID_OCTETS.includes(Number(octetOne));
}
    if (isInvalidIp(destination)) return false;
const { setupLogoUploads, setupPfpUploads } = require("../utils/files/multer");