File Upload Vulnerabilities: The Complete Practical Guide

File Upload Race Conditions

Exploiting the window between an upload being written to disk and validated or deleted: requesting the file in that gap and parallelizing to win it.

Published

Some uploads validate safely in principle but unsafely in order: they write your file to disk first, then check it, then delete it if it is bad. That ordering opens a window. For the moment between "written" and "deleted," your malicious file exists at a reachable path, and if you request it in that gap, it runs. The file never has to survive, it only has to be reachable once.

The window between written and validated

The vulnerable pattern is write-then-check:

  1. The server saves your upload to a path to work with it.
  2. It validates the file (scans it, checks the type, re-encodes it).
  3. If the file is bad, it deletes it.

Between steps 1 and 3, the file is on disk and often reachable. A design that intended to reject your file still placed it, briefly, where you could use it. That brief existence is the whole vulnerability.

Requesting the file in the window

To exploit it, you request the stored file during the gap:

POST /upload           (your shell is written to /uploads/shell.php)
GET  /uploads/shell.php?c=id    (fire this before validation deletes it)

If your GET lands while the file still exists, the server executes it and your command runs, even though the upload was about to be rejected. The read beat the cleanup.

Parallelizing to widen your odds

A single well-timed request rarely wins, so you increase the number of attempts:

  • Fire a burst of requests for the file's URL concurrently with the upload.
  • Keep hammering across the whole window so a read lands during the gap.
  • Retry the upload-and-flood cycle until one request connects in time.

It is probabilistic. You are not timing a single shot, you are spraying requests across the window until one hits.

Temporary-file predictability

Winning the race is far easier when you know exactly where the file is. Look at how the server names and places the file during processing:

  • A predictable upload directory and a kept filename mean you can request the exact URL immediately.
  • A predictable temporary path (a fixed temp directory, a guessable name) lets you target the file even before it reaches its final location.
  • Any leak of the stored path in a response removes the guesswork entirely.

The more predictable the path, the fewer requests it takes to land one in the gap.

The broader pattern

This is a specific case of a race condition, the general class of bug where the outcome depends on the timing of operations that were assumed to be atomic. The same mindset, find a check and an action that are not truly simultaneous, then act in the gap, applies far beyond uploads. Here the gap is between writing a file and validating it.

Defensively, the fix is to validate before the file is ever reachable, for example by writing to a non-served temporary location and only moving it into place after it passes. Practice winning an upload race on a free live target.

← Back to File Upload Vulnerabilities: The Complete Practical Guide