Nov 10, 2026·10 min read·1 visit
An Insecure Direct Object Reference (IDOR) and weak token generation flaw in FileGator (< 7.15.0) allows authenticated users to download and consume other users' temporary batch ZIP archives via GET /batchdownload by supplying a predicted or intercepted uniqid token.
FileGator versions prior to 7.15.0 contain a missing authorization check (CWE-862) and predictable identifier generation flaw (CWE-330) in the batch download implementation. When a user initiates a batch download, FileGator generates a temporary ZIP archive identified by PHP's non-cryptographic uniqid() function without binding the resulting identifier to the creating user's session. Consequently, any authenticated user with read, download, and batchdownload permissions who obtains or predicts an active archive identifier can retrieve another user's private files via GET /batchdownload, bypassing per-user home directory isolation and deleting the victim's temporary archive upon download completion.
FileGator is a multi-user PHP web file manager that supports isolated per-user home directories and granular permission controls (read, write, download, batchdownload). In deployments where administrators configure separate home directories for individual users, the application relies on controller-level authorization and storage path prefixes to prevent cross-user file access.
In versions prior to 7.15.0, the batch file download workflow exposes an unverified direct reference to shared temporary archive files via the GET /batchdownload endpoint, handled by DownloadController::batchDownloadStart(). While the initial archive creation request (POST /batchdownload) validates that the requesting user has access to the target files within their own repository scope, the subsequent download retrieval step fails to verify that the session requesting the archive is the same session that created it.
This missing authorization flaw (CWE-862) is compounded by the use of insufficiently random values (CWE-330) during archive creation. Specifically, ZipArchiver::createArchive() generates temporary archive identifiers using PHP's built-in uniqid() function, which derives a 13-character hexadecimal string directly from the current system timestamp in microseconds. Furthermore, because the identifier is transmitted in the URL query string (GET /batchdownload?uniqid=<id>), it is routinely recorded in web server access logs, proxy logs, and browser history.
An authenticated user possessing standard read, download, and batchdownload permissions who intercepts, reads from shared logs, or predicts an active uniqid value can request the archive before the legitimate owner retrieves it. Successful exploitation allows the attacker to cross home-directory boundaries and exfiltrate the victim's archived files, while simultaneously deleting the temporary archive from the server and causing the victim's download attempt to fail.
The root cause of CVE-2026-94618 lies in the decoupled, stateless handoff between the two endpoints governing FileGator's batch download lifecycle inside backend/Controllers/DownloadController.php and backend/Services/Archiver/Adapters/ZipArchiver.php.
FileGator implements batch downloading as a two-step asynchronous sequence:
POST /batchdownload): The client submits a JSON array of file and directory paths to DownloadController::batchDownloadCreate(). The controller iterates through the items, packs them into a temporary ZIP file managed by TmpfsInterface, and closes the session via $this->session->save() before returning the generated $uniqid to the client.GET /batchdownload): The client initiates a browser download navigation to /batchdownload?uniqid=<id>, invoking DownloadController::batchDownloadStart(). This method sanitizes the alphanumeric uniqid parameter, opens a read stream from the shared temporary filesystem ($tmpfs->readStream($uniqid)), streams the binary payload to the client, and immediately deletes the file ($tmpfs->remove($uniqid)).Two distinct code-level defects occur during this sequence. First, batchDownloadCreate() never binds the generated $uniqid to $this->session. Although FileGator maintains an authenticated session state for each user, the temporary filesystem (TmpfsInterface) operates as a global, flat namespace shared across all application users. When batchDownloadStart() executes, the router middleware confirms that the caller holds the general batchdownload permission, but the controller method performs no verification that the caller's session owns the specific $uniqid requested.
Second, ZipArchiver::createArchive() calls $this->uniqid = uniqid(); without additional entropy. In PHP, uniqid() with default parameters formats the current Unix timestamp (seconds and microseconds) into an 8-hex-character second prefix followed by a 5-hex-character microsecond suffix. Because this value is deterministic and strictly tied to the server clock, an attacker who knows or triggers the approximate time of a victim's archive creation faces a narrow search space of at most $10^6$ microsecond values per second, or significantly fewer if network latency and server response timing are measured.
> [!NOTE]
> Because batchDownloadStart() invokes $tmpfs->remove($uniqid) inside the StreamedResponse callback immediately after streaming finishes, every temporary archive is a single-use resource. If an attacker retrieves the archive first, the underlying file is unlinked from disk before the victim's browser request arrives.
In versions of FileGator prior to 7.15.0, backend/Controllers/DownloadController.php handled archive creation and streaming without recording session ownership metadata:
// Vulnerable code in backend/Controllers/DownloadController.php (prior to 7.15.0)
public function batchDownloadCreate(Request $request, Response $response, ArchiverInterface $archiver)
{
$items = $request->input('items', []);
$uniqid = $archiver->createArchive($this->storage);
// Session is closed without storing any association with $uniqid
$this->session->save();
foreach ($items as $item) {
// ... adds files/directories from $this->storage to archive ...
}
$archiver->storeArchive();
return $response->json(['uniqid' => $uniqid]);
}
public function batchDownloadStart(Request $request, StreamedResponse $streamedResponse, TmpfsInterface $tmpfs)
{
$uniqid = (string) preg_replace('/[^0-9a-zA-Z_]/', '', (string) $request->input('uniqid'));
// Directly opens stream from shared tmpfs without checking session ownership
$file = $tmpfs->readStream($uniqid);
$streamedResponse->setCallback(function () use ($file, $tmpfs, $uniqid) {
// ... streams $file in chunks ...
$tmpfs->remove($uniqid);
});
$streamedResponse->send();
}Commit 638737e560cbf20cbeb5da4e4e6c974f8a080531 addresses both the authorization failure and the weak token generation. First, in backend/Services/Archiver/Adapters/ZipArchiver.php, the predictable uniqid() call is replaced with a 256-bit cryptographically secure pseudorandom identifier generated via random_bytes(32):
// Patched code in backend/Services/Archiver/Adapters/ZipArchiver.php
public function createArchive(Storage $storage): string
{
// Replaces predictable uniqid() with 64-character hex string (256 bits of entropy)
$this->uniqid = bin2hex(random_bytes(32));
$this->archive = new Flysystem(
new ZipAdapter($this->tmpfs->getFileLocation($this->uniqid))
);
$this->storage = $storage;
return $this->uniqid;
}Second, backend/Controllers/DownloadController.php introduces session-scoped archive tracking using the batch_download_archives session key (BATCH_ARCHIVES_SESSION_KEY). Before batchDownloadCreate() closes the session to perform potentially slow I/O archiving operations, it registers the generated $uniqid in the active session via rememberBatchArchive():
// Patched code in backend/Controllers/DownloadController.php
const BATCH_ARCHIVES_SESSION_KEY = 'batch_download_archives';
const BATCH_ARCHIVES_LIMIT = 20;
public function batchDownloadCreate(Request $request, Response $response, ArchiverInterface $archiver)
{
$items = $request->input('items', []);
$uniqid = $archiver->createArchive($this->storage);
// Bind archive ID to the creating session before closing session lock
$this->rememberBatchArchive($uniqid);
// close session
$this->session->save();
// ... archive packing logic ...
}
public function batchDownloadStart(Request $request, Response $response, StreamedResponse $streamedResponse, TmpfsInterface $tmpfs)
{
$uniqid = (string) preg_replace('/[^0-9a-zA-Z_]/', '', (string) $request->input('uniqid'));
// Verify the current session created the requested archive
if (! $this->ownsBatchArchive($uniqid)) {
return $response->redirect('/');
}
$file = $tmpfs->readStream($uniqid);
// ... streaming and removal logic ...
}
protected function rememberBatchArchive(string $uniqid)
{
$archives = (array) $this->session->get(self::BATCH_ARCHIVES_SESSION_KEY, []);
$archives[$uniqid] = true;
// Cap stored identifiers at 20 to prevent unbounded session growth
if (count($archives) > self::BATCH_ARCHIVES_LIMIT) {
$archives = array_slice($archives, -self::BATCH_ARCHIVES_LIMIT, null, true);
}
$this->session->set(self::BATCH_ARCHIVES_SESSION_KEY, $archives);
}
protected function ownsBatchArchive(string $uniqid): bool
{
if ($uniqid === '') {
return false;
}
$archives = (array) $this->session->get(self::BATCH_ARCHIVES_SESSION_KEY, []);
return isset($archives[$uniqid]);
}The fix is complete and defense-in-depth is properly applied. By binding the identifier to the session prior to calling $this->session->save(), the patch preserves FileGator's concurrency behavior (avoiding PHP session lock contention during large ZIP creation) while ensuring that only the originating session can pass ownsBatchArchive(). Furthermore, if an unauthorized user submits a valid uniqid, batchDownloadStart() returns an HTTP 302 redirect to / before $tmpfs->readStream() or $tmpfs->remove() is reached, preventing unauthorized deletion of the victim's pending archive.
Exploiting CVE-2026-94618 requires a multi-user FileGator installation (< 7.15.0) where the attacker possesses a valid low-privileged account granted read, download, and batchdownload permissions. The vulnerability is triggered when another user (the victim) creates a batch archive of files inside their private home directory.
When the victim selects files and initiates a batch download, their browser sends a POST /batchdownload request:
POST /batchdownload HTTP/1.1
Host: filegator.example.com
Cookie: filegator_session=victim_session_id
Content-Type: application/json
{
"items": [
{
"type": "file",
"path": "/secret.txt",
"name": "secret.txt",
"time": 1754982000
}
]
}The server packages /john/secret.txt into the temporary directory and responds with the predictable uniqid:
{
"data": {
"uniqid": "66b961234a5b6"
}
}To exploit the flaw, the attacker acquires or guesses this 13-character hexadecimal uniqid within the window between $archiver->storeArchive() completing and the victim's browser completing GET /batchdownload (or in cases where the victim's download is interrupted or logged). For larger archives, or if the attacker races the victim's second request by polling predicted microsecond uniqid values, the attacker submits an authenticated GET request using their own session cookie:
GET /batchdownload?uniqid=66b961234a5b6 HTTP/1.1
Host: filegator.example.com
Cookie: filegator_session=attacker_session_idIn vulnerable versions, the server responds with HTTP/1.1 200 OK, Content-Type: application/octet-stream, and Content-Disposition: attachment; filename=archive.zip, streaming the victim's ZIP file to the attacker.
The official patch commit (638737e560cbf20cbeb5da4e4e6c974f8a080531) includes a functional regression test (tests/backend/Feature/BatchDownloadAuthorizationTest.php) that reproduces this exact cross-user attack path:
public function testCannotDownloadAnotherUsersBatchArchive()
{
// 1. Victim (john@example.com) creates a batch archive from isolated /john directory
$uniqid = $this->createSecretBatchArchiveAs('john@example.com', 'john123', '/john');
// 2. Attacker (jack@example.com) authenticates with separate /jack home directory
$this->signIn('jack@example.com', 'jack123');
// 3. Attacker requests John's archive using John's uniqid
$this->sendRequest('GET', '/batchdownload', [
'uniqid' => $uniqid,
]);
// Patched behavior: returns 302 redirect to '/' and preserves victim's file on disk
$this->assertStatus(302);
$this->assertEquals('/', $this->response->headers->get('Location'));
$this->assertFileExists(TEST_TMP_PATH.$uniqid);
}CVE-2026-94618 is assigned a CVSS v3.1 base score of 5.4 (Medium) with the vector CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:L. The primary security impact is a high-severity breach of confidentiality (C:H) across multi-tenant or multi-user boundaries within the FileGator application.
In organizations that deploy FileGator to provide departmental or client-isolated file storage via distinct home directories, any authenticated user with standard download rights can access sensitive files belonging to other users or administrators whenever those files are packaged into a batch archive. Because FileGator makes no distinction between administrative users and regular users at the temporary filesystem layer, an attacker who captures an administrator's batch archive ID gains full read access to all files included in that archive.
The vulnerability also carries a low availability impact (A:L). Because DownloadController::batchDownloadStart() unconditionally deletes the temporary archive file from TmpfsInterface once the HTTP response stream completes, an attacker who wins the race to download the archive destroys the pending file. When the legitimate user's browser attempts to retrieve /batchdownload?uniqid=<id>, the file no longer exists in temporary storage, causing the download operation to fail.
Exploitation complexity is rated High (AC:H) with User Interaction Required (UI:R) because an archive only exists on disk after a target user actively initiates a batch download and before the target user's client finishes streaming it (or before garbage collection purges abandoned temporary files). According to FIRST EPSS metrics, the vulnerability has an EPSS score of 0.00189 (8.61st percentile), and it is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog.
Administrators running FileGator should upgrade to version 7.15.0 or later (filegator/filegator via Composer or the official release package). Version 7.15.0 incorporates commit 638737e560cbf20cbeb5da4e4e6c974f8a080531, which binds batch archive identifiers to the originating user session and replaces uniqid() with 32 bytes (64 hexadecimal characters) of cryptographically secure randomness via random_bytes(32).
For deployments where an immediate upgrade to version 7.15.0 is not feasible, administrators can apply the following temporary workarounds to eliminate the attack surface:
batchdownload permission from untrusted or non-administrative user accounts in the FileGator user configuration. Individual single-file downloads (GET /download) validate paths directly against the authenticated user's storage repository and are not affected by this vulnerability./batchdownload until the application is patched.Security teams can also monitor web server access logs and configure Web Application Firewall (WAF) rules to detect exploitation attempts. In unpatched installations (< 7.15.0), legitimate and targeted uniqid values match the 13-character hexadecimal format ^[0-9a-f]{13}$. Multiple GET /batchdownload?uniqid= requests originating from a single IP address or session with incrementing hexadecimal suffixes indicate active microsecond timestamp brute-forcing. Once upgraded to version 7.15.0, any request to GET /batchdownload where the uniqid parameter is not exactly 64 hexadecimal characters (^[0-9a-f]{64}$) should be flagged and blocked as anomalous.
CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
FileGator filegator | < 7.15.0 | 7.15.0 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-94618 |
| Primary CWE | CWE-862 (Missing Authorization) & CWE-330 (Use of Insufficiently Random Values) |
| CVSS v3.1 Score | 5.4 Medium (CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:L) |
| Attack Vector | Network (Authenticated HTTP GET request to /batchdownload) |
| Affected Component | DownloadController::batchDownloadStart() & ZipArchiver::createArchive() |
| Security Impact | Cross-user batch archive exfiltration and deletion of victim's pending archive |
| Exploit Status | Proof-of-Concept (Included in repository PHPUnit regression suite) |
| Patched Version | FileGator 7.15.0 |
The product does not perform an authorization check when an actor attempts to access a resource or perform an action, allowing authenticated users to retrieve temporary batch archives created by other sessions.
An authorization bypass and insecure direct object reference (IDOR) vulnerability exists in FileGator versions prior to 7.15.0 within the chunked file upload reassembly handler. When processing multipart uploads in backend/Controllers/UploadController.php, the application fails to namespace the target assembly filename with a user-specific prefix inside the shared temporary storage directory. Authenticated users with upload privileges can manipulate chunk metadata parameters to exfiltrate, corrupt, or delete arbitrary transient files belonging to other users or the system.
CVE-2026-77596 is an authorization bypass vulnerability in the Kubeflow Pipelines API server affecting multi-user deployments prior to version 2.18.0. The HTTP multipart upload handlers prematurely terminate authorization checks and return success when the target namespace parameter is empty or omitted. Because the default configuration allows pipelines without namespaces to function as cluster-wide shared resources, any low-privileged tenant can create shared pipelines or inject arbitrary pipeline versions into existing shared pipelines without undergoing Kubernetes SubjectAccessReview validation. When other tenants execute unpinned recurring runs tracking these shared pipelines, the attacker-supplied workload runs inside the victim tenant's namespace using the victim's service account credentials.
An authorization bypass vulnerability in Velocidex Velociraptor versions prior to 0.77.2 allows authenticated users with low-privilege query permissions to bypass access controls in the sql() VQL plugin. By supplying crafted MySQL connection strings pointing to an attacker-controlled rogue MySQL server with local-infile enabled, an attacker can read arbitrary files from the Velociraptor server host filesystem with the privileges of the server process.
CVE-2026-97705 (GHSA-h89v-g948-47vp) is a high-severity unauthenticated Denial of Service (DoS) vulnerability affecting the Moleculer microservices framework for Node.js in versions prior to 0.14.36 and 0.15.2. The vulnerability resides in the internal TCP transporter packet parser (`src/transporters/tcp/parser.js`), which decodes the 4-byte packet length field as a signed 32-bit integer using `Buffer.prototype.readInt32BE()`. An unauthenticated attacker with network access to an exposed TCP transporter port can transmit a single malformed packet containing a negative length value or a positive length value less than the 6-byte header size. Due to how Node.js `Buffer.prototype.slice()` processes negative or sub-header offsets, the parser fails to advance the incoming buffer cursor while remaining inside a synchronous `while (packet.length > 0)` loop. Because Node.js relies on a single-threaded event loop, this infinite loop permanently blocks execution, freezing all services and endpoints within the affected process until manual intervention.
CVE-2026-108262 (GHSA-9hq4-hm3j-jmph) is a path traversal and arbitrary file overwrite vulnerability in the tar archive extraction routine of DDEV versions 1.25.2 and 1.25.3. Resulting from an incomplete fix for CVE-2026-32885, the Untar function in pkg/archive/archive.go validates absolute symbolic link targets by rebasing them against the extraction destination directory, but subsequently creates the symbolic link on disk using the raw, unvalidated absolute path. When subsequent entries in the archive write through the planted symbolic link, the host operating system resolves the link directly to the host filesystem, allowing an attacker to overwrite arbitrary files with the privileges of the user executing DDEV.
CVE-2026-119298 (GHSA-x89h-7h96-v88f) is a file overwrite confirmation bypass vulnerability in schollz/croc prior to version 11.5.2. The receive-side file validation logic in src/croc/croc.go improperly exempted any incoming file whose basename began with the prefix 'croc-stdin-' from interactive overwrite prompts and automatic collision renaming, failing to verify if the transfer session was actually an internal text transfer. An unauthenticated remote peer participating in a transfer session can exploit this flaw to silently overwrite, truncate, or replace matching pre-existing files in the receiver's working directory.