Skip to content

Stored XSS via unescaped filenames in upload handlers #85

Description

@anon87111

Security: Stored XSS via unescaped filenames in upload handlers (9+ controllers)

Commit: 1a00ac50ff71d2841624d587003c0fd538b301f6

Summary

Commit 1a00ac5 removes HtmlEscape.escape() from filename handling across 9 controllers. User-supplied filenames are now returned unescaped in JSON upload responses, and KindEditor's afterUpload callback inserts the title field via innerHTML, completing a stored XSS chain.

Affected files (17 removals across 9 controllers)

The alert that flagged this only identified 2 files. The actual scope is:

Controller Fields affected
AnswerFormAction.java fileName, sourceFileName
CommentFormAction.java fileName, sourceFileName
QuestionFormAction.java fileName, sourceFileName
TopicFormAction.java fileName, sourceFileName ×3
HelpManageAction.java fileName, sourceFileName
MembershipCardManageAction.java fileName, sourceFileName
ReportFormAction.java ext
AnswerManageAction.java fileName, sourceFileName
QuestionManageAction.java fileName, sourceFileName

Confirmed JSON reflection endpoints

These controllers put the unescaped filename directly into the JSON response title field:

  • /user/control/topic/uploadTopicFormAction
  • /admin/help/uploadHelpManageAction
  • /admin/membershipCard/uploadMembershipCardManageAction
  • /admin/question/uploadQuestionManageAction

Attack chain

  1. Authenticated user uploads a file with filename: "><img src=x onerror=alert(document.cookie)>.jpg
  2. Server returns unescaped JSON (escaping was removed in this commit):
{"error": 0, "url": "...", "title": "><img src=x onerror=alert(document.cookie)>.jpg"}
  1. KindEditor's afterUpload callback inserts data.title via innerHTML:
var html = '<div class="ke-file"><span>' + data.title + '</span></div>';
fileList.innerHTML += html;  // payload executes here
  1. Script executes in the victim's browser — session cookies exfiltrated

Verification

Confirmed end-to-end with a local DOM test: "><img src=x onerror=alert(1)>.jpg fires alert(1) when processed by the KindEditor innerHTML rendering path. The <script> tag variant is blocked by browsers per spec but event-handler payloads execute without restriction.

KindEditor's innerHTML-based file listing is a known XSS vector — see CVE-2021-42227 / GHSA-wv83-jrfh-rp33.

Fix

Restore the removed HtmlEscape.escape() calls. They were present for exactly this reason. Simplest fix per controller:

// Restore these lines that were removed in commit 1a00ac5
fileName = HtmlEscape.escape(fileName);
sourceFileName = HtmlEscape.escape(sourceFileName);

Alternatively, escape at the KindEditor layer before innerHTML assignment, but fixing it server-side covers all rendering contexts including any future frontend changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions