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.
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.
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.
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
Ananay Arora at Titan Security Research