All Advisories
criticalCVE-2026-36755Titan-2026-001OH-2026-001

Authentication Bypass via Missing Await in TUS Upload Endpoint

Product
Papermark
Vendor
Papermark (mfts/papermark)
Affected
<= v0.22.0
Fixed in
Patched in main (no release yet)
CVSS
9.8
Published
2026-03-02

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary

A missing await keyword on getServerSession() in Papermark's TUS file upload endpoint causes a complete authentication bypass, allowing unauthenticated attackers to upload arbitrary files up to 2 GB.

Description

The TUS (resumable file upload) endpoint at pages/api/file/tus/[[...file]].ts in Papermark calls getServerSession() without the await keyword. Since getServerSession() is an async function, it returns a Promise object rather than the resolved session value. In JavaScript, a Promise object is always truthy regardless of whether the session actually exists. As a result, the if (!session) guard never triggers, and all requests — including completely unauthenticated ones — are passed through to the TUS upload server.

The TUS server is configured with maxSize: 2 GiB, meaning an attacker can upload files up to 2 GB in size without any credentials. The upload metadata includes a teamId field, which is also not validated against the (non-existent) session, allowing uploads to be associated with any team.

Technical Details

The vulnerable code at line 104 of pages/api/file/tus/[[...file]].ts:

// VULNERABLE: missing await
const session = getServerSession(req, res, authOptions);
if (!session) {
  return res.status(401).json({ message: "Unauthorized" });
}
return tusServer.handle(req, res);

getServerSession() returns Promise<Session | null>. Without await, the variable session holds the Promise object itself, which is always truthy. The authentication check !session evaluates to false in all cases, so execution always falls through to tusServer.handle().

Exploitation is trivial using the standard TUS resumable upload protocol — a POST request with Tus-Resumable: 1.0.0 and Upload-Length headers initiates an upload, followed by PATCH requests to stream file data. No cookies, tokens, or authentication headers are required.

Remediation

Add the await keyword before getServerSession() and ensure the handler function is async:

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const session = await getServerSession(req, res, authOptions);
  if (!session) {
    return res.status(401).json({ message: "Unauthorized" });
  }
  return tusServer.handle(req, res);
}

Additional hardening: - Validate that the authenticated user belongs to the team specified in the upload metadata - Implement per-user upload rate limiting - Add content-type validation / file type allowlisting

Timeline

  1. 2026-03-01Vulnerability discovered by OpenHack
  2. 2026-03-01Vendor pushed fix to GitHub
  3. 2026-03-02Advisory published

References

Credit: Ananay Arora at OpenHack Security Research