Logo
Overview
my first CVE, CVE-2026-44692

my first CVE, CVE-2026-44692

May 8, 2026
7 min read

TL;DR

I found an authorization flaw in code16/sharp that was later assigned CVE-2026-44692.

Sharp exposes a generic download endpoint that authorizes only the supplied entity instance, but then reads a client-supplied disk and path from Laravel Storage.

Because the requested storage object is not bound to the authorized entity instance, an authenticated Sharp user who can view one valid record can use that record as an authorization anchor to download unrelated disk-relative storage objects from configured Laravel Storage disks.

In the tested version, downloads.allowed_disks defaults to *, which allows access to any configured Laravel disk unless the application explicitly restricts it.

The confirmed impact is authenticated disclosure of unrelated storage objects from configured Laravel Storage disks. I did not confirm arbitrary host filesystem access outside configured disk roots.

Affected package

  • Package: code16/sharp
  • Affected versions: versions before 9.22.0
  • Patched version: 9.22.0
  • Tested version: 9.21.1
  • CVE: CVE-2026-44692
  • Severity: High

The tested version was identified from src/SharpInternalServiceProvider.php:63.

The checkout I used during testing was not a git repository, so there was no commit hash available.

CVSS v3 base metrics

MetricValue
Attack vectorNetwork
Attack complexityLow
Privileges requiredLow
User interactionNone
ScopeChanged
ConfidentialityHigh
IntegrityNone
AvailabilityNone

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

Affected component

  • src/routes/web.php:63
  • src/Http/Controllers/Api/DownloadController.php:11-29
  • src/Config/SharpConfigBuilder.php:119-120
  • src/SharpInternalServiceProvider.php:225-229

Vulnerable endpoint

GET /sharp/{globalFilter}/download/{entityKey}/{instanceId?}

At a high level, the endpoint authorizes access to the supplied Sharp entity instance first, but the storage object being downloaded is not tied to that authorized record.

So if the application accepts a valid Sharp record for authorization, and the request can still influence the target disk and path, the user may retrieve a different object from an allowed storage disk.

Attacker requirements

The attacker needs:

  • an authenticated Sharp session
  • view access to at least one valid Sharp entity instance

The attacker does not need access to the storage object being downloaded.

Root cause

The download endpoint performs authorization only against the provided entityKey and instanceId.

After the view check succeeds, the controller reads disk and path directly from the request and returns the object from Laravel Storage.

There is no authorization check that verifies whether the requested disk-relative storage object belongs to the authorized entity instance.

With the default downloads.allowed_disks = '*', the attacker can also select any configured Laravel disk.

Source-to-sink trace

The vulnerable flow is:

authenticated Sharp user with view access to one valid entity instance
-> GET /sharp/{globalFilter}/download/{entityKey}/{instanceId?}
-> view authorization check on entityKey + instanceId
-> disk and path read from request query parameters
-> Storage::disk($disk)->get($path)
-> response returns the selected storage object

Relevant implementation points:

  • the download route is registered in src/routes/web.php:63
  • Sharp loads its web routes by default through src/SharpInternalServiceProvider.php:225-229
  • the default download disk policy is downloads.allowed_disks = '*' in src/Config/SharpConfigBuilder.php:119-120
  • DownloadController authorizes only view on the supplied entity instance
  • disk and path are read from request query parameters
  • the response is built from Storage::disk($disk)->get($path)
  • no path-to-record binding is enforced before returning the file

The missing security control is a file-level authorization check that binds the requested storage object to the authorized entity instance.

Runtime validation

I verified the issue at runtime with a framework test that exercised the real Sharp route, middleware stack, authenticated session handling, authorization check, and controller execution. This was not a direct unit invocation of the controller.

The reproducer used:

  • Orchestra Testbench with the real Sharp service provider
  • a Sharp entity named posts
  • a per-record view policy
  • one authenticated Sharp user
  • one authorized record: posts/1
  • one unauthorized record: posts/2
  • a local disk containing an unrelated object: private/backups/customers.csv
  • an archive disk containing an unrelated object: secrets.txt

