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/upload — TopicFormAction
/admin/help/upload — HelpManageAction
/admin/membershipCard/upload — MembershipCardManageAction
/admin/question/upload — QuestionManageAction
Attack chain
- Authenticated user uploads a file with filename:
"><img src=x onerror=alert(document.cookie)>.jpg
- Server returns unescaped JSON (escaping was removed in this commit):
{"error": 0, "url": "...", "title": "><img src=x onerror=alert(document.cookie)>.jpg"}
- 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
- 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.
Security: Stored XSS via unescaped filenames in upload handlers (9+ controllers)
Commit:
1a00ac50ff71d2841624d587003c0fd538b301f6Summary
Commit
1a00ac5removesHtmlEscape.escape()from filename handling across 9 controllers. User-supplied filenames are now returned unescaped in JSON upload responses, and KindEditor'safterUploadcallback inserts thetitlefield viainnerHTML, 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:
AnswerFormAction.javafileName,sourceFileNameCommentFormAction.javafileName,sourceFileNameQuestionFormAction.javafileName,sourceFileNameTopicFormAction.javafileName,sourceFileName×3HelpManageAction.javafileName,sourceFileNameMembershipCardManageAction.javafileName,sourceFileNameReportFormAction.javaextAnswerManageAction.javafileName,sourceFileNameQuestionManageAction.javafileName,sourceFileNameConfirmed JSON reflection endpoints
These controllers put the unescaped filename directly into the JSON response
titlefield:/user/control/topic/upload—TopicFormAction/admin/help/upload—HelpManageAction/admin/membershipCard/upload—MembershipCardManageAction/admin/question/upload—QuestionManageActionAttack chain
"><img src=x onerror=alert(document.cookie)>.jpg{"error": 0, "url": "...", "title": "><img src=x onerror=alert(document.cookie)>.jpg"}afterUploadcallback insertsdata.titleviainnerHTML:Verification
Confirmed end-to-end with a local DOM test:
"><img src=x onerror=alert(1)>.jpgfiresalert(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:Alternatively, escape at the KindEditor layer before
innerHTMLassignment, but fixing it server-side covers all rendering contexts including any future frontend changes.