CVE-2024-31453 (published 2024-04-09T18:15Z)

PsiTransfer is an open source, self-hosted file sharing solution. Prior to version 2.2.0, the absence of restrictions on the endpoint, which allows users to create a path for uploading a file in a file distribution, allows an attacker to add arbitrary files to the distribution. The vulnerability allows an attacker to influence those users who come to the file distribution after them and slip the victim files with a malicious or phishing signature. Version 2.2.0 contains a patch for the issue.\n\nCVE-2024-31453 allows users to violate the integrity of a file bucket and upload new files there, while the vulnerability with the number CVE-2024-31454 allows users to violate the integrity of a single file that is uploaded by another user by writing data there and not allows you to upload new files to the bucket. Thus, vulnerabilities are reproduced differently, require different security recommendations and affect different objects of the application’s business logic.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/psi-4ward/psitransfer
commit_details: repo_url: https://github.com/psi-4ward/psitransfer
author: Christoph Wiechert
author_date: 2024-03-17 18:40:50+01:00
msg: Restrict upload to a file which upload completed already
repository: repo_url: https://github.com/psi-4ward/psitransfer
repo_name: psi-4ward/psitransfer
description: Simple open source self-hosted file sharing solution.  
repo_language: JavaScript
owner: psi-4ward
file_changes: filename: endpoints.js
change_type: ModificationType.MODIFY
diff: @@ -29,6 +29,7 @@ const downloadPage = pug.compileFile(path.join(__dirname, '../public/pug/downloa
 const store = new Store(config.uploadDir);
 const Db = require('./db');
 const { createGzip } = require("zlib");
+const httpErrors = require("http-errors");
 const db = new Db(config.uploadDir, store);
 db.init();
 const app = express();
@@ -315,7 +316,7 @@ app.get(`${ config.baseUrl }files/:fid`, async (req, res, next) => {
 
 // Upload file
 app.use(`${ config.uploadAppPath }files`,
-  function(req, res, next) {
+  async function(req, res, next) {
     // Upload password protection
     if (config.uploadPass) {
       const bfTimeout = 500;
@@ -331,6 +332,22 @@ app.use(`${ config.uploadAppPath }files`,
 
     if (req.method === 'GET') return res.status(405).end();
 
+    // Restrict upload to a file which upload completed already
+    if(['POST', 'PATCH'].includes(req.method)) {
+      try {
+        const fid = req.url.substring(1);
+        const info = await store.info(fid);
+        if(!info.isPartial) {
+          return res.status(400).end('Upload already completed');
+        }
+      } catch(e) {
+        if(! e instanceof httpErrors.NotFound) {
+          console.error(e);
+          return;
+        }
+      }
+    }
+
     if (req.method === 'POST') {
       // validate meta-data
       // !! tusMeta.encode supports only strings !!
@@ -387,7 +404,6 @@ app.use(`${ config.uploadAppPath }files`,
     afterComplete: (req, upload, fid) => {
       db.add(upload.metadata.sid, upload.metadata.key, upload);
       debug(`Completed upload ${ fid }, size=${ upload.size } name=${ upload.metadata.name }`);
-
       eventBus.emit('fileUploaded', upload);
     },
   })

programming_language: TypeScript