The test suite passed:

OK (6 tests, 16 assertions)

This confirmed the issue was reachable through the real framework route and controller path.

Confirmed exploit path

The authenticated Sharp user was allowed to view posts/1.

That valid record could then be used as the authorization anchor while requesting an unrelated object from the local storage disk.

Proof of concept

GET /sharp/default/download/posts/1?disk=local&path=private/backups/customers.csv HTTP/1.1
Host: target.local
User-Agent: Mozilla/5.0
Cookie: laravel_session=ATTACKER_SESSION
Connection: close

Actual result

The server returned 200 OK and the contents of the unrelated storage object:

customer_id,email
7,bob@example.test

The response also included file download headers, including a content disposition for attachment download.

As supplementary evidence, the same authenticated Sharp user could also read an unrelated object from another configured Laravel disk when downloads.allowed_disks remained at its default value:

GET /sharp/default/download/posts/1?disk=archive&path=secrets.txt

Result:

200 OK
archive-secret

Expected result

The endpoint should only return storage objects associated with the authorized entity instance, or require a file-level authorization check before returning the object.

Negative controls

I also verified the following controls:

  • invalid entityKey returned 404
  • unauthorized instanceId returned 403
  • restricting downloads.allowed_disks to ['public'] caused the same disk=local request to return 403
  • path traversal outside the local disk root was not confirmed

Impact

This is an authenticated cross-record / cross-feature storage object disclosure issue.

A Sharp user with view access to one valid entity instance can download unrelated objects from configured Laravel Storage disks.

In real applications, those disks may contain:

  • exports
  • backups
  • invoices
  • internal documents
  • uploaded files belonging to other records
  • tenant-specific data
  • operational or administrative files stored in private application disks

The confirmed impact is limited to configured Laravel Storage disks. I did not confirm arbitrary host filesystem access outside configured disk roots.

Limitations and assumptions

  • The confirmed scope is configured Laravel Storage disks, not arbitrary host filesystem paths.
  • Path traversal outside a local disk root was not confirmed.
  • Impact depends on sensitive objects existing on configured disks.
  • If an application explicitly restricts downloads.allowed_disks, exposure can be reduced.
  • Disk allowlisting reduces the reachable storage surface, but does not fix the missing per-record file binding.

Package-level impact

This appears to be a package-level authorization flaw that applications may inherit through Sharp’s default route and controller flow.

A valid record-level view permission can be reused as an authorization anchor for unrelated storage objects. Applications using the generic download endpoint with the default downloads.allowed_disks = '*' behavior may expose disk-relative objects from configured Laravel Storage disks unless they add compensating controls.

Patch

The fix in 9.22.0 uses signed URLs for Sharp-generated download links.

After the patch, changing parameters such as:

  • disk
  • path
  • entityKey
  • instanceId

invalidates the signature, so the modified request is rejected instead of returning a different storage object.

The endpoint should not accept raw client-controlled disk and path values as the authorization basis for file downloads.

Recommended fixes:

  • replace disk and path query parameters with an opaque server-generated file identifier
  • resolve the storage object server-side from the authorized entity instance
  • enforce per-record file ownership or binding before returning the object
  • keep disk allowlisting, but do not rely on disk allowlisting as the only security control
  • prevent cross-disk access unless explicitly required and authorized
  • return a handled 403 or 404 for invalid, unbound, or traversal-like paths

A safer design would make the download endpoint derive the storage location from the authorized record or from a server-side file registry, rather than accepting the storage location directly from the request.

Workarounds

If upgrading is not possible immediately, the practical short-term mitigations are:

  • reduce downloads.allowed_disks to the smallest set possible
  • avoid storing unrelated sensitive files on disks reachable through Sharp downloads
  • add application-level checks that bind the requested file to the authorized record

These measures reduce exposure, but they do not fully solve the missing per-record file binding issue.

References