Titan
PricingPentest
Log in
All Advisories
TITAN-2026-001Critical

Authentication Bypass via Missing Await in TUS Upload Endpoint

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.

CVSS Score
9.8 / 10.0
Product
Papermark
Vendor
Papermark (mfts/papermark)
Affected Versions
<= v0.22.0
Fixed In
Patched in main (no release yet)
Published
2026-03-02
Updated
2026-03-02
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

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

2026-03-01Vulnerability discovered by Titan Security
2026-03-01Vendor pushed fix to GitHub
2026-03-02Advisory published

References

  • Papermark GitHub Repository
  • Fix: File upload authentication bypass (PR #2096)
  • Fix Commit (c322c1a)
  • TUS Resumable Upload Protocol

Credit

Ananay Arora at Titan Security Research

AI-powered application security that finds real vulnerabilities.

Product

  • Security Agent
  • PR Integration
  • AI Autofix
  • Custom Context
  • Pricing

Services

  • Managed Pentesting

Solutions

  • Application Security
  • DevSecOps
  • Compliance
  • For Security Engineers
  • For Developers
  • For CISOs

Company

  • About
  • Wall of Fame
  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Titan Security Labs, Inc. All rights reserved.

PrivacyTerms[email protected]